TIL

다양한 연관 관계 매핑

연관 관계 매핑 시 고려 사항 3가지

다중성

단방향, 양방향

연관 관계의 주인

다대일 [N:1]

다대일 단방향

img.png

@Entity
public class Member {
	// ...
   
   @ManyToOne
   @JoinColumn(name = "TEAM_ID")
   private Team team;
}

다대일 양방향

img.png

@Entity
public class Team {

   // ...

   @OneToMany(mappedBy = "team")
   List<Member> members = new ArrayList<Member>();

일대다[1:N]

일대다 단방향

img.png

@Entity
public class Team {

		@Id @GenerateValue
		private Long id;

		@OneToMany
		@JoinColumn(name = "TEAM_ID")
		private List<Member> members = new ArrayList<>();
Member member = new Member("member");
em.persist(member); //insert member, 외래키 team_id에는 null이 일단 들어감

Team team = new Team("A");
team.getMembers().add(member);
em.persist(team); 
// team을 영속화하는 순간 team에 대한 insert 쿼리 발생
// member에 대한 insert도 발생하는데 일대다 단방향의 경우 team_id는 null로 insert
// 이후에 따로 member 테이블의 외래키(team_id)를 수정하는 update 쿼리도 나가야 함
// 양방향이었다면 insert할 때 한 번에 team_id도 넣어서 update 쿼리가 필요 없음

일대다 양방향

컬렉션에서 필드를 미리 초기화하는 이유

  1. NPE 방지
  2. 하이버네이트가 엔티티를 영속화 할 때 내부에서 컬렉션이 있으면 하이버네이트가 특별하게 조작한 컬렉션으로 변경한다. (컬렉션의 데이터가 추가 되었는지 등등을 인식할 수 있어야 하니까) 따라서 실제 참조가 변경될 수 있다. 그런데 개발자가 임의로 나중에 new ArrayList()로 초기화를 하게 되면 이 부분이 하이버네이트가 관리하는 컬렉션에서 개발자가 직접 만든 컬렉션으로 변경될 수 있다. 그러면 하이버네이트가 정상 동작하지 않는다. 이런 문제를 방지하기 위해 필드에서 빠르게 컬렉션을 초기화 하고, 해당 컬렉션을 바꾸는 행위를 막도록 코드를 작성하는 것이 좋다.

일대일 [1:1]

일대일: 주 테이블에 외래 키 단방향

@OneToOne
@JoinColumn(name = "LOCKED_ID")
private Locker locker

일대일: 주 테이블에 외래 키 양방향

@OnetoOne(mappedBy = "locker")
private Member member;

대상 테이블에 외래 키를 두고 일대일 단방향은 JPA에서 지원하지 않는다. 그리고 대상 테이블에 외래 키 양방향은 사실상 주 테이블 외래 키 양방향이랑 똑같다.

일대일 정리

img.png

다대다[N:M]

다대다 매핑의 한계

다대다 한계 극복

연결 테이블용 엔티티의 PK를 FK를 묶어서 사용할 수도 있지만 PK는 정말 비즈니스적으로 의미 없는 값을 사용하는 것이 좋기 때문에 따로 id를 만들어서 PK로 사용하는게 유지보수성이 좋다는 영한님 의견이 있음

N:M 관계는 1:N, N:1로

@JoinColumn