Free Lines Arrow
본문 바로가기
DataBase/JPA

[JPA] 연관관계 매핑기초3: 양방향 사용시 주의점

by skahn1215 2021. 8. 2.
728x90
반응형

양방향 연관관계의 주의점

  • 값 조회 안되는 경우
  • 무한루프에 빠지는 경우

 

 

값조회가 안되는 경우

 

예제설명

양방향 연관관계를 쓸때는 양쪽에 값을 다 넣어 줘야 된다.

앞서예제에서 Member 에도 team이 있고 Team 에도 Members 가 있었다.

 

 

여기서 Member에만 team을 넣어 주고 Team에다가 Member를 넣어주지 않으며 어떻게 될까?

예제로 살펴보자.

 

 

 

예제1 Member 에만 Team을 넣어 주었을때

  • 1. Team을 생성한다.
  • 2. Team을 영속성 컨텍스트에 넣는다.
  • 3. 새로운 회원을 만든다.
  • 4. 회원을 영속성 컨텍스트에 넣는다.
  • 5. 주석처리...
  • 6. flush 를 이용하여 DB에 저장한다.
  • 7. 영속성컨텍스트에 있는 모든 정보를 날린다.
  • 8. 영속성컨텍스트가 비어있기 때문에 DB에서 가져온다.
  • 9. Team 에서 members를 가져온다
  • 10. 영속성컨텍스트에 내용이 없기 때문에 조회 할때 DB 에서 조회한다. 
  • 여기 까지는 문제가 없다 문제는 다음예제에서 설명한다.
1. Team team = new Team();
   team.setName("TeamA");
2. em.persist(team);

// 수정코드
3. Member member = new Member();
   member.setUsername("member1");
   member.setTeam(team);
4. em.persist(member);

// Member에만 setTeam으로 팀을 넣어주었다.
//5. team.getMembers().add(member);

// select 쿼리를 보고 싶을때
6. em.flush(); // 영속성에있는 데이터를 DB 에 저장
7. em.clear(); // 영속성 컨텍스트 모두 클리어


// 조회시 수정코드
// 쿼리문 한번 발생
8. Member findMember = em.find(Member.class, member.getId());
9. List<Member> members = findMember.getTeam().getMembers();

10. for(Member m : members)
   {
       // 조회시 쿼리문 발생한다.
       System.out.println("m: " + m.getUsername());
   }

 

그림으로 설명

 

 

 

 

 

 

예제2 Member 에만 Team을 넣어 주었을때 flush 와 clear를 안해주었을때.

  • 1. Team을 생성한다.
  • 2. Team을 영속성 컨텍스트에 넣는다.
  • 3. 새로운 회원을 만든다.
  • 4. 회원을 영속성 컨텍스트에 넣는다.
  • 5. 주석처리
  • 6. 주석처리
  • 7. 주석처리
  • 8. 영속성컨텍스트가 비어있기 때문에 DB에서 가져온다.
  • 9. Team 에서 members를 가져온다
  • 10. 영속성컨텍스트에 내용이 없기 때문에 조회 할때 DB 에서 조회한다. 

조회 실패!!!!!!!!

이유는 다음과 같다.

  • 단순히 여기서는 영속성컨텍스트에만 데이터들이 있다.
  • 그렇기 때문에 실제로 Team 클래스의 Members 컬렉션에는 Member가 들어가 있지 않다.
1. Team team = new Team();
   team.setName("TeamA");
2. em.persist(team);

// 수정코드
3. Member member = new Member();
   member.setUsername("member1");
   member.setTeam(team);
4. em.persist(member);

// Member에만 setTeam으로 팀을 넣어주었다.
//5. team.getMembers().add(member);

// select 쿼리를 보고 싶을때
//6. em.flush(); // 영속성에있는 데이터를 DB 에 저장
//7. em.clear(); // 영속성 컨텍스트 모두 클리어


