ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA - 연관관계 매핑
    Spring/JPA 2022. 3. 8. 18:10
    728x90
    반응형

     

    연관관계 매핑

     

     

     

     

     

     객체를 테이블에 맞추어 모델링하면 연관관계를 만들 수 없지만 테이블은 외래 키로 조인을 사용하여 연관된 테이블을 찾을 수 있다. 이런 차이 때문에 객체를 테이블에 맞춘 모델링은 좋은 방법이 아니다.

     

     

     @Entity
     public class Member { 
     
         @Id @GeneratedValue
         private Long id;
         
         @Column(name = "USERNAME")
         private String name;
         
         @Column(name = "TEAM_ID")
         private Long teamId; 
         … 
     } 
         
     @Entity
     public class Team {
     
         @Id @GeneratedValue
         private Long id;
         private String name; 
         … 
     }

     

     위와 같이 테이블에 맞춘 모델링은 외래 키 식별자를 직접 다룰 수밖에 없다. 때문에 MEMBER 테이블에서 가져온 member 객체에서 Team의 정보를 알기 위해서는 다시 TEAM 테이블에서 정보를 가져와야 한다.

     

     //조회
     Member findMember = em.find(Member.class, member.getId());
     
     //연관관계가 없음
     Team findTeam = em.find(Team.class, team.getId());

     

     

     이러한 방식은 불편함만 있을 뿐만 아니라 성능에서도 문제가 된다. JPA에서는 이러한 문제점들을 해결하기 위해 내부적으로 객체지향 모델링을 제공한다.

     


     

    단방향 연관관계

     

     

    @Entity
    public class Member { 
        @Id @GeneratedValue
        private Long id;
        
        @Column(name = "USERNAME")
        private String name;
        private int age;
        
        // @Column(name = "TEAM_ID")
        // private Long teamId;
        @ManyToOne
        @JoinColumn(name = "TEAM_ID")
        private Team team;
        … 
    }

     

     위와 같이 연관관계 설정 @ManyToOne을 해주게 되면 객체 그래프 탐색이 가능해지기 때문에 조회, 수정이 객체지향적으로 처리할 수 있게 된다. 

     

    //조회
    Member findMember = em.find(Member.class, member.getId()); 
    
    //참조를 사용해서 연관관계 조회
    Team findTeam = findMember.getTeam();
    
    // 새로운 팀B
    Team teamB = new Team();
    teamB.setName("TeamB");
    em.persist(teamB);
    
    // 회원1에 새로운 팀B 설정
    member.setTeam(teamB);

     

     

    양방향 연관관계

     

     단방향 매핑은 Member 엔티티에 @ManyToOne의 연관관계만 설정했지만 양방향 매핑은 Team 엔티티에 @OneToMany 설정까지 해주는 방법이다. 단방향 매핑은 Member에서만 Team을 참조할 수 있었지만 양방향 매핑은 Team에서 Member로 참조도 할 수 있게 된다. 즉 양쪽 모두 서로를 참조할 수 있게 해 준다.

     

     

     

     


     

    객체의 양방향 연관관계와 테이블의 연관관계 차이

     

    • 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개이다.
    • 즉, 객체를 양방향으로 참조하기 위해서는 단방향 연관관계를 2개 만들어야 한다.
    • 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리할 수 있다.

     

     

     


     

    양방향 매핑 규칙

     

    • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 한다.
    • 연관관계의 주인만이 외래 키를 관리(등록, 수정)를 할 수 있다.
    • 주인이 아닌 쪽은 읽기만 가능하다.
    • 주인은 mappedBy 속성 사용 X
    • 주인이 아니면 mappedBy 속성으로 주인 지정한다.
    • 외래 키가 있는 곳이 주인이다.
    • 연관관계의 주인에게 값을 입력해야 한다.
    • 단방향 매핑을 잘하고 양방향은 필요할 때 추가해도 된다.

     

    @Entity
    public class Member { 
    
        @Id @GeneratedValue
        private Long id;
        
        @Column(name = "USERNAME")
        private String name;
        
        private int age;
        
        @ManyToOne
        @JoinColumn(name = "TEAM_ID")
        private Team team;
        …
    }
    
    @Entity
    public class Team {
    
        @Id @GeneratedValue
        private Long id;
        
        private String name;
        
        @OneToMany(mappedBy = "team")
        List<Member> members = new ArrayList<Member>();
        … 
    }

     

    주의 사항

     

    • 연관관계 주인에 값을 입력해야 한다
    • 아래와 같이 주인이 아닌 곳에 값을 입력하면 값이 DB에 값이 안 들어간 것을 볼 수 있다.

     

    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
    
    Member member = new Member();
    member.setName("member1");
    
    //주인이 아닌 곳에 데이터를 넣어도 DB에는 저장이 안된다.
    team.getMembers().add(member);
    em.persist(member);

     

     

    • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하는 것이 좋다
    • 연관관계 편의 메서드를 이용하여 양쪽에 값을 설정
        public void addOrderItem(OrderItem orderItem) {
            orderItems.add(orderItem);
            orderItem.setOrder(this);
        }
    • 양방향 매핑 시에 무한 루프를 조심해야 한다.
              ex) toString 오버 라이딩 시 발생

     

    참고

    단방향 매핑만으로도 이미 연관관계 매핑은 완료된 상태이다.
    양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것뿐이므로
    단반향 매핑을 잘하고 양방향은 필요할 때 추가하는 것이 좋다
    (추가 시 테이블에 영향을 주지 않기 때문)

     

     

    728x90
    반응형

    댓글

Designed by Tistory.