JPAアノテーションによるエンティティ設計とリレーションシップ管理

基本エンティティアノテーション

@Entity はクラスが永続化可能なエンティティであることを示す必須アノテーションです。オプションで name 属性を指定でき、データベースの対応するテーブル名として使用されます。

@Entity(name = "CustomEntity")
public class User { ... }

@Table はデータベース上のテーブル情報を明示的に指定します。name 属性により物理テーブル名を定義でき、デフォルトではクラス名がそのまま使用されます。

@Entity
@Table(name = "t_user_info")
public class User { ... }

ID 定義と生成戦略

@Id は主キーを宣言するために使用されます。@GeneratedValue と組み合わせることで、自動採番方式を設定できます。

  • AUTO: データベースの種類に応じて適切な戦略を選択
  • IDENTITY: データベースのオートインクリメント機能を利用
  • SEQUENCE: シーケンスオブジェクトを使用(Oracleなど)
  • TABLE: 専用のテーブルを使ってカウンタを管理
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

カラムマッピング

@Column を使用して、フィールドとデータベース列の詳細なマッピングを制御できます。

@Column(
  name = "user_name",
  nullable = false,
  unique = true,
  length = 64
)
private String userName;
  • name: テーブル上の列名(省略時はフィールド名)
  • nullable: NULL許可の有無(デフォルト: true)
  • unique: 一意制約の有無
  • length: 文字列型の最大長

継承構造の扱い

@MappedSuperclass は共通フィールドを持つ親クラスに付与します。このアノテーションがあると、親クラス自体にはテーブルが作成されず、子クラスのテーブル定義にフィールドが含まれます。

@MappedSuperclass
public abstract class BaseEntity {
    @Id
    private Long id;
    private LocalDateTime createdAt;
}

日時型の処理

@Temporaljava.util.DateCalendar 型に対して、データベースでの保存形式を指定します。

@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;

@Temporal(TemporalType.DATE)
private Date birthDate;

関連付けの実装パターン

以下は「収集ルート」「収集ポイント」「目的地」の3つのエンティティ間の関係をモデル化した例です。

1対多・多対1 関係(CollectLine と CollectLinePoint)

収集ルートが複数の収集ポイントを持つ場合、@OneToMany@ManyToOne を使用します。

@Entity
@Table(name = "t_collect_line")
public class CollectLine extends BaseEntity<Long> {

    @Column(name = "name", length = 100)
    private String name;

    @OneToMany(
        cascade = CascadeType.ALL,
        mappedBy = "collectLine",
        fetch = FetchType.LAZY
    )
    private List<CollectLinePoint> points = new ArrayList<>();
}
@Entity
@Table(name = "t_collect_line_point")
public class CollectLinePoint extends BaseEntity<Long> {

    @Column(name = "point_name", length = 50)
    private String name;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "collect_line_id")
    private CollectLine collectLine;
}

1対1 関係(CollectLine と Destination)

関係の所有者は外鍵を持つ側(ここでは Destination)になります。

@Entity
@Table(name = "t_destination_point")
public class Destination extends BaseEntity<Long> {

    @Column(name = "dest_name", length = 50)
    private String name;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "collect_line_id")
    private CollectLine collectLine;
}

双方向関連の注意点

JSONシリアル化時に循環参照を防ぐため、一方のプロパティに @JSONField(serialize = false) を付加することが推奨されます。特に、親から子への参照を非表示にするのが一般的です。

@JSONField(serialize = false)
@OneToMany(mappedBy = "collectLine")
private List<CollectLinePoint> points;

多対多関係の実現

@JoinTable を使用して中間テーブルを定義します。

@Entity
@Table(name = "t_role")
public class Role extends BaseEntity<Long> {

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "t_role_resource",
        joinColumns = @JoinColumn(name = "role_id"),
        inverseJoinColumns = @JoinColumn(name = "resource_id")
    )
    private Set<Resource> resources = new HashSet<>();
}
@Entity
@Table(name = "t_resource")
public class Resource extends BaseEntity<Long> {

    @ManyToMany(mappedBy = "resources")
    private Set<Role> roles = new HashSet<>();
}

上記により、t_role_resource という中間テーブルが生成され、role_idresource_id の複合主キーによって関係が管理されます。

タグ: jpa Hibernate アノテーション エンティティマッピング リレーションシップ

7月23日 17:41 投稿