Spring Data JPAにおける一対多・多対一・多対多の関係設定
主に使用されるアノテーションをいくつか紹介します。
@ManyToOne:多対一@ManyToMany:多対多@OneToMany:一対多@JoinColumn:テーブル間の関連付け@JsonIgnoreProperties:プロパティの無視(JSONの無限ループ防止)
たとえば、customer と bill の2つのテーブルがあるとします。1人の顧客は複数の請求書を持ち、それぞれのテーブルは customer.id と bill.customer_id を通して関連づけられます。
エンティティの定義は以下の通りです:
@Entity
@Table(name = "bill")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bill {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "customer_id")
private Long customerId;
@Column(name = "bill_no")
private String billNo;
@Column(name = "bill_amount")
private float billAmount;
@Column(name = "bill_date")
private LocalDate billDate;
}
@Entity
@Table(name = "customer")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "customer_name")
private String customerName;
@Column(name = "customer_age")
private Integer customerAge;
@Column(name = "customer_account")
private String customerAccount;
@Column(name = "customer_password")
private String customerPassword;
}
テーブルを関連付けるためには、エンティティに関連ロジックを追加する必要があります。
Customer.java に以下を追加します:
@OneToMany(targetEntity = Bill.class, fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JsonIgnoreProperties(value = {"customer"})
@JoinColumn(name = "customer_id",
referencedColumnName = "id",
insertable = false,
updatable = false)
private List<Bill> bills;
Bill.java に以下を追加します:
@ManyToOne(targetEntity = Customer.class, fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JsonIgnoreProperties(value = {"bills"})
@JoinColumn(name = "customer_id",
referencedColumnName = "id",
insertable = false,
updatable = false)
private Customer customer;
次に、これらのアノテーションの使い方について説明します。
@ManyToOne、@ManyToMany、@OneToMany
意味はアノテーション名から理解できるため省略しますが、各属性について確認します。
- targetEntity
型:Class<?>
関連するもう一方のエンティティのクラスを指定します。例では、CustomerとBillの関連で、Customerのbillsフィールドには@OneToManyを適用し、targetEntityにBill.classを指定しています。この属性は任意ですが、明示的に指定することを推奨します。
- fetch
型:javax.persistence.FetchType
ロード戦略を指定します。即時読み込み(FetchType.EAGER)または遅延読み込み(FetchType.LAZY)を指定できます。
- 即時読み込み:主エンティティを読み込む際に、関連するエンティティも同時に読み込まれます。これにより、データベースクエリ回数を減らすことができます。
- 遅延読み込み:主エンティティを読み込んだ時点では関連エンティティは読み込まれず、アクセスされた際に初めて読み込まれます。これにより不要なクエリを回避できます。
遅延読み込みを使用した際の注意点として、1件のAレコードが数十万件のBレコードに対応する場合、即時読み込みは非効率です。このような場合に遅延読み込みを使用すると、以下のようなエラーが発生することがあります。
No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer...同様の問題に遭遇した場合は、以下のアノテーションをエンティティクラスに追加してください:
@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}) public class Bill { // 実装(省略) }
- cascade
型:javax.persistence.CascadeType[]
カスケード操作を定義します。保存、更新、削除などの永続化操作を関連するエンティティにも適用します。CascadeTypeの選択肢は以下の通りです。
CascadeType.ALL:すべての操作(PERSIST、MERGE、REMOVE、REFRESH、DETACH)がカスケードされます。CascadeType.PERSIST:新しいエンティティが作成された場合、関連エンティティも保存されます。CascadeType.MERGE:エンティティがマージされる際、関連エンティティもマージされます。CascadeType.REMOVE:エンティティが削除された場合、関連エンティティも削除されます。CascadeType.REFRESH:エンティティがリフレッシュされる際、関連エンティティもリフレッシュされます。CascadeType.DETACH:エンティティがコンテキストから分離される際、関連エンティティも分離されます。
@JoinColumns:複数カラムによる関連
2つのテーブルが複数のカラムによって関連付けられている場合、@JoinColumns を使用して複数の @JoinColumn をまとめて定義します。
@JoinColumn:関連カラムの定義
2つのエンティティをどのカラムで関連付けるかを指定します。以下の属性があります。
- name
型:string
自テーブルの外部キーとなるカラム名を指定します。
- referencedColumnName
型:string
参照先テーブルのカラム名(外部キー)を指定します。
- unique
型:bool
外部キーのカラムに一意制約を設定するかどうかを指定します。デフォルトはfalseです。
- nullable
型:bool
外部キーがNULL値を許容するかどうかを指定します。デフォルトはtrueです。
- insertable
型:bool
新規挿入時にこのカラムを含めるかどうかを指定します。デフォルトはtrueです。
- updatable
型:bool
更新時にこのカラムを含めるかどうかを指定します。デフォルトはtrueです。
- columnDefinition
型:string
外部キーのSQL定義を指定します。カラムの型やサイズなどをカスタマイズできます。
- table
型:string
外部キーが存在するテーブル名を指定します。継承構造などで使用されます。
- foreignKey
型:String
外部キーの制約名を指定します。省略した場合、Spring Data JPAが自動的に生成します。
@JsonIgnoreProperties:JSON出力時のプロパティ除外
関連プロパティにこのアノテーションを適用しない場合、JSONの出力時に無限ループが発生します。主な属性は以下の通りです。
- value
型:string[]
JSON出力時に無視するプロパティ名を指定します。例では、CustomerのbillsとBillのcustomerを相互に無視するように設定しています。
- ignoreUnknown
型:boolean
JSONからJavaオブジェクトへの変換時に、未知のプロパティを無視します。デフォルトはfalseです。
- allowGetters
型:boolean
getterメソッドが存在しないプロパティでもアクセス可能になります。デフォルトはfalseです。
- allowSetters
型:boolean
setterメソッドが存在しないプロパティでも設定可能になります。デフォルトはfalseです。
以上が基本的な設定方法です。今後追加情報があれば更新予定です。