Skip to content

ファイル作成の順序

Spring Bootプロジェクトでのファイル作成の順序

Section titled “Spring Bootプロジェクトでのファイル作成の順序”

Spring Bootアプリケーションを開発する際の、効率的なファイル作成の順序について解説します。

データベースアクセスの設計思想

Section titled “データベースアクセスの設計思想”

Spring Bootでデータベースにアクセスする際には、主に2つの設計思想があります:

  1. JPA(Java Persistence API): ORM(Object-Relational Mapping)の設計思想

    • Hibernate: JPAの実装の一つ
    • オブジェクトとリレーショナルデータベースを自動的にマッピング
    • エンティティベースの開発
  2. MyBatis: SQLマッパーフレームワーク

    • SQLを直接制御しながら、Javaオブジェクトとデータベースの結果をマッピング
    • SQLベースの開発

選択の指針:

  • JPA(Hibernate): CRUD操作が中心、オブジェクト指向的な設計を重視、開発速度を優先
  • MyBatis: 複雑なSQLクエリが多い、パフォーマンスが重要、既存のSQL資産を活用したい

Spring Bootプロジェクトでは、下位レイヤーから上位レイヤーへ順番にファイルを作成するのが一般的です。

1. Model/Entity層
2. Repository層 / Mapper層
3. Service層
4. Controller層
5. DTO層
6. Config層
7. Exception層

まず、データベースのテーブルに対応するエンティティクラスを作成します。

@Dataアノテーションを使用すれば、コンストラクタ、getter/setterが自動生成されるため、定義不要です。

src/main/java/com/example/myapp/model/User.java
@Entity
@Table(name = "users")
@Data // getter/setter、toString、equals、hashCode、コンストラクタを自動生成
@NoArgsConstructor // 引数なしコンストラクタ(JPAの要件)
@AllArgsConstructor // 全フィールドを引数に持つコンストラクタ(オプション)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(unique = true, nullable = false, length = 255)
@Email
private String email;
@Column(nullable = false)
private String password;
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime updatedAt;
// @Dataにより以下が自動生成される:
// - 引数なしコンストラクタ(@NoArgsConstructor)
// - 全フィールドを引数に持つコンストラクタ(@AllArgsConstructor)
// - getter/setter
// - toString()
// - equals()/hashCode()
}

メリット:

  • コードが簡潔で読みやすい
  • ボイラープレートコードが削減される
  • 保守性が向上する

⚠️ 重要な注意点: 双方向の関連がある場合の@Exclude

もしあなたが@Data(または@ToString@EqualsAndHashCode)を使い、かつ「双方向(親子両方から参照できる)」の関連を作るなら、手動で@Excludeを入れるのは必須の儀式だと思ってください。

問題のある例(無限ループが発生):

// Userエンティティ
@Entity
@Table(name = "users")
@Data // ⚠️ 問題: 双方向の関連がある場合、無限ループが発生する可能性
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>(); // ⚠️ OrderからもUserを参照
}
// Orderエンティティ
@Entity
@Table(name = "orders")
@Data // ⚠️ 問題: 双方向の関連がある場合、無限ループが発生する可能性
@NoArgsConstructor
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user; // ⚠️ UserからもOrderを参照(双方向)
private BigDecimal amount;
}
// 問題: toString()やequals()/hashCode()が無限ループを引き起こす
// User.toString() → Order.toString() → User.toString() → ...

解決方法: @Excludeを使用

// Userエンティティ
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@ToString(exclude = "orders") // ✅ ordersフィールドをtoStringから除外
@EqualsAndHashCode(exclude = "orders") // ✅ ordersフィールドをequals/hashCodeから除外
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}
// Orderエンティティ
@Entity
@Table(name = "orders")
@Data
@NoArgsConstructor
@ToString(exclude = "user") // ✅ userフィールドをtoStringから除外
@EqualsAndHashCode(exclude = "user") // ✅ userフィールドをequals/hashCodeから除外
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
private BigDecimal amount;
}

なぜ@Excludeが必須なのか:

  1. 無限ループの防止: @ToStringが双方向の関連フィールドを含むと、循環参照によりスタックオーバーフローが発生
  2. パフォーマンスの問題: @EqualsAndHashCodeが双方向の関連フィールドを含むと、計算コストが膨大になる
  3. 予期しない動作: 双方向の関連を含むequals()/hashCode()は、エンティティの管理状態によって予期しない動作を引き起こす可能性がある

