양방향 연관관계란 연관관계가 있는 두 엔티티 간에 양쪽 방향에서 모두 객체 탐색이 가능하도록 연관관계를 설정하는 것을 말함
회원-팀 엔티티가 있을 때, 회원 객체가 팀 필드를 가지고 있어 회원 객체로부터 팀을 조회할 수 있고, 팀 객체가 회원 필드를 가지고 있어 팀 객체로부터 회원을 조회할 수 있도록 함
실제로는 회원 -> 팀 참조와 팀 -> 회원 참조가 가능한 단방향 매핑이 모두 있는 것이지만 이를 양방향이라고 일컬음
양방향 매핑할 객체의 필드에 연관관계 어노테이션을 달고, (mappedBy = 반대편 객체 필드 명)을 달아줌.
mappedBy를 명시해 준 엔티티가 아닌 반대편 엔티티가 연관관계의 주인이 됨
연관관계의 주인인 엔티티만 연관관계에 있는 해당 객체를 등록, 수정, 삭제할 수 있음
주인이 아닌 엔티티는 객체 조회만 가능함. 등록, 수정, 삭제한다고 해도 DB에 반영되지 않음.
객체 지향으로 데이터를 관리하는 것과 관계형 DB로 데이터를 관리하는 것의 차이에서 기인
관계형 DB는 외래키로 연관관계를 맺게됨. 그리고 JOIN을 통해서 양쪽 테이블이 합쳐짐
외래키의 값이 바뀌면 해당 테이블의 값만 변경해주면 됨. 주키를 가지고 있는 테이블은 변화가 없음(주키를 가지고 있는 테이블은 외래키 테이블의 데이터를 함께 가지고 있지 않음)
그러나 객체는 양방향 관계를 맺었을 때 양쪽 모두 데이터를 가지고 있음. 회원은 팀 엔티티를 물고 있고, 팀 엔티티는 회원 리스트 엔티티를 물고 있음.
만약에 회원 1이 속한 팀1을 팀2로 바꾸려고 한다면, 회원 엔티티가 물고 있는 팀 필드의 값을 변경해주어야 할까? 아니면 팀1의 회원1을 삭제하고, 팀2에 회원1을 추가해야 할까?
외래 키가 있는 곳을 주인으로 정해라(김영한님 가이드)
우선 외래키가 있는 곳이 아닌 테이블을 연관관계의 주인으로 삼으면, 해당 엔티티를 바꿨을 때 다른 테이블에 업데이트 쿼리가 나감. JPA에 아주 빠삭하다면 모르겠지만 직관적이지 않고 복잠함
DB 입장에서 보면 외래키가 있는 곳이 무조건 다, 반대편이 1인 다대일 관계임(1대1도 있지만), N:1 관계에서 N쪽이 무조건 연관관계 주인이라고 생각하면 편함.
비즈니스적으로는 아무 의미가 없음, 비즈니스적으로 다르게 해야할까 고민하지 말고, 기능상 설계를 위해 무조건 이렇게 하는 것을 추천
성능 이슈도 있는데 이건 나중에 설명
연관관계의 주인이 아닌 쪽에서 데이터를 등록, 수정, 삭제해봤자 JPA는 이걸 처리하지 않는다.
그래서 주인이 아닌 쪽 필드는 가짜매핑이라고 봐야 한다. 조회만 가능하다.
그런데 가짜매핑한 쪽의 필드를 가지고 데이터를 조작하는 실수를 많이한다.
아래 코드에서 멤버의 팀 정보는 업데이트되지 않는다.
테스트 케이스 작성 시에도 JPA 없이 작성하기 때문에 위 불일치를 해결해야 함.
양쪽 모두 값을 추가하는 처리는 귀찮기도 하고, 깜빡할 수도 있기 때문에 아예 아래와 같이 연관관계 편의 메소드를 만들어서 해결하는 것을 추천한다.
또한 setter보다 changeTeam과 같은 메소드명으로 사용하는 것을 추천.(setter 지양)
toString() 메소드 라이브러리를 이용해 오버라이딩하거나, lombok을 이용해서 toString() 메소드가 자동 오버라이딩되어 있을 때 양방향 매핑의 각 엔티티가 계속 순환참조하면서 StackOverFlow가 발생할 수 있음.
JSON 생성 라이브러리를 이용해도 엔티티를 JSON으로 바꾸면서 위와 같이 쭉 객체를 참조해 뽑아버리면서 무한루프가 발생, 보통 컨트롤러에서 응답으로 엔티티 자체를 RETURN하면 JSON으로 바꾸면서 에러가 발생
하지만 컨트롤러에서 Entity 자체를 반환하지 말고, DTO로 바꿔서 반환하라는 원칙을 따르면 위 문제는 발생하지 않을 것임.
Entity 자체는 유지보수하며 변경될 여지가 있음. Entity 자체를 반환하면 나중에 API의 스펙 자체가 아예 바뀌어버림. DTO를 반환하면 Entity에 API가 종속적이지 않으므로 문제 발생 X
김영한님 강의 자반 ORM 표준 JPA 프로그래밍 참고하여 작성