Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

양방향 연관관계와 연관관계 주인 - 주의점 본문

인프런/[인프런] 자바ORM 표준 JPA 프로그래밍

양방향 연관관계와 연관관계 주인 - 주의점

Seung__ 2021. 8. 10. 21:06

1. 양방향 매핑 시 가장 빈번한 실수


  • 연관관계의 주인에 값을 입력하지 않은 실수
  • 주인이 아닌 방향 만 연관관계를 설정하면..?
    - 주인이 아닌 곳에서 만 값을 입력하면 SQL 나갔다가 rollback됨 ㅋㅋ
    Member member = new Member();
    member.setUserName("member1");
    em.persist(member);
    
    Team team = new Team();
    team.setName("TeamA");
    //주인이 아닌 쪽에서 연관관계 설정
    team.getMemberList().add(member);
    em.persist(team); //team이 영속상태 되면 id값이 들어감.​
  • Team은 연관관계 주인이 아니다. 
    • mappedBy는 읽기 전용이고,  JPA에 update나 insert 시 mapppedBy는 무시함.
    • 즉, mappedBy가 붙은 Team의 MemberList에 member를 추가해도 update, insert 대상이 아님.
      @OneToMany(mappedBy = "team") //일대다 매핑에서 나는 뭐랑 연결되어있지? Member 클래스의 Team 필드명.
      private List<Member> memberList = new ArrayList<>();​
  • 그래서 연관관계 주인 쪽에서 값을 입력해 줘야 한다.
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team); //team이 영속상태 되면 id값이 들어감.
    
    Member member = new Member();
    member.setUserName("member1");
    member.setTeam(team);
    em.persist(member);​
    캬~
  • 양쪽으로 넣어도됨..? -->예쓰
    - 하지만 주인이 아닌 쪽에서 입력해도 읽기 전용이라 JPA에서 안씀 ㅋㅋㅋ
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team); //team이 영속상태 되면 id값이 들어감.
    
    Member member = new Member();
    member.setUserName("member1");
    member.setTeam(team);
    em.persist(member);
    
    //어차피 읽기 전용이라 JPA에서 안씀.
    team.getMemberList().add(member);
    
    
    em.flush();
    em.clear();​

잘 들어간다

 

 

 

2. 결국 양방향 매핑은 양 쪽 다 값을 넣어줘야 함.


  • 객체지향 적으로 생각해 보면 앙쪽에 넣는 것이 맞음.
  • 만약 Team에서 memberList값을 입력을 하지 않은 상태에서
    team.getMemberList()를 호출해 보자.
         Team team = new Team();
            team.setName("TeamA");
            em.persist(team); //team이 영속상태 되면 id값이 들어감.

            Member member = new Member();
            member.setUserName("member1");
            member.setTeam(team);
            em.persist(member);

            //어차피 읽기 전용이라 JPA에서 안씀.
            //team.getMemberList().add(member);


            em.flush();
            em.clear();


            //member의 소속 팀 찾기....흠..
            Member findMember = em.find(Member.class, member.getId());

            List<Member> memberList = findMember.getTeam().getMemberList();
            System.out.println("==================");
            for (Member m : memberList) {

                System.out.println("member1 = " + m.getUserName());

            }
            System.out.println("==================");

            tx.commit();
  • 값은 호출이 된다.
member1 = member1
  • 하지만 log를 보면 select 쿼리가 날라간다.
  • FK가 매핑이 완료되었기 때문에, Team과 연관된 Member를 쿼리를 날려서 가져오게 된다.
Hibernate: 
    select
        memberlist0_.TEAM_ID as team_id3_0_0_,
        memberlist0_.MEMBER_ID as member_i1_0_0_,
        memberlist0_.MEMBER_ID as member_i1_0_1_,
        memberlist0_.TEAM_ID as team_id3_0_1_,
        memberlist0_.USERNAME as username2_0_1_ 
    from
        Member memberlist0_ 
    where
        memberlist0_.TEAM_ID=?
  • 이렇게 양방향으로 안넣어 주게되면 두 가지 문제가 발생한다.
    - 1차 캐시에서 조회
    - Test case작성
  • 지금 예제처첨 flush, clear하면 문제는 없지만. 안하면 지금과 다르게 동작한다.
    - team은 영속상태이기 때문에 findTeam은 1차 캐시에서 가져온다.
    - 이 때 1차 캐시에 있는 team은 처음 영속성 컨텍스트에 올라간 상태 그대로이다.
    - 즉 team의 memberList에는 member정보가 없다.
    try{

            Team team = new Team();
            team.setName("TeamA");
            em.persist(team); //team이 영속상태 되면 id값이 들어감.

            Member member = new Member();
            member.setUserName("member1");
            member.setTeam(team);
            em.persist(member);


            //1차 캐시에 들어간 상태 그대로임..연관관계 세팅 안됨.
            Team findTeam = em.find(Team.class, team.getId());
            List<Member> memberList = findTeam.getMemberList();
            System.out.println("==================");
            for (Member m : memberList) {
                System.out.println("member1 = " + m.getUserName());
            }
            System.out.println("==================");

            tx.commit()
  }
  • member값이 없기 때문에 출력할 값이 없다.
==================
==================
  • 다른 문제점은 Test case 작성 시 JPA없이 돌아가도록 짜야하는데, 
    양쪽으로 안넣어주면 문제 발생.
  • 정리!!! 양쪽에 다! 세팅하자.

 

3. 정리


  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
  • 연관관계 편의 메서드를 생성하자 
      public void setTeam(Team team) {
            this.team = team;
            
            team.getMemberList().add(this);
        }​
  • 양방향 매핑시 무한 루프를 조심하자(순환참조 등)
    - toString(), lombok, JSON 생성 라이브러리 사용 시 자주 발생.
    - 엔티티를 컨트롤러에 반환하지마 ㅋㅋㅋ
  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료!!
    - 단방향 매핑으로 설계를 끝내!!
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색)기능이 추가된 것 뿐!!!!!!
  • JPQL에서 역방향으로 탐색할 일이 많음
  • 단방향 매핑을 잘~~~하고 양방향은 필요할 때 추가해!
    어차피 테이블에 영향 없어!!
  • 연관관계 주인은 외래키 위치를 기준으로!

 

4. GitHub : 210810 Entity Relationship2


 

GitHub - bsh6463/JPA

Contribute to bsh6463/JPA development by creating an account on GitHub.

github.com

 

Comments