ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA (4) - 연관 관계 매핑 (양방향,단방향)
    카테고리 없음 2024. 1. 11. 12:47

     

     

    > 기본 용어 방향 : 양방향, 단방향 (객체에만 존재 참조가 양쪽에 있는지 한쪽에만 있는지)

    > 다중성 : 다대일,일대다,일대일,다대다 

    > 연관관계의 주인 : 객체 양방향 연관관계에서 주인이 필요하다 (외래키 관리 -> 변경시 DB에 반영될 객체)

     

    1. 연관관계가 필요한 이유 

     

    엔티티를 테이블에 맞추어 설계시 아래와 같음

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

     

    - 참조 대신 외래키를 직접 저장해서 사용함

     

    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
    
    Member member = new Member();
    member.setName("member1");
    member.setTramId(team.getId);
    em.persist(member);
    
    //기본 시퀀스 전략이라 다른테이블이지만 1씩증가함..! (동일 시퀀스 공유)
    //다르게 하고 싶으면 시퀀스 생성해서 각각하면될듯
    
    
    //조회
    Member findMember = em.find(Member.class,member.getId());
    
    Team findTeam = em.find(Team.class,team.getId());
    
    //member가 소속된 팀을 찾고싶은데 연관관계 딱히 없어서 가지고 있는 식별자로 따로 조회해야함

     

    > 객체를 테이블에 맞추면 협력관계 (그래프 탐색)를 만들 수 없다.

     

    2. 단방향 연관관계

     

    출처: JPA기본(김영한) - 인프런

     

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

     

    - 여러 멤버가 한개의 팀에 소속됨을 표현 

    - JoinColumn은 실제 테이블에 외래키 저장할 칼럼 설정하는 것 (TEAM_ID에 TEAM의 기본키 저장)

    - 기본키도 저장하고, 연관관계도 맺고! 

     

    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
    
    Member member = new Member();
    member.setName("member1");
    member.setTramId(team);
    em.persist(member);
    
    Member findMember = em.find(Member.class, member.getId));
    
    Team findTeam = findMember.getTeam();
    
    //** 
    //그냥 이런식으로 작성하면, 1차캐쉬에서 가져옴 따라서 쿼리 보고싶으면
    //flush(); clear()날리자

     

     

    3. 양방향 연관관계와 연관관계의 주인

     

    - 테이블의 연관관계는 기본적으로 fk하나로 join을 통해 둘다 식별가능

    - 객체는 참조를 통해 식별하므로, 양방향을 위해선 (사실은 단방향 두번) 참조를 알고 있어야한다.

    출처: JPA기본(김영한) - 인프런

    @Entity
    public class Member{
    
    	@Id @GeneratedValue
        private Long id;
        
        @Column(name="USERNAME")
        private String name;
        
       	@ManyToOne
        @JoinColumn(name = "TEAM_ID")
        private Team teamId;
    
    }
    
    @Entity 
    public class Team{
    
    	@Id @generatedValue
        private Long id;
        
        private String name;
        
        @OneToMany(mappedBy = "team")
        List<Member> members = new ArrayList<Member>();
    
    }
    Team findTeam = em.find(Team.class, team.getId());
    
    //Member의 Team fk와 Team의 pk join해서 Team 생성할때 쫙 가져옴

     

    3.1 연관관계의 주인 mappedBy

     

    > fk를 관리 -> 영속성 컨텍스트에서 변화를 감지하고 DB에 반영하는 쪽 정하는 것을 의미

    > fk는 무조건 Many쪽에 생성됨! (테이블이 원래 그러하다) 또한 외래 키 하나로 두 테이블의 연관관계를 관리 

    > 주인으로 지정된 참조가 변화하는 것은 감지하고, DB에 반영

    > 종속된 것이 변화하면 DB반영 안함 (어플리케이션 레벨에서만 반영됨)

     

    * Team으로 예를 들면 Team을 DB에서 find할때 Member List를 가지고 오는데 어플리케이션에서 List의 값을 변경해도 

      DB에 반영안됨 그냥 읽기전용 

     

    * 만약 이런 개념이 없다면 DB는 Team에 변화를 DB에 반영할지, Member에 변화를 반영할지 알 수 없음! 

      -> 만약 Member에 setTeam은 없고 Team에 add만 있다면? 이런 불일치를 막기 위해 한쪽에만 위임 

     

    3.2 양방향 매핑 규칙

     

    > 객체 중 하나를 연관관계의 주인으로 지정 (외래키 관리)

    > 주인이 아닌쪽은 읽기만 가능 

     

    > 그러면 양방향에서 어디를 주인으로 지정해야하는가? :

         - 테이블 기준으로 fk가 있는 쪽을 주인으로 두자! 

         - 테이블은 무조건 다쪽에 fk가 생성 따라서 위 상황이면 Member를 주인으로 두자 

         - Team을 주인으로 둘 수도 있음! -> 하지만, 어차피 이런식으로 맵핑해서 테이블 생성하더라도 항상 Many쪽에 fk생성됨 

         - 관계 파악이 쫌 힘들 수 있고, 또한 Team을 수정했는데 Member도 같이 수정되는 이상현상 겪을 수도 있다.                                                                                      

     

    3.3 양방향 매핑 시 자주하는 실수 

     

    - 주인 아닌 쪽에 업데이트 

    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 setTeam(Team team) {
            this.team = team;
            this.team.getMembers().add(this);
        }
    
    //사실은 team을 set하면, 기본 team에서 member제거하고, 해야하는데 이정만해도 충분할 수도 있다함! 
    //어차피 update됨 team은 그냥 읽기만하고

    -> 양방향 매핑시 무한 루프 매우 주의해야함 

     

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

     

    toString -> Team.toString() -> members.toString-> member.toStirng()..반복

    JSON문제는 컨트롤러에서 엔티티 반환하는 것을 금지!! toString()은 적절하게 알아서 잘 빼고쓰던지해라 

    아마 한 쪽만 빼주면 무한루프 안할 것 같긴함 

     

    4. 정리

     

    - 일단 단방향 매핑으로도 연관관계는 충분하다 

    - 양방향 매핑은 반대 조회(그래프 탐색)이 꼭 필요할 때만 동적으로 추가하자 (어차피 테이블에 영향 없음)

    - JPQL에서 특히 역방향 탐색할 일이 많다. 

    - 또한 외래키 관리 기준으로 잡아야함 비지니스상 우위를 주인으로 정하면 안됨 

Designed by Tistory.