ベストプラクティス:

  • 双方向の関連フィールドは常に@Excludeで除外する
  • IDフィールドのみを使用してequals()/hashCode()を計算する(推奨)
// より安全な実装例
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@ToString(exclude = "orders")
@EqualsAndHashCode(of = "id") // ✅ IDフィールドのみを使用
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}

@Getter/@Setterを使用したほうが良いパターン

Section titled “@Getter/@Setterを使用したほうが良いパターン”

以下の場合は、@Getter/@Setterを個別に使用することを推奨します:

  1. 特定のフィールドのみgetter/setterを生成したい場合
  2. setterを制限したい場合(例: idフィールドはsetterを生成しない)
  3. アクセス修飾子を制御したい場合
src/main/java/com/example/myapp/model/User.java
@Entity
@Table(name = "users")
@NoArgsConstructor // 引数なしコンストラクタ(JPAの要件)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter // getterのみ生成(setterは生成しない)
private Long id;
@Column(nullable = false, length = 100)
@Getter
@Setter
private String name;
@Column(unique = true, nullable = false, length = 255)
@Email
@Getter
@Setter
private String email;
@Column(nullable = false)
@Getter
@Setter(AccessLevel.PRIVATE) // setterをprivateに制限(外部からの変更を防ぐ)
private String password;
@CreatedDate
@Column(nullable = false, updatable = false)
@Getter // getterのみ生成(作成日時は外部から変更不可)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(nullable = false)
@Getter // getterのみ生成(更新日時は自動更新のため、外部から変更不可)
private LocalDateTime updatedAt;
// コンストラクタ(必要に応じて)
public User(String name, String email, String password) {
this.name = name;
this.email = email;
this.password = password;
}
}

使用例:

// idフィールドはgetterのみ(setterなし)
User user = new User();
user.getId(); // OK
// user.setId(1L); // コンパイルエラー(setterが存在しない)
// passwordフィールドはprivate setter(クラス内からのみ変更可能)
user.setPassword("newPassword"); // クラス内からのみアクセス可能
// createdAt/updatedAtはgetterのみ(外部から変更不可)
user.getCreatedAt(); // OK
// user.setCreatedAt(...); // コンパイルエラー(setterが存在しない)

@Getter/@Setterを使うべき場合:

  • IDフィールド: setterを生成しない(不変性を保証)
  • 作成日時・更新日時: getterのみ生成(自動更新のため外部から変更不可)
  • パスワードフィールド: setterをprivateに制限(セキュリティのため)
  • 特定のフィールドのみアクセス制御: 細かい制御が必要な場合

作成する理由:

  • データベースのスキーマを定義する
  • 他のレイヤーの基盤となる

次に、データアクセスを担当するリポジトリインターフェースを作成します。

src/main/java/com/example/myapp/repository/UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
List<User> findByNameContaining(String name);
}

作成する理由:

  • エンティティのCRUD操作を提供
  • カスタムクエリを定義

APIのリクエスト・レスポンス用のDTOクラスを作成します。

src/main/java/com/example/myapp/dto/UserDTO.java
public class UserDTO {
private Long id;
private String name;
private String email;
// コンストラクタ、getter/setter
public UserDTO() {}
public UserDTO(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// getter/setter
}
// src/main/java/com/example/myapp/dto/UserCreateRequest.java
public class UserCreateRequest {
@NotBlank(message = "Name is required")
@Size(min = 1, max = 100)
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Email must be valid")
private String email;
@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;
// getter/setter
}

作成する理由:

  • エンティティとAPIの分離
  • バリデーションルールの定義

ビジネスロジックを実装するサービスクラスを作成します。

src/main/java/com/example/myapp/service/UserService.java
@Service
@Transactional(readOnly = true)
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserDTO findById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User", id));
return convertToDTO(user);
}
@Transactional
public UserDTO create(UserCreateRequest request) {
if (userRepository.existsByEmail(request.getEmail())) {
throw new DuplicateResourceException("User", request.getEmail());
}
User user = new User();
user.setName(request.getName());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
User savedUser = userRepository.save(user);
return convertToDTO(savedUser);
}
private UserDTO convertToDTO(User user) {
return new UserDTO(user.getId(), user.getName(), user.getEmail());
}
}

