ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 연관관계 매핑 - 단방향, 양방향 연관관계
    JPA/JPA 기본 2023. 7. 11. 18:03

    연관관계가 필요한 이유 : 하나의 객체가 단독으로 존재하는 경우는 드물기 때문에 객체(또는 엔티티) 끼리의 연관관계를 통해 데이터를 주고받아야 한다.

     

     

    단방향 매핑

    Member와 Team을 보면 연관관계 매핑이 되어있는데, 양방향이 아니라 단방향으로 멤버가 Team을 가리키고 있다. 그리고 이 경우에는 일대 다 연관관계 상황이며 Team 하나에 Member가 여러명 들어갈 수 있다.

     

    Member 엔티티

    @Entity
    public class Member {
        @Id @GeneratedValue
        @Column(name = "MEMBER_ID")
        private Long id;
        
        @ManyToOne
        @JoinColumn(name = "TEAM_ID")
        private Team team;
    }

    Team하나에 Member가 여러명 들어갈 수 있으므로 Member가 다수이고 Team이 하나이다.

    Member에서 Team이라는 엔티티에 연관관계 매핑을 할 때에는 @ManyToOne으로 매핑한다. 매핑을 하는 방법은 어떠한 관계인지 (@ManyToOne 애노테이션), 매피하는 컬럼이 어떤 것인지(@JoinColumn 애노테이션)을 넣어주면 된다.

     

     

     

    연관관계 매핑을 하지 않았을 경우

     // 팀 저장
     Team team = new Team();
     team.setName("TeamA");
     em.persist(team);
     //회원 저장
     Member member = new Member();
     member.setName("member1");
     member.setTeamId(team.getId());
     em.persist(member);

    연관관계 매핑을 하지 않았을 때에는 회원 저장을 할 때 team 객체를 만들고 team의 id를 가져와서 member의 PK를 지정해주고 persist 하는 등으로 복잡한 관계로 진행이 된다.

     

     

     

    연관관계 매핑 사용 시

    //팀 저장
     Team team = new Team();
     team.setName("TeamA");
     em.persist(team);
     //회원 저장
     Member member = new Member();
     member.setName("member1");
     member.setTeam(team); //단방향 연관관계 설정, 참조 저장
     em.persist(member);

    연관관계 매핑을 하면 Team의 PK를 member에 넣어주는것이 아니라 Team을 member에 setTeam으로 넣어줄 수 있다.

    외래키를 통해서 객체지향스럽게 연관관계 매핑을 해줄 수 있다.

     

     

     

    양방향 매핑

    사진 : 인프런 김영한님 강의

    이번에는 양방향으로 매핑을 하는데, 테이블은 단방향일 때와 차이가 없다. 테이블 연관관계는 외래키 하나로 연관관계가 이루어지기 때문이다. 하지만 객체는 단방향과의 차이점이 있어야한다. Team 객체에 members라는 List를 넣어줘야지 객체의 참조가 연관관계를 가진다.

     

    사진 : 인프런 김영한님 강의

    사실 양방향 연관관계는 사실 양방향 관계가 아니라 서로 다른 단방향 연관관계가 2개 있고, 서로를 매핑하는 구조이다.

     

    객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야한다.

     

     

     

    연관관계 주인

    양방향 매핑에는 하나 의문점이 있다.

    사진 : 인프런 김영한님 강의

    Team_ID의 참조가 2개기 때문에 Member의 Team 값을 바꿨을 때 외래키가 update되는지, Team에 있는 members 값이 바뀌었을 때 외래키가 update 되는지를 알아야한다.

     

     

     

    양방향 매핑 규칙

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

     

     

    Member 엔티티에서의 연관관계

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    Team 엔티티에서의 연관관계

    @OneToMany(mappedBy = "team") // team이랑 매핑되었다는 뜻
    private List<Member> members = new ArrayList<>();
    // new ArrayList로 초기화 해두는게 관례(NullPointException이 안 뜸)

    Member와 Team 엔티티에서의 연관관계인데, Member가 연관관계 주인이므로 Team 엔티티에서 (mappedBy = "team)으로 Member 엔티티의 team을 주인으로 지정해준 것이다.

     

     

     

    예를 들어서 team.getmembers()로 members를 조회할 수 있지만 members에 값을 넣는 것은 불가능하다. 따라서 값을 넣어줘도 db에는 아무런 데이터가 들어가지 않고, Member 엔티티에 있는 team에 값을 넣어줘야한다.

     

     

     

    누구를 주인으로 설정하는지?

    • 외래 키가 있는 곳을 주인으로 설정하기
    • 일대 다 관계에서 다수의 쪽을 주인으로 하고, 하나의 쪽을 mappedBy로 설정해주는 것이 좋다.

     

    사진 : 인프런 김영한님 강의

    만약에 Team 엔티티에 있는 List members를 주인으로 설정하면 Team의 members를 바꾸면 다른 테이블(MEMBER 테이블)의 업데이트 쿼리가 나가는 것이다. Team의 값을 바꿨는데 member 테이블의 쿼리가 나가기 때문에 헷갈릴수 있거나 약간의 성능문제가 있을 수 있다. 그렇기 때문에 외래 키가 있는 곳을 주인으로 설정하는 것이 좋다.

     

     

     

    양방향 매핑시 주의점

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

    Member member = new Member();
    member.setUsername("member1");
    em.persist(member);
    
    Team team = new Team();
    team.setName("TeamA");
    team.getMembers().add(member);
    em.persist(team);
    
    em.flush();
    em.clear();
    
    tx.commit();

    위의 코드는 연관관계 매핑이 제대로 이루어지지 않는다. team.getMembers().add(member)로 members의 값을 넣어줬는데, 연관관계의 주인이 아니기 때문에 db에 반영되지 않는다.

     

    Team team = new Team();
    team.setName("TeamA");
    //team.getMembers().add(member);
    em.persist(team);
    
    Member member = new Member();
    member.setUsername("member1");
    member.setTeam(team):
    em.persist(member);
    
    em.flush();
    em.clear();
    
    tx.commit();

    위의 코드처럼 members에 값을 넣어주는 것이 아니라 team에 값을 넣어줘야한다. 둘 다 넣어줘도 된다. 왜냐하면 members는 읽기전용이기 때문에 JPA에서 쿼리를 날리지 않기 때문이다.

     

     

    객체 지향적으로 생각해보면 항상 양쪽 다 값을 입력하는 것이 최선의 방법이다. members에서도 add(member)로 통해서 값을 넣어주는 것이 좋다.

     

     

    한 가지 더 주의할 점은 양방향으로 매핑되어있기 때문에 무한 루프를 조심해야한다.

    예) toString(), lombok, JSON 생성 라이브러리

     

    lombok등의 toString 쓰지말기, 컨트롤러에서 엔티티를 반환하지말고 Dto로 바꿔서 반환하기

     

     

     

    정리

    - 처음 설계를 할 때에는 단방향 매핑만으로 연관관계 매핑을 종료해야한다. 처음부터 양방향 매핑을 하는 것은 좋지않다.

    이후에 JPQL에서 역방향으로 탐색할 일이 많은데 그때 양방향은 필요할 때 추가하면 된다.

     

    양방향 매핑은 엔티티에 코드 몇 줄만 추가하면 되기때문에 단방향 매핑을 잘 하고, 양방향은 테이블에 영향을 주지 않기 때문에 필요할 때 추가해도 된다.

     

     

    - 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안되며 외래 키의 위치를 기준으로 정해야한다.

    'JPA > JPA 기본' 카테고리의 다른 글

    즉시로딩과 지연로딩  (0) 2023.07.11
    프록시  (0) 2023.07.11
    상속관계 매핑  (0) 2023.07.11
    다양한 연관관계 매핑  (0) 2023.07.11
    영속성 컨텍스트  (0) 2023.07.11
Designed by Tistory.