// 조회시 수정코드
// 쿼리문 한번 발생
8. Member findMember = em.find(Member.class, member.getId());
9. List<Member> members = findMember.getTeam().getMembers();

10. for(Member m : members)
   {
       // 조회시 쿼리문 발생한다.
       System.out.println("m: " + m.getUsername());
   }

 

그림으로 설명

당연 멤버에 team을 저장만 했고

team 에는 Member를 저장하지 않았기 때문에 값을 찾아오지 못한다.

 

 

 

 

예제3 Member 와 Team 양방향에는 둘다 값을 넣어주자!!

  • 1. Team을 생성한다.
  • 2. Team을 영속성 컨텍스트에 넣는다.
  • 3. 새로운 회원을 만든다.
  • 4. 회원을 영속성 컨텍스트에 넣는다.
  • 5. Team 의 Members 에도 member를 넣어준다.
  • 6. flush 를 이용하여 DB에 저장한다.
  • 7. 영속성컨텍스트에 있는 모든 정보를 날린다.
  • 8. 영속성컨텍스트가 비어있기 때문에 DB에서 가져온다.
  • 9. Team 에서 members를 가져온다
  • 10. 영속성컨텍스트에 내용이 없기 때문에 조회 할때 DB 에서 조회한다. 
1. Team team = new Team();
   team.setName("TeamA");
2. em.persist(team);

// 수정코드
3. Member member = new Member();
   member.setUsername("member1");
   member.setTeam(team);
4. em.persist(member);

// Team의 members 에도 값을 넣어줌
5. team.getMembers().add(member);

// select 쿼리를 보고 싶을때
6. em.flush(); // 영속성에있는 데이터를 DB 에 저장
7. em.clear(); // 영속성 컨텍스트 모두 클리어


// 조회시 수정코드
// 쿼리문 한번 발생
8. Member findMember = em.find(Member.class, member.getId());
9. List<Member> members = findMember.getTeam().getMembers();

10. for(Member m : members)
   {
       // 조회시 쿼리문 발생한다.
       System.out.println("m: " + m.getUsername());
   }

 

 

 

 

 

예제4 헷갈리수도 있으니 코드를 개선해보자

Member 에서 Team 저장할때 member 도 같이 저장하기

 

Main 코드

1. Team team = new Team();
   team.setName("TeamA");
2. em.persist(team);

// 수정코드
3. Member member = new Member();
   member.setUsername("member1");
   member.setTeam(team);
4. em.persist(member);

// Team의 members 에도 값을 넣어줌
//5. team.getMembers().add(member);

// select 쿼리를 보고 싶을때
6. em.flush(); // 영속성에있는 데이터를 DB 에 저장
7. em.clear(); // 영속성 컨텍스트 모두 클리어


// 조회시 수정코드
// 쿼리문 한번 발생
8. Member findMember = em.find(Member.class, member.getId());
9. List<Member> members = findMember.getTeam().getMembers();

10. for(Member m : members)
   {
       // 조회시 쿼리문 발생한다.
       System.out.println("m: " + m.getUsername());
   }

 

Member에서 member도 같이 넣어주기

changeTeam 메소드를 이용하여 team을 넣어주고 동시에 team에서 members 컬렉션을 불러와 넣어 주었다.

public class Member {

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

    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }

...
...
}

 

 

 

 

무한루프에 빠지는 경우

 

Member 에서 toString을 할경우

team을 출력시 다시 Team에 가서 toString을 수행한다.

그럼 team에서 다시 Member의 toString을 호출하면서 무한 루프에 빠진다.

 

Member

public class Member {

...
...
    @Override
    public String toString() {
        return "FullDuplexMember{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", team=" + team +
                '}';
    }
...
...
}

 

Team

@Entity
public class Team {


    @Override
    public String toString() {
        return "Team{" +
                "id=" + id +
                ", members=" + members +
                ", name='" + name + '\'' +
                '}';
    }
}

 

 

그림설명

728x90
반응형

댓글