연관간계 매핑2 (다대다, 다대일 양방향)

김성인·2023년 10월 4일
0

🧷SpringBoot JPA

목록 보기
10/10

ex) N: 멤버, 1: 팀

💨다대일 (N:1)

➡ 다대일 단방향 (N:1)

다대일 단 방향에서는 외래키를 가지는 엔티티가 "연관관계의 주인"이 된다.

다(N) 엔티티

@Entity
public class NClass{
	@Id @GeneratedValue
    @column(name = "N_ID")
    private Long id;
    
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "ONE_ID")  //*****
    private OneClass oneclass;
}

일(1) 엔티티

@Entity
public class OneClass{
	@Id @GeneratedValue
    @column(name = "ONE_ID")
    private Long id;
    
    private String name;
}

"일(1) 엔티티"가 "다(N) 엔티티"를 외래키로 관리하게 된다.


↔ 다대일 양방향 (N:1)

다(N) 엔티티

@Entity
public class NClass{
	@Id @GeneratedValue
    @column(name = "N_ID")
    private Long id;
    
    private String name;
    
    @ManyToOne 
    @JoinColumn(name = "ONE_ID")
    private OneClass oneclass;
    
    public void setOne(OneClass oneclass){ // Nclass와 무한루프 방지
    	this.oneclass = oneclass;
        
        if(!oneclass.getN().contains(this)){
        	oneclass.getN().add(this);
        }
    }
}

일(1) 엔티티

@Entity
public class OneClass{
	@Id @GeneratedValue
    @column(name = "ONE_ID")
    private Long id;
    
    private String name;

    @OneToMany(mappedBy = "oneclass") //*****
    private List<NClass> nClass = new ArrayList<NClass>();
    
    public void addN(Oneclass nClass){ // OneClass와 무한루프 방지
    	this.nClass.add(oneclass);
        if(nClass.getOneClass() != this){
        	nClass.setOneclass(this);
        }
    }
}
  • 연관관계 매핑에서 외래키를 관리하는 곳은 항상 ManyToOne을 가진 칼럼의 엔티티
    = 다(N) 엔티티
  • 양방향 연관관계에서는 항상 서로를 참조시켜야함. (편의 메서드를 작성하여 양쪽다 객체 관점에서 데이터를 처리)

💨일대다 (1:N)

➡ 일대다 단방향(1:N)

JPA 2.0 부터 지원

다(N) 엔티티

@Entity
public class NClass{
	@Id @GeneratedValue
    @column(name = "N_ID")
    private Long id;
    
    private String name;
}

일(1) 엔티티

@Entity
public class OneClass{
	@Id @GeneratedValue
    @column(name = "ONE_ID")
    private Long id;
    
    private String name;
    
    @OneToMany //*****
    @JoinColumn(name = "N_ID")
    private List<NClass> nClass = new ArrayList<NClass>();
}

오히려 다 쪽에서 외래키를 관리하게된다.

  • 일대다 단 방향 관계를 매핑할 때는 @JoinColumn을 명시 해야함.
  • 매핑 관계에서 외래키가 반대쪽 엔티티에 저장되어 있다.
    (N에 원래 있어야하는데 1엔티티에서 관리)
  • INSERT 외에 UPDATE문 까지 추가로 쿼리가 만들어지게 됨.

다대일 양방향을 사용하도록 하는 것을 권장함..!


일대일 (1:1)

주 테이블 외래키

-> 객체 지향 관점에서 더 편함.

  • 주 테이블에서 외래키를 단방향으로 가질 수는 있다.

대상 테이블 외래키

-> DB 테이블 관리 관점에서 더 편함. (일대다) 테이블로 전환하기 더 용이함.

  • 일대일 관계에서는 대상테이블에서 주 테이블로 단방향 매핑이 존재하지 않음..
  • 반드시 양방향을 통해 관계를 형성하여야 한다.

대상 테이블 외래키

@Entity
public class MainClass{
	@Id @GeneratedValue
    @Column(name = "MAIN_ID")
    private Long id;
    
    private String name;
    
    @OneToOne(mappedBy = "main")
    private SubClass sub;
}
@Entity
public class SubClass{
	@Id @GeneratedValue
    @Column(name = "SUB_ID")
    private Long id;
    
    private String name;
    
    @OneToOne
    @JoinColumn(name = "MAIN_ID")
    private MainClass main;

프록시를 사용할 때 외래키를 직접 관리하지 않는 MainClass (mapped By를사용하는 매핑 관계의 주인이 아닌 엔티티)는 지연로딩이 되지만, SubClass는 즉시 로딩이 됨.


💨다대다 (N:N)

@ManyToMany

@JoinTable

  • name : 연결 테이블 지정
  • joinColumns: 현재 엔티티에서 매핑할 조인 컬럼 정보 (다른 엔티티에서 외래키로 보는 내 기본키)
  • inverseColumns: 대상 엔티티를 매핑할 외래키
ex)
@ManyToMany
@JoinTable(name = "CONN_TABLE", 
			joinColumns = @JoinColumn(name="My_PK"),
            inserveJoinColumns = @JoinColumn(name="Target_FK"))
private List<Target> targets = new ArrayList<Target>();

복합키를 기본키로 하는 다대다 관계

@Entity
public class Member{
	@Id @Column(name = "MEMEBER_ID")
    private String id;
    
    @OneToMany(mappedBy = "member")
    private List<MemberProduct> MemberProducts;
}
@Entity
public class Product{
	@Id @Column(name = "PRODUCT_ID")
    private String id;
    private String name;
}
@Entity
@IdClass(MemberProductId.Class)
public class MemberProduct{
	@Id
    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;
    
    @Id
    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;
    
    private int orderAmount;
    private int orderPrice;
}
public class MemberProductId implements Serializable{
	private String member;
    private String product;
    
    @Override
    equals(), hashCode();
}
  • 복합 기본키를 사용할 시에 식별자를 위한 클래스를 반드시 만들어야함
  • 복합 기본키 식별자 클래스는 Serializable을 인터페이스로 사용.
  • 반드시 public 클래스로 지정되어야 함. @EmbeddedId 어노테이션으로 대체 가능함.
  • 위와 같이 복합기본키를 특정 엔티티의 기본키로 사용하는 상태를 식별 관계라고 함.

복합키 조회

MemberProductId mpId = new MemberProductId();
mpId.setMember("member1");
mpId.setProduct("productA");

MemberProduct mp = em.find(MemberProduct.class, memberProductId);

다대다 기본키 사용

복합키말고 해당 엔티티에 기본키를 새로 쥐어주고, 타 테이블 엔티티를 그냥 외래키 참조 형태로 구현하면 더 간단하다.

@Entity
public class Order{
	@Id @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;
    
    @ManyToOne
    @JoinCOlumn(name = "MEMBER_ID")
    private Member member;
    
    @ManyToOne
    @JonColumn(name = "PRODUCT_ID")
    private Product product;
    
    private int orderAmount;
    ...
}

조회

Long orderId = 1L;
Order order = em.find(Order.class, orderId);

외래키 관계를 이렇게 쉽게 정의하다니 편해보인다...
이전에 했던 Jdbc를 사용한 프로젝트를 이렇게 다 변환해서 매핑하면 얼마나 편할지 상상이 안감..

profile
개발자가 꿈인 25살 대학생입니다.

0개의 댓글