17.様々な関連関係のマッピング(3)


17.様々な関連関係のマッピング(3)


4.多対多[N:M]


リレーショナル・データベースでは、2つの標準化されたテーブルで複数のリレーショナルを表すことはできません.したがって、通常、多対多関係を1対多対一関係に分解する接続テーブルが使用されます.下図を見る.例えば、会員が商品を注文する.逆に、商品は会員が注文します.両者とも互いに依存する関係である.そのため、会員表と商品表だけではこのような関係は表現できません.

したがって、上図のように、接続テーブルを中間に追加する必要があります.この図を見て、Member Product接続テーブルを追加しました.このテーブルを使用して、複数対の複数の関係を1対の複数対の関係に分解します.この連結表は会員が注文した商品を表しています.

ただし、表とは異なり、オブジェクトは2つのオブジェクトから多対多関係を作成できます.たとえば,会員オブジェクトは集合参照商品を用いてもよいし,逆に商品は集合参照会員を用いてもよい.
@ManyToManyを使用すると、次の図に示すように、これらの多対多関係を簡単にマッピングできます.

1.多対多:一方向


多対多の一方向関係の会員と商品実体を見てみましょう.
@Entity
public class Member {

    @Id @Column(name = "MEMBER_ID")
    private String id;
    
    private String username;
    
    @ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT",
    joinColumns = @JoinColumn(name = "MEMBER_ID"),
    inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))
    private List<Product> products = new ArrayList<Product>();
    ...
}
@Entity
public class Product {

    @Id @Column(name = "PRODUCT_ID")
    private String id;
    
    private String name;
    ...
}
メンバーエンティティと商品エンティティを@ManyToManyにマップします.ここで重要なのは、@ManyToManyと@JoinTableを使用して接続テーブルに直接マッピングすることです.したがって,会員と商品を接続する会員商品エンティティを必要とせずにマッピングを完了する.
マッピング接続テーブルの@JoinTableプロパティを整理します.
  • @JoinTable.name:接続テーブルが指定されています.ここではMEMBER PRODUCT表を選びました.
  • @JoinTable.joinColumns:メンバーにマッピングする結合列情報を現在の方向に指定します.MEMBER IDとして指定します.
  • @JoinTable.逆JoinColumns:商品にマッピングする結合列情報を逆方向に指定します.PRODUCT IDに指定します.
  • MEMBER PRODUCTテーブルは、多対多関係を1対多または多対一関係に分解するために必要な接続テーブルの一部である.
    ただ.ManyToManyにマッピングされているため、この接続テーブルを考慮する必要はありません.
    以下に、複数対の複数の関係を格納する例を示します.
    public void save() {
    
        Product productA = new Product();
        productA.setId("productA");
        productA.setName("상품A");
        em.persist(productA);
        
        Member member1 = new Member();
        member1.setId("member1");
        member1.setUsername("회원1");
        member1.getProducts(0.add(productA)  // 연관관계 설정
        em.persist(member1);
        
    }
    会員1と商品Aとの関連関係が設けられているため、会員1を保存する際には、接続テーブルにも値が格納される.したがって、このコードを実行すると、次のSQLが実行されます.
    INSERT INTO PRODUCT ...
    INSERT INTO MEMBER ...
    INSERT INTO MEMBER_PRODUCT ...
    public void find() {
    
        Member member = em.find(Member.class, "member1");
        List<Product> products = member.getProducts();  // 객체 그래프 탐색
        for (Product product : products) {
            System.out.println("product.name = " + product.getName());
        }
    }
    次の例です.順番に保存してから、探してみると、保存されている商品が見つかります.
    member.getProducts()を呼び出して商品名を出力すると、次のSQLが実行されます.
    SELECT * FROM MEMBER_PRODUCT MP
    INNER JOIN PRODUCT P ON MP.PRODUCT_ID = P.PRODUCT_ID
    WHERE MP.MEMBER_ID=?
    実行するSQLに従って、接続テーブルMEMBER PRODUCTと商品テーブルをチェックインして関連商品を検索します.
    @ManyToManyは、複雑な多対多関係をアプリケーションで非常に簡単にします.今、この関係を双方向に変えましょう.

    2.多対多:双方向


    次の例はマルチペアマルチマッピングであるため、逆@ManyToManyを使用します.そして双方の任意の位置においてmappedByで関連関係のオーナーを指定する(もちろん、mappedByがないところは関連関係のオーナーである).
    @Entity
    public class Product {
        
        @Id
        private String id;
        
        @ManyToMany(mappedBy = "products")  // 역방향 추가
        private List<Member> members;
        ...
    }
    複数対複数の双方向関連は、次のように設定されます.
    member.getProducts().add(product);
    members.getMembers().add(member);
    双方向関連は、関連関係編を追加する方法で管理しやすい.次の図に示すように、メンバーエンティティに関連付けを追加するのに便利な方法です.
    public void addProduct(Product product) {
        ...
        products.add(product);
        product.getmembers().add(this);
    }
    関連関係編のメソッドを追加したので、以下のように双方向関連を簡単に設定できます.
    member.addProduct(product);
    双方向の関連関係なので、製品です.getMembers()を使用して、オブジェクトシェイプを逆方向にブラウズできます.次の例です.
    public void findInverse() {
    
        Product product = em.find(Product.class, "productA");
        List<Member> members = product.getMembers();
        for (Member member : members) {
            System.out.println("member = " + member.getUsername());
        }
    }
    リファレンス
  • Java ORM標準JPAプログラミング