作成する理由:

  • ビジネスロジックの実装
  • トランザクション管理
  • エンティティとDTOの変換

カスタム例外クラスを作成します。

src/main/java/com/example/myapp/exception/ResourceNotFoundException.java
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String resourceName, Object identifier) {
super(String.format("%s with identifier %s not found", resourceName, identifier));
}
}
// src/main/java/com/example/myapp/exception/DuplicateResourceException.java
public class DuplicateResourceException extends RuntimeException {
public DuplicateResourceException(String resourceName, Object identifier) {
super(String.format("%s with identifier %s already exists", resourceName, identifier));
}
}

作成する理由:

  • 統一された例外処理
  • エラーメッセージの標準化

最後に、HTTPリクエストを処理するコントローラークラスを作成します。

src/main/java/com/example/myapp/controller/UserController.java
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.findById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<UserDTO> createUser(
@Valid @RequestBody UserCreateRequest request) {
UserDTO createdUser = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
}

作成する理由:

  • HTTPリクエストの受け取り
  • レスポンスの生成

7. Config層の作成(必要に応じて)

Section titled “7. Config層の作成(必要に応じて)”

設定クラスを作成します。

src/main/java/com/example/myapp/config/SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// セキュリティ設定
}
// src/main/java/com/example/myapp/config/RestTemplateConfig.java
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

1つの機能(例: ユーザー管理)を完全に実装してから、次の機能に進む方法です。

1. Userエンティティの作成
2. UserRepositoryの作成
3. UserDTO、UserCreateRequestの作成
4. UserServiceの作成
5. UserControllerの作成
6. テストの作成
7. 次の機能(例: Order管理)に進む

すべてのレイヤーを一度に作成する方法です。

1. すべてのエンティティを作成
2. すべてのリポジトリを作成
3. すべてのDTOを作成
4. すべてのサービスを作成
5. すべてのコントローラーを作成

小規模プロジェクト:

機能単位での開発を推奨
→ 1つの機能を完全に実装してから次に進む

中規模プロジェクト:

レイヤー単位での開発を推奨
→ すべてのエンティティを作成してから、次のレイヤーに進む

大規模プロジェクト:

モジュール単位での開発を推奨
→ 1つのモジュール(例: ユーザー管理モジュール)を完全に実装してから次に進む

ファイル作成時のチェックリスト

Section titled “ファイル作成時のチェックリスト”

各レイヤーのファイルを作成する際に確認すべき項目:

  • @Entityアノテーションが付いているか
  • @Id@GeneratedValueが設定されているか
  • 適切な@Column設定があるか
  • @Dataまたは@Getter/@Setterが適切に設定されているか(Lombok使用時)
  • @NoArgsConstructorが設定されているか(JPAの要件)
  • アクセス制御が必要なフィールドには@Getter/@Setterを個別に使用しているか
  • JpaRepositoryを継承しているか
  • 必要なカスタムクエリが定義されているか
  • メソッド名が適切か
  • @Serviceアノテーションが付いているか
  • @Transactionalが適切に設定されているか
  • 例外処理が実装されているか
  • DTOへの変換メソッドがあるか
  • @RestControllerアノテーションが付いているか
  • @RequestMappingが適切に設定されているか
  • @Validアノテーションが使用されているか
  • 適切なHTTPステータスコードが返されているか

JPA(Hibernate)を使用する場合のファイル作成の順序:

  1. Model/Entity層: データベーススキーマの定義
  2. Repository層: データアクセスの実装
  3. DTO層: APIのリクエスト・レスポンスの定義
  4. Service層: ビジネスロジックの実装
  5. Exception層: カスタム例外の定義
  6. Controller層: HTTPリクエストの処理
  7. Config層: 設定クラスの定義(必要に応じて)

この順序に従うことで、依存関係を明確にし、効率的に開発を進めることができます。


MyBatisを使用する場合(A5M2使用想定)

Section titled “MyBatisを使用する場合(A5M2使用想定)”

