양방향 연관관계

그저늅늅·2021년 11월 30일
0

JPA

목록 보기
3/6

https://www.inflearn.com/course/ORM-JPA-Basic/dashboard

Inflearn의 김영한 강사님 강의 [자바 ORM 표준 JPA 프로그래밍 - 기본편] 을 보고 정리했습니다.


시나리오

  • 회원과 팀이 있다.
  • 회원은 하나의 팀에 소속된다.
  • 회원과 팀은 다대일 관계다.

양방향 연관관계와 연관관계 주인

  • 단방향 연관관계 상태에서는
    • Member에서 Team을 얻을 수 있지만
    • Team에서 Member를 얻을 수는 없다.
  • MemberTeam 양쪽으로 참조하여 서로를 얻을 수 있다.

양방향 연관관계를 맺을 때

  • Table의 연관관계는 변하지 않는다.
    • Table은 외래키를 통해 join하면 된다.
    • 즉, 외래키 하나로 양방향 연관관계가 맺어져 있는 상태.
  • 객체의 경우
    • 단방향 연관관계일때 MemberTeam을 갖고 있지만 TeamMember를 갖고있지 않다.
    • 즉, 객체는 MemberTeam 모두 연관관계를 설정해야 한다.
public class Team {
    ...
    @OneToMany(mappedBy = "team") // mappedBy : team에 의해서 관리가 된다. 읽기만 가능. 값을 넣어도 아무일도 벌어지지 않는다.
    private List<Member> members = new ArrayList<>(); // ArrayList로 초기화 하는것이 관계, add할때 nullpoint가 안뜬다.
}
@Entity
public class Member {
    ...
    @ManyToOne // 다대일 관계에서 멤버가 '다' 팀이 '일'
    @JoinColumn(name = "TEAM_ID") // Team 객체와 TEAM_ID 외래키와 연관관계 매핑.
    private Team team;
}
  • 멤버가 속한 팀의 모든 멤버를 조회
  • Member findMember = em.find(Member.class, member.getId()); List<Member> members = findMember.getTeam().getMembers(); // 멤버에서 팀으로, 팀에서 멤버로 접근이 된다.

연관관계의 주인과 mappedBy

객체와 테이블간에 연관관계를 맺는 차이

  • 객체 연관관계 = 2개
    • 회원 -> 팀 (단방향)
    • 팀 -> 회원 (단방향)
    • 객체의 양방향 관계는 사실 서로 다른 단방향 관계 2개
  • 테이블 연관관계 = 1개
    • 회원 <-> 팀 (양방향)
    • 외래키 하나로 두 테이블의 양방향 연관관계를 관리한다.

Member 테이블의 외래키는 누구와 연결해야 하는가.

  • Member의 팀을 바꾸고 싶을 때, MemberTeam을 바꿔야 할지 Teammembers를 바꿔야 할지 정해야한다.
  • 외래키 업데이트의 기준은 언제로 해야하는가? : Member 혹은 Team 둘 중 하나로 외래키를 관리해야 한다.

연관관계의 주인(Owner)

양방향 매핑 규칙

  • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
  • 주인이 아닌 쪽은 읽기만 가능
  • 주인은 mappedBy 속성 사용 X
  • 주인이 아니면 mappedBy 속성으로 주인 지정.

누구를 주인으로?

  • 외래 키가 있는 곳을 주인으로 정한다.
  • DB Table의 '다(Many)' 쪽이 주인이 된다.

양방향 매핑시 가장 많이 하는 실수.

  1. 연관관계의 주인에 값을 입력하지 않음.

    • DB의 Member테이블에 Team이 저장되지 않는다.
    • Team은 연관관계의 주인이 아니기 때문에 변경이 되지 않음.
    • team.getMembers().add(member); 대신 member.setTeam(team); 을 해야 DB에 정상적으로 값이 들어간다.
    • 순수한 객체 관계를 고려해서 가급적이면 TeamMember모두 값을 설정하는게 좋다.
    • Team team = new Team(); em.persist(team); Member member = new Member(); member.setTeam(team); em.persist(member); Team findTeam = em.find(Team.class, team.getId()); // 영속성 컨텍스트 1차 캐시에 있는 내용을 꺼내온다 List<member> members = findTeam.getMembers(); //1차 캐시의 team에는 멤버가 없는상태로 들어와 있기 때문에 아무것도 가져오지 못한다.
  2. Member member = new Member(); em.persist(member); Team team = new Team(); team.getMembers().add(member); // 역방향(주인이 아닌 방향)만 연관관계 설정 em.persist(team);

  3. 연관관계 편의 메소드를 생성한다. (실수를 줄이기 위해)

  4. @Entity public class Member { ... public void setTeam(Team team) { this.team = team; team.getMembers().add(this); // member에 team을 설정하면서 동시에 team에서도 member를 추가한다. } // set대신 로직을 쓰는것을 권장. public void changeTeam(Team team) { ... } }

  5. 양방향 매핑시에 무한 루프를 조심한다.

    @Entity
    public class Member {
        ...
        @Override
        public String toString() {
            return ... + team + ... // team의 toString()이 호출된다.
        }
    }
    @Entity
    public class Team {
        ...
        @Override
        public String toString() {
            return ... + members + ... // members의 컬렉션 하나하나의 toString() 들을 호출한다.
        }
    }
    • membertoString()을 호출하면 teamtoString()이 호출되고 teamtoString()에서 membertoString()을 호출하는 무한 루프가 생긴다.
    • lombok에서 toString() 최대한 사용하지 않는다.
    • Controller에서는 Entity를 절대 반환하지 않는다. DTO로 변환해서 반환하는것을 추천한다.
  6. ex) toString(), lombok, JSON 생성 라이브러리.


정리

  1. 단방향 매핑만으로 이미 연관관계 매핑 완료
    • JPA 모델링 할 때, 처음에 단방향 매핑만으로 설계를 완료한다.
  2. 양방향 매핑은 반대 방향으로 조회 기능이 추가된 것 뿐이다.
  3. JPQL에서 역방향으로 탐색할 일이 많다.
  4. 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 된다. (테이블에 영향을 주지 않는다.)
profile
양현석

0개의 댓글