본문 바로가기
Spring/JPA

양방향 연관관계 주의점

by YellowCow 2023. 3. 15.

양방향 관계는 테이블에서 외래키를 이용하여 양쪽 테이블에 접근하는 것처럼

양쪽 Entity에 서로의 참조값을 갖고 있게 함으로써

양방향으로 Entity에 접근을 가능하게 한다

 

다만, 양쪽 Entity에 서로의 참조값을 갖고 있어서 주의할 점이 몇가지 있는데

 

아래와 같이 1:N의 관계를 가진 Entity를 예시로 들어서 설명하겠다

 

mappedBy가 위치한 곳은 읽기 전용이다

  • 연관관계의 주인인 곳에서만 참조값을 변경할 수 있다
  • 연관관계의 주인이 아닌 쪽의 값을 변경하더라도 읽기 전용이기 때문에 DB에 반영되지 않는다

 

양쪽에 값을 넣어주자

아래 코드를 살펴보자 

위 코드에서는 연관관계의 주인인 쪽에만 참조값을 저장하고 있다

 

여기서 한가지 문제가 있다.

 

객체지향적인 관점에서 봤을 때,

Employee에서는 Department를 조회할 수 있지만

Department에서는 Employee를 조회할 수 없다

 

물론, 위 코드에서는 em.flush와 em.clear()가 실행되어서

DB 반영 및 영속성 컨텍스트 초기화가 되었기 때문에
지연로딩을 이용하여 Member 정보를 가져올 수 있다.

 

- em.flush(): 영속성 컨텍스트의 내용을 DB에 반영

- em.clear(): 영속성 컨텍스트를 초기화

 

그런데, 개발자의 실수로 em.flush()와 em.clear()를 호출하지 않는다면 어떻게 될까?

Employee와 Department가 DB에 반영되지 않은 상태이므로

Department에서 Employee 정보를 조회할 수 없다

 

이를 위해 아래와 같이 Employee와 Department 양쪽에 참조값을 넣어줘야 한다

 

  • 연관관계 편의 메서드
    • 위 코드에서 department.getEployees().add(employee)와 같은 코드는 개발자의 실수가 발생할 수 있다(누락, 오타 등)
    • 개발자의 실수를 줄이기 위해 연관관계 편의 메서드를 사용한다
    • 1:N 관계에서 N쪽에 연관관계 편의 메서드를 추가한다(개발상황에 따라 다를 수 있음)
    • 위 코드 기준으로 Employee에 연관관계 편의 메서드를 추가한다
    • 연관관계 메서드 이름을 정할 때,
      자바 getter/setter 관례로 인해, set으로 시작하는 메서드 이름은 setter메서드와 혼동될 수 있으므로
      set으로 시작하는 이름은 지양해야 한다

연관관계 편의 메서드를 적용한 모습
메서드 한번으로 양쪽 Entity 모두에 참조값을 저장하므로 개발자의 실수를 방지할 수 있다

 

무한루프

  •  Lombok에서 toString 왠만하면 쓰지 마라
    • Entity안에 다른 Entity가 속성으로 있을 경우 연관관계에 있는 엔티티의 toString 메소드가 순환참조되어 무한호출됨

 

  • Contoller에서 Entity를 반환하지 마라
    • DTO로 변환해서 반환해야 함
    • Spring에서 객체를 반환할 경우, JSON으로 변환해서 반환하게 되어있음(Default)
    • Entity를 변경하면 API 스펙이 변경될 소지가 있음
    • Entity안에 다른 Entity가 속성으로 있을 경우 연관관계에 있는 엔티티가 순환참조되어 JSON 변환이 무한실행됨

 

단방향 연관관계로 설계를 완료해야 한다

앞에서 언급한 연관관계 편의 메서드, 연관관계의 주인 등 고민거리가 증가한다.

 

양방향 연관관계는 역방향 탐색(객체 그래프 탐색) 기능을 추가한 것 뿐,

단방향 연관관계로 먼저 설계하고, 필요 시 양방향 연관관계를 추가하면 된다.

(테이블에 영향을 주지 않기 때문)

 

양방향 연관관계 사용을 지양하도록 하자

댓글