ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 연관관계 매핑
    IT/JPA 2020. 12. 8. 21:25

    1. 연관관계

      1) 연관관계

       - 방향 : 단방향, 양방향 이 있다. ex) 회원->팀(단방향), 회원->팀 팀->회원(양방향)

       - 다중성 : 다대일(N:1) 일대다(1:N), 일대일(1:1), 다대다(N:M)

       - 연관관계의 주인 : 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다.

     

    2. 단방향 연관관계

      * 연관관계 중에선 다대일(N:1) 단방향 관계를 가장 먼저 이해해야 한다.

       - 회원과 팀이 있다.

       - 회원은 하나의 팀에만 소속될 수 있다.

       - 회원과 팀은 다대일 관계다.

      * 객체 연관관계

       - 회원 객체는 Member.team 필드로 팀 객체와 연관관계를 맺는다.

       - 회원 객체와 팀 객체는 단방향 관계다. Member.team 필드를 통해 팀을 알 수 있지만 팀은 회원을 알 수 없다.

     

      * 테이블 연관관계

       - 회원 테이블은 TEAM_ID 외래 키로 팀 테이블과 연관관계를 맺는다.

       - 회원 테이블과 팀 테이블은 양방향 관계이다. 회원 테이블의 TEAM_ID로 MEMBER JOIN TEAM,

          TEAM JOIN MEMBER 둘다 가능하다.

     

      * 객체 연관관계와 테이블 연관관계의 가장 큰 차이

       - 참조를 통한 연관관계는 언제나 단방향이다.

       - 객체간에 연관관계를 양방향으로 만들고 싶으면 반대쪽도 필드를 추가해서 참조를 보관해야 한다.

       - 결국 양방향 관계가 아니고 서로 다른 단방향 관계 2개이다.

     

      1) 객체 관계 매핑

    @Entity
    public class Member {
      ....
      ....
    
      @ManyToOne
      @JoinColumn(name="TEAM_ID)
      private Team team;
      ....
    }

       - 객체 연관관계 : 회원 객체의 Member.team 필드 사용.

       - 테이블 연관관계 : 회원 테이블의 MEMBER.TEAM_ID 외래키 컬럼 사용.

       - @ManyToOne : 다대일(N:1) 매핑정보에 사용.

       - @JoinColumn(name="TEAM_ID") : 외래키를 매핑할때 사용, name 속성에는 매핑할 외래키 이름을 지정한다.

       - @JoinColumn을 생략하면 외래키를 찾을 때 디폴트로 필드명+_+참조하는 테이블의 컬럼명 으로 찾는다.

             (team_TEAM_ID)

     

    3. 연관관계 사용

      1) 저장

    member1.setTeam(team1); //회원->팀 참조
    em.persist(member1); //저장

     

      2) 조회

       - 연관관계가 있는 엔티티를 조회하는 방법은 크게 2가지다.

       - 객체 그래프 탐색(객체 연관관계를 사용한 조회)

       - 객체지향 쿼리 사용(JPQL)

    //객체 그래프 탐색
    Member member = em.find(Member.class, "member1");
    Team team = member.getTeam(); //객체 그래프 탐색
    System.out.println(team.getName());
    
    //객체지향 쿼리 사용
    String jpql = "select m from Member m join m.team t where t.name=:teamName";
    List<Member> resultList = em.createQuery(jpql, Member.class)
    	.setParameter("teamName", "팀1")
        .getResultList();

     

      3) 수정

    //회원1에 새로운 팀2 설정
    Member member = em.find(Member.class, "member1");
    member.setTeam(team2);

     

      4) 연관관계 제거

    Member member1 = em.find(Member.class, "member1");
    member.setTeam(null); //연관관계 제거

     

      5) 연관된 엔티티 삭제

    //멤버1,멤버2 에 연결된 team을 모두 null로 하여 종속성 제거 후 remove
    member1.setTeam(null);
    member2.setTeam(null);
    em.remove(team);

     

    4. 양방향 연관관계

      1) 양방향 연관관계

       - 객체의 일대다 관계는 여러건과 연관을 맺을 수 있으므로 컬렉션을 사용해야 한다.

     

       - 데이터베이스 테이블은 하나의 외래키로 양방향 조회가 가능하다.

     

    @Entity
    public class Team {
    	...
        
        @OneToMany(mappedBy = "team")
        private List<Member> members = new ArrayList<Member>();
        
        ...
    
    }

       - 팀과 회원은 일대다 관계다. 따라서 List<Members> members 를 추가했다.

       - 일대다 관계를 매핑하기 위해 @OneToMany  매핑 정보를 사용했다.

       - mappedBy 속성은 양방향 매핑일 때 사용하는데, 반대쪽 매핑의 필드값을 이름으로 주면 된다.

     

      2) 일대다 컬렉션의 조회

    Team team = em.find(Team.class, "team1");
    List<Member> members = team.getMembers();

     

    5. 연관관계의 주인

      * 엄밀이 이야기하면 객체에는 양방향 연관관계가 없다. 서로 다른 단방향 연관관계 2개가 묶여서 양방향 처럼 보이게 할 뿐이다.

      1) 양방행 매핑의 규칙 : 연관관계의 주인

       - 양방향 연관관계 매핑 시 하나를 반드시 연관관계의 주인으로 정해야 한다.

       - 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고, 외래키를 관리(등록,수정,삭제) 할 수 있다.

       - 주인이 아닌 쪽은 mappedBy 속성을 사용하고, 읽기만 가능하다.

     

      2) 연관관계의 주인은 외래키가 있는 곳

       - 연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야 한다.

       - 외래키가 있는 엔티티를 주인으로 정하면 자기 테이블에 있는 외래 키를 관리하면 된다.

     

    6. 양방향 연관관계의 저장

      1) 양방향 연관관계의 저장

    Team team1 = new Team("team1", "팀1");
    em.persist(team1);
    
    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1); //연관관계 설정 member1->team1
    em.persist(member1);

     

    7. 양방향 연관관계의 주의점

      1) 연관관계의 주인이 아닌곳에만 값을 입력한 경우

    Member member1 = new Member("member1", "회원1");
    em.persist(member1);
    Member member2 = new Member("member2", "회원2");
    em.persist(member2);
    
    Team1 team1 = new Team("team1", "팀1");
    //주인이 아닌곳만 연관관계 설정
    team1.getMembers().add(member1);
    team1.getMembers().add(member2);
    em.persist(team1);

       - 데이터베이스 조회 결과는 다음과 같다.

       - 외래키 TEAM_ID에 null 값이 저장되어있는데, 연관관계의 주인이 아닌 곳에서 값을 저장했기 때문이다.

       - 연관관계의 주인이 아니면 값을 읽기만 가능하다.

     

    8. 순수한 객체까지 고려한 양방향 연관관계

      1) 순수한 객체까지 고려한 양방향 연관관계

       - 객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다.

       - 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있다.

       - 순수한 객체 관점에서는 회원->팀을 설정하면 팀->회원도 설정해야 한다.

    //양방향 관계
    
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);
    
    Member member1 = new Member("member1", "회원1");
    
    //양방향 연관관계 설정
    member1.setTeam(team1);    //member1->team1
    team1.getMembers().add(member1);	//team1->member1
    em.persist(member1);
    

       - 양쪽에 연관관계를 설정하여 순수한 객체 상태에서도 동작하며, 테이블의 외래 키도 정상 입력된다.

       - Member.team : 연관관계의 주인, 이 값으로 외래키를 관리한다.

       - Team.members : 연관관계의 주인이 아니다. 따라서 DB 저장 시에 사용되지 않는다.

     

      2) 연관관계 편의 메소드

       - 로직에 위와같이 호출하면, 실수로 둘중 하나만 호출해서 양방향이 깨질 수 있다.

       - Member 엔티티의 setTeam() 메소드 하나로 양방향 관계를 모두 설정하도록 작성한다.

    public class Member {
    	...
        private Team team;
        
        ....
        
        public void setTeam(Team team){
        	this.team = team;
            team.getMembers().add(this);	//양방향 추가
        }
    }

     

      3) 연관관계 편의 메소드 작성시 주의사항

    member1.setTeam(teamA); //1
    member1.setTeam(teamB); //2
    Member findMember = teamA.getMember();	//여전히 member1이 조회된다.

       - teamA -> member1 로의 관계가 남아있다. 따라서 teamB로 변경할 때 기존의 연관관계를 삭제하는 코드를 추가해야 한다.

    public void setTeam(Team team) {
    	//기존 팀과 관계를 제거
        if(this.team != null){
        	this.team.getMembers().remove(this);
        }
        this.team = team;
        team.getMembers().add(this);
    }

     

    'IT > JPA' 카테고리의 다른 글

    고급 주제와 성능 최적화  (0) 2021.02.28
    웹 애플리케이션과 영속성 관리  (0) 2021.01.24
    값 타입  (0) 2020.12.29
    고급 매핑  (0) 2020.12.19
    JPA 의 영속성 관리  (0) 2020.11.29
Designed by Tistory.