🍊
JPAの@ManyToManyとは
1.多対多
関係データベースは正規化されたテーブル二つで多対多関係を表すことができない。
- 多対多関係を表す為には連想テーブル(中間テーブル)が必要である。
反面オブジェクトはコレクションを利用してオブジェクト二つで多対多関係ができる。
2.@ManyToManyとは
@ManyToManyとはRDBとオブジェクト間のパラダイムの不一致を解消し簡単にマッピングしてくれるアノテーション。
2.1 使用方法
-
@ManyToMany
使用 -
@JoinTable
で連想テーブル指定 - 多対多マッピング : 片方向、双方向可能。
2.1.1 片方向
-
@ManyToMany
のみ指定した時
Member.java
@Entity @Getter
@Table(name = "MEMBER")
public class Member {
...
@ManyToMany
private List<Product> products = new ArrayList<>();
...
}
自動的に連想テーブル(中間テーブル)を作ってくれる。
- テーブル名やカラム名も任意に設定してくれる。
- FKも設定してくれる。
# 自動生成されたDDL文
create table MEMBER_PRODUCT (
Member_MEMBER_ID bigint not null,
products_PRODUCT_ID bigint not null
) engine=InnoDB
alter table MEMBER_PRODUCT
add constraint FKeenqm751wrk56y5nn62daim8f
foreign key (products_PRODUCT_ID)
references PRODUCT (PRODUCT_ID)
alter table MEMBER_PRODUCT
add constraint FKf8wyhskc9dlf0xlrsodwxa7wf
foreign key (Member_MEMBER_ID)
references MEMBER (MEMBER_ID)
- この二つのエンティティに値を入れてみると
select MEMBER_ID, USERNAME, PRODUCT_ID, NAME
from MEMBER M
inner join MEMBER_PRODUCT MP
on M.MEMBER_ID = MP.Member_MEMBER_ID
inner join PRODUCT P
on MP.products_PRODUCT_ID = P.PRODUCT_ID;
ちゃんと入っていることを確認できる。
-
@JoinTalble
と一緒に使用
連組テーブル名やカラム名を指定する事できる。
Member.java
@ManyToMany
@JoinTable(
name = "MEMBER_PRODUCT",
joinColumns = @JoinColumn(name = "MEMBER_ID"),
inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID")
)
@Setter
private List<Product> products = new ArrayList<>();
2.1.2 双方向
Product
に@ManyToMany(mappedBy = “products”)
を追加
@Entity @Getter
@Table(name = "PRODUCT")
public class Product {
@Id @GeneratedValue(strategy = IDENTITY)
@Column(name = "PRODUCT_ID")
private Long id;
@Column(name = "NAME", length = 50)
@Setter
private String name;
@ManyToMany(mappedBy = "products")
private List<Member> members = new ArrayList<>();
}
3.@ManyToManyの限界
本当に楽に見えるが、実務で使用してはならない、連想テーブルは単純に連結だけで終わらない。下の関係を例にするとオーダータイムやオーダー量みたいなデータが追加される可能性が高い。
だが、@ManyToMany
の連組テーブルは隠れているためカラムの追加など変化に対応できない。又、どんなクエリが出るか予想できない。
4.まとめ
RDBと同じく連組テーブルを中間に置いて解決する方法を使用
連組テーブルをのエンティティを桁上げし@OneToMany, @ManyToOne関係で解決する
4.1 エンティティを桁上げ、片方向の例
Member.java
@Entity
@Getter
@Table(name = "MEMBER")
public class Member {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME", length = 50)
@Setter
private String username;
@Setter
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts =
new ArrayList<>();
}
MemberProduct.java
@Entity
@Table(name = "MEMBER_PRODUCT")
public class MemberProduct {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "MEMBER_PRODUCT_ID")
private Long id;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "PRODUCT_ID")
private Product product;
@Column(name = "ORDERAMOUNT")
private int orderAmount;
@Column(name = "ORDERDATE")
private LocalDateTime orderDate;
}
Product.java
@Entity
@Getter
@Table(name = "PRODUCT")
public class Product {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "PRODUCT_ID")
private Long id;
@Column(name = "NAME", length = 50)
@Setter
private String name;
// 片方向なのでProductには何もない。
}
5. JPAの自己結合
おまけ、JPAの自己結合
Category.java
@Entity
public class Category {
...
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Category parent;
...
}
- CATEGORY
次(@MappedSuperclass)
Discussion