MyBatisはSQLマッパーフレームワークで、SQLを直接制御しながらJavaオブジェクトとデータベースの結果をマッピングします。A5M2(MyBatis Generator)を使用することで、データベーススキーマからEntity、Mapperインターフェース、Mapper XMLファイルを自動生成できます。

基本的な開発フロー(MyBatis使用時)

Section titled “基本的な開発フロー(MyBatis使用時)”

MyBatisを使用する場合の開発フローは、A5M2を使用してコードを自動生成することを前提とします。

1. データベーススキーマの作成
2. A5M2によるコード自動生成
- Entityクラス
- Mapperインターフェース
- Mapper XMLファイル
3. DTO層の作成
4. Service層の作成
5. Controller層の作成
6. Config層の作成(必要に応じて)
7. Exception層の作成(必要に応じて)

1. データベーススキーマの作成

Section titled “1. データベーススキーマの作成”

まず、データベーススキーマを作成します。

-- usersテーブルの作成
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- ordersテーブルの作成
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
total_amount DECIMAL(10, 2) NOT NULL,
order_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

A5M2(MyBatis Generator)を使用して、データベーススキーマからコードを自動生成します。

A5M2の設定ファイル(generatorConfig.xml)

Section titled “A5M2の設定ファイル(generatorConfig.xml)”
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="PostgreSQL" targetRuntime="MyBatis3">
<!-- コメント生成の無効化 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- データベース接続情報 -->
<jdbcConnection driverClass="org.postgresql.Driver"
connectionURL="jdbc:postgresql://localhost:5432/mydb"
userId="myuser"
password="mypassword">
</jdbcConnection>
<!-- Java型の解決 -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- Entityクラスの生成設定 -->
<javaModelGenerator targetPackage="com.example.myapp.model"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- Mapper XMLファイルの生成設定 -->
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- Mapperインターフェースの生成設定 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.example.myapp.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- テーブルの設定 -->
<table tableName="users" domainObjectName="User">
<property name="useActualColumnNames" value="false"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
<table tableName="orders" domainObjectName="Order">
<property name="useActualColumnNames" value="false"/>
<generatedKey column="id" sqlStatement="JDBC"/>
</table>
</context>
</generatorConfiguration>

A5M2を実行してコードを自動生成します。

Terminal window
# Mavenを使用する場合
mvn mybatis-generator:generate
# または、A5M2プラグインを使用する場合
# IntelliJ IDEAの場合: Databaseツールウィンドウから実行

A5M2により、以下のファイルが自動生成されます:

  1. Entityクラスsrc/main/java/com/example/myapp/model/User.javaなど)
  2. Mapperインターフェースsrc/main/java/com/example/myapp/mapper/UserMapper.javaなど)
  3. Mapper XMLファイルsrc/main/resources/mapper/UserMapper.xmlなど)

自動生成されるEntityクラスの例:

src/main/java/com/example/myapp/model/User.java
public class User {
private Long id;
private String name;
private String email;
private String password;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// コンストラクタ、getter/setterが自動生成される
// (A5M2の設定により、Lombokの@Dataを使用することも可能)
}

自動生成されるMapperインターフェースの例:

src/main/java/com/example/myapp/mapper/UserMapper.java
@Mapper
public interface UserMapper {
int deleteByPrimaryKey(Long id);
int insert(User record);
User selectByPrimaryKey(Long id);
List<User> selectAll();
int updateByPrimaryKey(User record);
}

自動生成されるMapper XMLファイルの例:

src/main/resources/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.myapp.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.example.myapp.model.User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
<result column="created_at" property="createdAt" jdbcType="TIMESTAMP"/>
<result column="updated_at" property="updatedAt" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 基本的なCRUD操作が自動生成される -->
</mapper>

3. カスタムMapperメソッドの追加

Section titled “3. カスタムMapperメソッドの追加”

自動生成されたMapperインターフェースとXMLファイルに、カスタムメソッドを追加します。

Mapperインターフェースにカスタムメソッドを追加:

src/main/java/com/example/myapp/mapper/UserMapper.java
@Mapper
public interface UserMapper {
// 自動生成されたメソッド
int deleteByPrimaryKey(Long id);
int insert(User record);
User selectByPrimaryKey(Long id);
List<User> selectAll();
int updateByPrimaryKey(User record);
// カスタムメソッドを追加
User selectByEmail(String email);
boolean existsByEmail(String email);
List<User> selectByNameContaining(String name);
}

