작성
·
385
·
수정됨
1
안녕하십니까 선생님 다름이아니오라 em.flush(), em.clear()를 했을 때와 안했을 때 query를 호출하는 경우와 호출되지 않는 경우에 대해서 이해가 잘 되지 않아 질문드립니다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
tx.commit();
위 코드에서
em.flush();
em.clear();
이 경우에서 em.flush(), em.clear() 를 하지 않으면 컨텍스트에 올라간 객체들은 Team(member 없음), Member 이렇게 존재하게 되는데,
em.find(Team) 을 통해서 얻어온 Team에는 Member가 없는 상황이라고 생각합니다.
이후 team.getMembers() 를 진행하면 Member 가 없는 상황이기에 쿼리가 발생하면서 DB에서 조회할 것 이라고 생각하나, 쿼리를 실행하지 않고 바로 요소가 비어있는 Persistentbag(Collection 구현체) 를 반환합니다.
그렇기 때문에 for ( var m : team.getMembers() ) 을 순회하면 team.getMembers() 가 비어있기 때문에 반복문이 실행되지 않아 표준출력이 발생하지 않습니다.
이와 마찬가지로
em.flush(), em.clear() 를 해줬을 때, em.find()를 통해 team을 호출하면 team 객체를 가져오는 쿼리를 실행하게 되는데, 이때도 team 에대한 정보만 조회하기에 Team에는 Member가 없는 상황이라고 생각합니다.
이후 team.getMembers() 를 하면 마찬가지로 Persistentbag(Collection 구현체) 을 반환하는데 이 비어있는 Collection 에 대해서 for ( var m : team.getMembers() ) 을 진행하면 member에 대한 query를 진행하여 member정보를 얻어옵니다.
정리해보자면 em.flush(), em.clear() 한 경우와 안한 경우 모두 team.getMembers()의 반환값으로 빈 Persistentbag 을 반환하는데, 어떤 기준으로 동작방식이 (쿼리가 실행되고 안되고) 달라지는지 궁금합니다ㅠ
추측을 해보자면...
em.flush(), em.clear() 한 경우에는
Persistentbag<ProxyMember> 이고
em.flush(), em.clear() 안한 경우에는
Persistentbag<Member> 이고,
Persistentbag.iteration() 함수 내부에서
<E> 가 ProxyMember 일 경우에 hasNext() == false 면 Query 를 진행해서 db에서 조회 해보고,
<E> 가 Member(순수한 객체) 일 경우에는 hasNext() == false 면 그냥 query없이 return 하게되는 걸까요..?
답변 1
2
안녕하세요. softcamp.study님
em.persist()를 호출하게 되면 메모리에 있는 객체를 그대로 영속성 컨텍스트에 저장해버립니다.
이 경우 직접 만드신 Team, Member가 영속성 컨텍스트에 존재하게 됩니다.
그런데 문제는 직접 만드신 Team에는 Member를 연관관계로 가지고 있지 않습니다.
이것은 사실상 순수 자바 객체입니다! JPA와는 무관하다고 보셔야 합니다.
두번째로 em.clear()를 통해서 영속성 컨텍스트를 완전히 제거한 후에 Team을 조회하게 되면 JPA가 해당 객체를 생성하게 됩니다. 따라서 JPA가 제공하는 지연 로딩 같은 기능을 제공 받을 수 있습니다.
따라서 처음과 같은 문제를 예방하기 위해서는 근본적으로 처음 관계를 설정할 때 양방형 연관관계를 모두 설정하셔야 합니다. 지금의 경우 Team -> Member의 관계가 설정되지 않았습니다.
물론 이것은 트레이드 오프가 있습니다. 양방향 연관관계를 모두 설정하는 것은 코드 복잡도가 높아지니까요. 비즈니스 로직에서 처음과 같은 상황에 Persist 이후에 바로 Team -> Member를 찾아서 사용해야 한다면 반드시 양방향 연관관계를 설정하셔야 합니다.
추가로 다음 내용도 참고해주세요.
https://www.inflearn.com/questions/336059
감사합니다.