JPA - 다대다 연관관계(@ManyToMany),N:N

2021. 4. 15. 14:29 Spring Data/Spring Data JPA

설명에 앞서 사실 관계형 데이터베이스는 정규화된 테이블 2개로 다대다 곤계를 표현할 수 없다. 그래서 보통 다대다 관계를 일대다,다대일 관계로 풀어내는 연결 테이블을 사용한다. 왜냐하면 다대다 관계를 1:1 테이블 매핑은 한다고 생각해보자. 회원과 상품의 관계인데, 한 회원이 여러개의 상품을 구입할 수 있고, 한 상품(ID)이 여러 회원에 의해 구입될 수 있다. 그렇다면 서로 몇개까지 살 수 있냐라는 제한이 없으면 외래키가 유동적으로 늘어난다. 그렇다면 엄청 많은 외래키를 굳이 미리 생성할 필요도 없다. 즉, 이렇게 몇개인지 알수 없는 다대다 관계를 중간에 연결 테이블 하나를 두고 일대다, 다대일 관계로 매핑을 시켜주는 것이다. 연결테이블은 단순히 하나의 로우에 회원의 기본키,상품의 기본키를 가지고 있으면 되므로 관계의 수가 늘어나면 단순히 로우의 수만 증가시켜주면 되기 때문이다.

 

 

(현재소스와는 관계없는 그림)

 

다대다(@ManyToMany) 매핑 방법 1

package com.spring.jpa.entitiy;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
 
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
 
import com.spring.jpa.common.RoleType;
 
 
/*
 * 유니크키 설정 및 nullable,length 등의 속성은 모두 auto DDL을 사용했을 때만 유효한 설정이다.
 * 즉, 테이블을 직접 생성한다면 적용되지 않는다. 하지만 테이블과 객체간의 관계표현에 있어 해당 설정들을 해놓으면
 * 엔티티 클래스만 봐도 테이블의 구조가 파악되기에 가독성을 위해서라도 설정을 해놓는 것이 좋다.
 */
/*
 * sequence table
 * CREATE TABLE MY_SEQUENCE(
 *    sequence_name varchar2(255) PRIMARY KEY,
 *    next_val number(22,0)
 * )
 */
@Entity
@Table(name = "MEMBER"
       ,uniqueConstraints = {
           @UniqueConstraint(
                   name = "NAME_AGE_UNIQUE",
                   columnNames = {"NAME","AGE"} //uniqueConstraints는 auto DDL 속성을 사용할때만 유효한 설정이다.
           )
})
public class Member {
    
    @Id
    @Column(name = "MEMBER_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
    @TableGenerator(
            name="MEMBER_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="MEMBER_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    /*
     * not null
     * varchar2(10) -> 기본값 255;
     */
    @Column(name = "NAME",nullable=false,length=10)
    private String username;
    
    private Integer age;
    
    /*
     * EnumType의 기본값 설정은 정수이다.
     */
    @Enumerated(EnumType.STRING)
    @Column(name="ROLE_TYPE",nullable=false,length=20)
    private RoleType roleType;
    
    @Temporal(TemporalType.TIMESTAMP)
    @Access(AccessType.FIELD)
    private Date createdDate = new Date();
    
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    
    @Lob
    private String description;
    
    @ManyToMany
    //다대다를 일대다-다대일 관계로 연결해줄 테이블명
    @JoinTable(name = "MEMBER_PRODUCT_CONN",
               joinColumns = @JoinColumn(name = "MEMBER_ID"),//멤버랑 연결시켜줄 연결테이블의 컬럼명(현재방향)
               inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))//상품과 연결시켜줄 연결테이블의 칼럼명(반대방향)
    private List<Product> products = new ArrayList<Product>();
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public RoleType getRoleType() {
        return roleType;
    }
 
    public void setRoleType(RoleType roleType) {
        this.roleType = roleType;
    }
 
    public Date getCreatedDate() {
        return createdDate;
    }
 
    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }
 
    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }
 
    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public List<Product> getProducts() {
        return products;
    }
 
    public void setProducts(List<Product> products) {
        this.products = products;
    }
 
    @Override
    public String toString() {
        return "Member [id=" + id + ", username=" + username + ", age=" + age + ", roleType=" + roleType
                + ", createdDate=" + createdDate + ", lastModifiedDate=" + lastModifiedDate + ", description="
                + description + ", products=" + Arrays.toString(products.toArray()) + "]";
    }
 
    
    
}
package com.spring.jpa.entitiy;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
 
@Entity
@Table(name = "TB_PRODUCT")
public class Product {
    