Mapper XMLファイルにカスタムSQLを追加:

src/main/resources/mapper/UserMapper.xml
<mapper namespace="com.example.myapp.mapper.UserMapper">
<!-- 自動生成されたSQL -->
<!-- カスタムSQLを追加 -->
<select id="selectByEmail" resultMap="BaseResultMap">
SELECT * FROM users WHERE email = #{email}
</select>
<select id="existsByEmail" resultType="boolean">
SELECT EXISTS(SELECT 1 FROM users WHERE email = #{email})
</select>
<select id="selectByNameContaining" resultMap="BaseResultMap">
SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')
</select>
</mapper>

APIのリクエスト・レスポンス用のDTOクラスを作成します(JPA版と同じ)。

src/main/java/com/example/myapp/dto/UserDTO.java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private Long id;
private String name;
private String email;
}
// src/main/java/com/example/myapp/dto/UserCreateRequest.java
@Data
@NoArgsConstructor
public class UserCreateRequest {
@NotBlank(message = "Name is required")
@Size(min = 1, max = 100)
private String name;
@NotBlank(message = "Email is required")
@Email(message = "Email must be valid")
private String email;
@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;
}

ビジネスロジックを実装するサービスクラスを作成します。

src/main/java/com/example/myapp/service/UserService.java
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Slf4j
public class UserService {
private final UserMapper userMapper;
private final PasswordEncoder passwordEncoder;
public UserDTO findById(Long id) {
User user = userMapper.selectByPrimaryKey(id);
if (user == null) {
throw new ResourceNotFoundException("User", id);
}
return convertToDTO(user);
}
@Transactional
public UserDTO create(UserCreateRequest request) {
if (userMapper.existsByEmail(request.getEmail())) {
throw new DuplicateResourceException("User", request.getEmail());
}
User user = new User();
user.setName(request.getName());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
userMapper.insert(user);
return convertToDTO(user);
}
private UserDTO convertToDTO(User user) {
return new UserDTO(
user.getId(),
user.getName(),
user.getEmail()
);
}
}

HTTPリクエストを処理するコントローラークラスを作成します(JPA版と同じ)。

src/main/java/com/example/myapp/controller/UserController.java
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.findById(id);
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<UserDTO> createUser(
@Valid @RequestBody UserCreateRequest request) {
UserDTO createdUser = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
}

ファイル作成時のチェックリスト(MyBatis使用時)

Section titled “ファイル作成時のチェックリスト(MyBatis使用時)”
  • テーブル定義が適切か
  • 主キーが設定されているか
  • 外部キー制約が適切に設定されているか
  • インデックスが適切に設定されているか
  • generatorConfig.xmlの設定が正しいか
  • 生成されるパッケージ名が適切か
  • 生成されるファイル名が適切か
  • @Mapperアノテーションが付いているか
  • カスタムSQLが適切に記述されているか
  • パラメータマッピングが適切か
  • 結果マッピングが適切か
  • @Serviceアノテーションが付いているか
  • @Transactionalが適切に設定されているか
  • 例外処理が実装されているか
  • DTOへの変換メソッドがあるか

MyBatisを使用する場合のファイル作成の順序:

  1. データベーススキーマ: テーブル定義の作成
  2. A5M2によるコード自動生成: Entity、Mapperインターフェース、Mapper XMLファイル
  3. カスタムMapperメソッド: 必要なカスタムSQLの追加
  4. DTO層: APIのリクエスト・レスポンスの定義
  5. Service層: ビジネスロジックの実装
  6. Controller層: HTTPリクエストの処理
  7. Config層: 設定クラスの定義(必要に応じて)
  8. Exception層: カスタム例外の定義(必要に応じて)

JPAとMyBatisの使い分け:

  • JPA(Hibernate): CRUD操作が中心、オブジェクト指向的な設計を重視、開発速度を優先
  • MyBatis: 複雑なSQLクエリが多い、パフォーマンスが重要、既存のSQL資産を活用したい、A5M2によるコード自動生成を活用したい

この順序に従うことで、MyBatisを使用した開発を効率的に進めることができます。