    @Id
    @Column(name = "PRODUCT_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "PRODUCT_SEQ_GENERATOR")
    @TableGenerator(
            name="PRODUCT_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="PRODUCT_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    @Column(name = "PRODUCT_NAME")
    private String name;
    
    @ManyToMany(mappedBy = "products")
    private List<Member> members = new ArrayList<Member>();
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public List<Member> getMembers() {
        return members;
    }
 
    public void setMembers(List<Member> members) {
        this.members = members;
    }
    
    
}
@ManyToMany
    //다대다를 일대다-다대일 관계로 연결해줄 테이블명
    @JoinTable(name = "MEMBER_PRODUCT_CONN",
               joinColumns = @JoinColumn(name = "MEMBER_ID"),//멤버랑 연결시켜줄 연결테이블의 컬럼명(현재방향)
               inverseJoinColumns = @JoinColumn(name = "PRODUCT_ID"))//상품과 연결시켜줄 연결테이블의 칼럼명(반대방향)
    private List<Product> products = new ArrayList<Product>();

 

매핑에 대한 상세 설명을 하자면 우선 연관관계를 맺을 필드에 @ManyToMany 어노테이션을 달아준다. 이것은 현재 참조하고 있는 컬렉션과 다대다 관계임을 명시해준다. 하지만 여기서 의문이 드는것이 "다대다 관계가 안된다며?"이다. 이것은 다음 속성에 나온다. @JoinTable로 중간에 연결테이블에 대한 속성을 정의해준다. name속성은 연결테이블의 이름, joinColums는 연관관계를 맺어줄 연결테이블의 컬럼을 정의해준다. @JoinColums는 현재 회원테이블에 대한 외래키이고, inverseJoinColums는 반대쪽 상품 테이블에 대한 외래키이다. 그리고 반드시 연관관계에는 연관관계의 주인이 있어야 하므로, 회원테이블에 연관관계의 주인임을 명시해주었다.(상품에 mappedBy속성이 있음으로) 즉, @ManyToMany도 결국에 데이터베이스에는 일대다,다대일 관계로 매핑되며 중간에 연결테이블이 생성된다.

 

이렇게 편하게 다대다 관계를 맺어줄 수 있다. 하지만 이 연관관계는 한가지 한계점이 존재한다. 실무에서는 연결테이블에 단순 외래키만 존재하길 원하지 않는다. 회원이 몇개의 상품을 주문했는지의 수량, 언제 주문했는지 날짜등의 데이터를 연결테이블에 있길 원할 수도 있기 때문인데, 이것은 @ManyToMany로 매핑할 수 없다. 이 한계점을 개선한 다대다 매핑을 다음에 설명한다.

 

다대다(@ManyToMany -> @OneToMany,@ManyToOne & 복합기본키 ) 매핑 방법 2

package com.spring.jpa.manytomanyexpend;
 
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
 
import com.spring.jpa.common.RoleType;
import com.spring.jpa.entitiy.Product;
 
@Entity
@Table(name = "MEMBER_2"
       /*,uniqueConstraints = {
           @UniqueConstraint(
                   name = "NAME_AGE_UNIQUE",
                   columnNames = {"NAME","AGE"} //uniqueConstraints는 auto DDL 속성을 사용할때만 유효한 설정이다.
           )
}*/)
public class Member_2 {
    
    @Id
    @Column(name = "MEMBER_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "MEMBER2_SEQ_GENERATOR")
    @TableGenerator(
            name="MEMBER2_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="MEMBER2_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    /*
     * not null
     * varchar2(10) -> 기본값 255;
     */
    @Column(name = "NAME",/*nullable=false,*/length=10)
    private String username;
    
    private Integer age;
    
    /*
     * EnumType의 기본값 설정은 정수이다.
     */
    @Enumerated(EnumType.STRING)
    @Column(name="ROLE_TYPE",/*nullable=false,*/length=20)
    private RoleType roleType;
    
    @Temporal(TemporalType.TIMESTAMP)
    @Access(AccessType.FIELD)
    private Date createdDate = new Date();
    
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    
    @Lob
    private String description;
    
    @OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts ;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public RoleType getRoleType() {
        return roleType;
    }
 
    public void setRoleType(RoleType roleType) {
        this.roleType = roleType;
    }
 
    public Date getCreatedDate() {
        return createdDate;
    }
 
    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }
 
    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }
 
    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public List<MemberProduct> getMemberProducts() {
        return memberProducts;
    }
 
    public void setMemberProducts(List<MemberProduct> memberProducts) {
        this.memberProducts = memberProducts;
    }
 
    
    
}
package com.spring.jpa.manytomanyexpend;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.TableGenerator;
 
//회원엔티티와 상품엔티티의 다대다 연결을 위한 연결엔티티이다.
@Entity
//복합 기본키 매핑을 위한 식별자 클래스
@IdClass(MemberProductId.class)
public class MemberProduct {
    
    @Id
    @ManyToOne
    @JoinColumn(name = "MEMBER_ID") //외래키의 주인이며, MEMBER_ID로 해당테이블에 외래키가 생성된다.
    private Member_2 member;
    
    @Id
    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID") //위와 동일
    private Product2 product;
    
    private int orderAmount;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public Member_2 getMember() {
        return member;
    }
 
    public void setMember(Member_2 member) {
        this.member = member;
    }
 
    public Product2 getProduct() {
        return product;
    }
 
    public void setProduct(Product2 product) {
        this.product = product;
    }
 
    public int getOrderAmount() {
        return orderAmount;
    }
 
    public void setOrderAmount(int orderAmount) {
        this.orderAmount = orderAmount;
    }
    
    
}
package com.spring.jpa.manytomanyexpend;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
 
import com.spring.jpa.entitiy.Member;
 
@Entity
@Table(name = "TB_PRODUCT2")
public class Product2 {
    
    @Id
    @Column(name = "PRODUCT_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "PRODUCT2_SEQ_GENERATOR")
    @TableGenerator(
            name="PRODUCT2_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="PRODUCT2_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    @Column(name = "PRODUCT_NAME")
    private String name;
    
    /*@ManyToMany(mappedBy = "products")
    private List<Member> members = new ArrayList<Member>();*/
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    
    
}
package com.spring.jpa.manytomanyexpend;
 
import java.io.Serializable;
 
public class MemberProductId implements Serializable{
    
    private Long member;
    private Long product;
    
    
    public Long getMember() {
        return member;
    }
    public void setMember(Long member) {
        this.member = member;
    }
    public Long getProduct() {
        return product;
    }
    public void setProduct(Long product) {
        this.product = product;
    }
    
    //복합키 식별자 클래스는 반드시 밑의 메소드를 오버라이드해주어야한다.
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((member == null) ? 0 : member.hashCode());
        result = prime * result + ((product == null) ? 0 : product.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MemberProductId other = (MemberProductId) obj;
        if (member == null) {
            if (other.member != null)
                return false;
        } else if (!member.equals(other.member))
            return false;
        if (product == null) {
            if (other.product != null)
                return false;
        } else if (!product.equals(other.product))
            return false;
        return true;
    }
    
}

 

이번 다대다 매핑은 명시적으로 어노테이션도 @OneToMany,@ManyToOne으로 연관관계를 맺었다. 이전에는 내부적으로 연결테이블을 생성했지만 지금은 명시적으로 연결 엔티티를 생성해준다. 그리고 연결엔티티에서 @ManyToOne 어노테이션을 갖는다. 이 말은 즉슨, 연결테이블이 연관관계의 주인이 되는 것이다.(보통 데이터베이스에서 다대일,일대다 관계에서 다 쪽에 외래키를 갖는다.) 하지만 여기서 조금 특이한 것이 있다면 @IdClass이다. 이것은 회원과 상품의 기본키를 연결테이블에서 외래키로 사용함과 동시에 두키를 복합키로 하여 기본키를 지정하기 때문에 식별자 클래스가 추가된 것이다.(회원의 외래키와 상품의 외래키를 복합키로 하여 기본키로 지정) 식별자 클래스는 별개 없다. 단순히 각 테이블(회원,상품)의 기본키의 필드타입으로 하여서 두개의 식별자로 사용될 필드를 선언하고 Getter,Setter메소드를 만들어 준 후에 IDE의 기능을 이용하여 hashCode()와 equals()를 자동 구현해주면된다. 그리고 마지막으로 @Embeddable 클래스는 반드시 기본생성자를 필수로 생성해주고, Serializable을 implements 해주면 된다. 하지만 복합키도 좋지만 이렇게 되면 해야할 일이 늘어난다. 식별자 클래스를 만들어주는 등의.... 그래서 다음 매핑방법에는 복합키를 사용하지 않고 연결엔티티에 별도로 기본키를 할당해주어서 식별자 클래스등을 만드는 불편함을 줄이겠다.

 

다대다(@ManyToMany -> @OneToMany,@ManyToOne ) 매핑 방법 3

package com.spring.jpa.manytomanyexpend;
 
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
 
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
 
import com.spring.jpa.common.RoleType;
import com.spring.jpa.entitiy.Product;
 
@Entity
@Table(name = "MEMBER_2"
       /*,uniqueConstraints = {
           @UniqueConstraint(
                   name = "NAME_AGE_UNIQUE",
                   columnNames = {"NAME","AGE"} //uniqueConstraints는 auto DDL 속성을 사용할때만 유효한 설정이다.
           )
}*/)
public class Member_2 {
    
    @Id
    @Column(name = "MEMBER_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "MEMBER2_SEQ_GENERATOR")
    @TableGenerator(
            name="MEMBER2_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="MEMBER2_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    /*
     * not null
     * varchar2(10) -> 기본값 255;
     */
    @Column(name = "NAME",/*nullable=false,*/length=10)
    private String username;
    
    private Integer age;
    
    /*
     * EnumType의 기본값 설정은 정수이다.
     */
    @Enumerated(EnumType.STRING)
    @Column(name="ROLE_TYPE",/*nullable=false,*/length=20)
    private RoleType roleType;
    
    @Temporal(TemporalType.TIMESTAMP)
    @Access(AccessType.FIELD)
    private Date createdDate = new Date();
    
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
    
    @Lob
    private String description;
    
    @OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts = new ArrayList<MemberProduct>();
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public RoleType getRoleType() {
        return roleType;
    }
 
    public void setRoleType(RoleType roleType) {
        this.roleType = roleType;
    }
 
    public Date getCreatedDate() {
        return createdDate;
    }
 
    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }
 
    public Date getLastModifiedDate() {
        return lastModifiedDate;
    }
 
    public void setLastModifiedDate(Date lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }
 
    public String getDescription() {
        return description;
    }
 
    public void setDescription(String description) {
        this.description = description;
    }
 
    public List<MemberProduct> getMemberProducts() {
        return memberProducts;
    }
 
    public void setMemberProducts(List<MemberProduct> memberProducts) {
        this.memberProducts = memberProducts;
    }
 
    
    
}
package com.spring.jpa.manytomanyexpend;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
 
import com.spring.jpa.entitiy.Member;
 
@Entity
@Table(name = "TB_PRODUCT2")
public class Product2 {
    
    @Id
    @Column(name = "PRODUCT_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "PRODUCT2_SEQ_GENERATOR")
    @TableGenerator(
            name="PRODUCT2_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="PRODUCT2_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    @Column(name = "PRODUCT_NAME")
    private String name;
    
    @OneToMany(mappedBy = "product")
    private List<MemberProduct> members = new ArrayList<MemberProduct>();
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public List<MemberProduct> getMembers() {
        return members;
    }
 
    public void setMembers(List<MemberProduct> members) {
        this.members = members;
    }
    
    
}
package com.spring.jpa.manytomanyexpend;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.TableGenerator;
 
//회원엔티티와 상품엔티티의 다대다 연결을 위한 연결엔티티이다.
@Entity
//복합 기본키 매핑을 위한 식별자 클래스
/*@IdClass(MemberProductId.class)*/
public class MemberProduct {
    
    @Id
    @Column(name = "MEMBERPRODUCT_ID")
    @GeneratedValue(strategy=GenerationType.TABLE, generator = "MEMBERPRODUCT_SEQ_GENERATOR")
    @TableGenerator(
            name="MEMBERPRODUCT_SEQ_GENERATOR",
            table="MY_SEQUENCE",
            pkColumnName="SEQ_NAME", //MY_SEQUENCE 테이블에 생성할 필드이름(시퀀스네임)
            pkColumnValue="MEMBERPRODUCT_SEQ", //SEQ_NAME이라고 지은 칼럼명에 들어가는 값.(키로 사용할 값)
            allocationSize=50
    )
    private Long id;
    
    /*@Id*/
    @ManyToOne
    @JoinColumn(name = "MEMBER_ID") //외래키의 주인이며, MEMBER_ID로 해당테이블에 외래키가 생성된다.
    private Member_2 member;
    
    /*@Id*/
    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product2 product;
    
    private int orderAmount;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public Member_2 getMember() {
        return member;
    }
 
    public void setMember(Member_2 member) {
        this.member = member;
    }
 
    public Product2 getProduct() {
        return product;
    }
 
    public void setProduct(Product2 product) {
        this.product = product;
    }
 
    public int getOrderAmount() {
        return orderAmount;
    }
 
    public void setOrderAmount(int orderAmount) {
        this.orderAmount = orderAmount;
    }
    
    
}

 

크게 바뀐 것은 없다. 식별자 클래스가 없어지고, 연결엔티티에 외래키를 기본키로 사용하는 @Id가 없어지는 등 조금의 수정이 이루어졌을 뿐이다. 단순히 시퀀스를 기본키로 사용하므로서 복합키 매핑등의 조금은 복잡한 과정이 빠져 더 쉽게 연관관계 매핑을 할 수 있다. 위의 코드와 비교해보면 차이점을 금방 알 수 있다.


출처: https://coding-start.tistory.com/72?category=781616 [코딩스타트]