강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

오리쉐리님의 프로필 이미지
오리쉐리

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

양방향 연관관계와 연관관계의 주인 2 - 주의점, 정리

em.flush()유무에 대해 질문드립니다.

작성

·

121

0

Team team = new Team();

team.setName("TeamA");

em.persist(team);

Member member = new Member();

member.setName("member1");

member.setTeam(team);

em.persist(member);

//em.flush(); // 영속성 컨텍스트에 있는 쿼리를 db에 전송한다.

//em.clear(); // 영속성 컨텍스트를 초기화

// 이렇게 하면, 위 em.persist()를 통해 영속성컨텍스트에 있는 Member를 가져오는게 아니라,

// em.find()를 할때 db에서 select를 해서 가져온다.

Team findTeam = em.find(Team.class, team.getId());

List<Member> members = findTeam.getMembers();

for (Member m : members) {

System.out.println("m = " + m.getName());

}

여기에서 em.flush()와 em.clear()를 하지않으면, findTeam에 Members가 비어있는 이유가 무엇인지 모르겠습니다.

em.flush()를해서 db에 있는 Team데이터와, em.flush()하지않은 영속성컨텍스트에 있는 Team데이터가 무엇이 다르길래 영속성컨텍스트에서 em.find를 하면 members가 비어있는것인지 모르겠습니다. 즉, db를 통해 em.find하면 members가 있고, 영속성컨텍스트를 통해 em.find를 하면 members가 없는데 왜 그런것인가요?

1차캐시에 team데이터를 저장하는것과 db에 team데이터를 저장하는것이 다른가요? 강의를 계속 봐도 이해가 가질않아 질문드립니다.

답변 3

1

오리쉐리님의 프로필 이미지
오리쉐리
질문자

찾아보니, 지연로딩과 프록시에 대한 내용인거같은데 강의 중반부에 해당 내용이 있더라구요..

지금은 그냥 db에서 조회한 Team에 대해서 team.getMember()를 호출하면 Select쿼리를 호출해서 member를 가져오기때문에 members에 값이 있는것이고, 1차캐시에서 조회한 Team에 대해서 team.getMember()를 호출하면 값이 없기때문이라고 이해해도 괜찮을까요?

맞습니다!

양방향 관계의 객체들끼리는 본 강의에서 설명해드린 연관관계 편의 메서드와 같이 연관관계의 주인이 아닌 쪽에 매핑된 객체에서의 처리도 필요합니다.

Team : Member 이라는 객체 간의 관계에서 보면

말씀해주셨던 위의 코드대로 했을 때

Team team = new Team();

team.setName("TeamA");

em.persist(team);

Member member = new Member();

member.setName("member1");

member.setTeam(team);

em.persist(member);

이 단계에서 1차 캐시를 기준으로 봤을 때 member.teammember.setTeam(team) 으로 인해 member의 team 필드에 값이 들어갔으나, team.members 필드에는 team 객체가 추가되지 않은 상태입니다. team 객체 입장에서 봤을 때는 members 에 어떤 작업도 해주지 않았기 때문입니다.

JPA는 1차 캐시를 우선적으로 바라보고 있기 때문에 플러시 및 영속성 컨텍스트 초기화를 해주지 않고 find()를 해줬을 때 1차캐시에 있는 memberteam를 그대로 사용합니다. 그래서 team.members 필드가 빈 것으로 나오는 것입니다.

반면에 플러시 및 영속성 컨텍스트를 초기화 해준 후에 find()를 해주게 되면 Team 엔티티를 조회할 때 members 필드를 보고 관련 데이터를 같이 조회해서 members 필드에 매핑해주게 됩니다. 그래서 이 때는 team 의 members 필드가 비지 않은 상태가 됩니다.

JPA 또한 기본적으로는 자바 객체를 사용하기 때문에 단순히 자바 객체의 입장에서 생각해보시면 조금 더 이해가 쉬우실 것 같습니다 🙂

오리쉐리님의 프로필 이미지
오리쉐리
질문자

답변 너무 감사드립니다!!

추가적으로 답변 주신걸 보면,

JPA는 1차 캐시를 우선적으로 바라보고 있기 때문에 플러시 및 영속성 컨텍스트 초기화를 해주지 않고 find()를 해줬을 때 1차캐시에 있는 member를 그대로 사용합니다. 그래서 team.members 필드가 빈 것으로 나오는 것입니다.

 

==> 1차캐시에 있는 member를 그대로 사용한다는게 무슨 의미인가요??

1차 캐시에 영속상태로 저장된 memberteam 엔티티(team.members가 비어있는)를 그대로 사용한다는 뜻입니다 🙂

오리쉐리님의 프로필 이미지
오리쉐리
질문자

음 근데 왜 team.members 필드가 빈 것으로 나오는 것인가요..?

죄송합니다... 제가 답변을 이상하게 드렸습니다😭
1차 캐시에 영속상태로 저장된 team을 그대로 사용하기 때문에 team.members 가 비어있다는 말씀을 드리고 싶었습니다

오리쉐리님의 프로필 이미지
오리쉐리
질문자

1차 캐시에 영속상태로 저장된 team의 team.members 필드에는 team 객체가 추가되지 않은 상태인데 1차 캐시에 영속상태로 저장된 team을 그대로 사용하기 때문에 team.members 가 비어있다는 말씀이실까요??

먼저 위에 있는 글들도 수정해두겠습니다!

1차 캐시에 영속상태로 저장된 team의 team.members 필드에는 team 객체가 추가되지 않은 상태인데 1차 캐시에 영속상태로 저장된 team을 그대로 사용하기 때문에 team.members 가 비어있다는 말씀이실까요??

team의 team.members 에서는 member객체가 들어가있지 않은 상태입니다! 왜냐하면 객체적으로 저희가 team.members.add(member)와 같은 코드를 해주지 않았기 때문입니다.

그래서 team.members = [] 인 상태의 team이 영속화되어 1차캐시에 저장되어 있고 find()시 이를 그대로 사용하기 때문에 team.members 가 비어있다고 말씀드리고 싶었습니다!

0

오리쉐리님의 프로필 이미지
오리쉐리
질문자

아직 이해가 되지않습니다..

em.flush()를 해주고 나면, db에 데이터가 반영되고, 이후에 em.find() 해서 team.getMembers();를하면 해당 team을 찾기 위한 select쿼리를 날리고, 해당 member를 찾기위한 select쿼리를 날려서 가져오기때문에 team.getMembers()를하면 값이 있는거고, 1차캐시에 대해서 em.find() 해서 team.getMembers()를하면 이때는 team을 찾기 위한 select쿼리를 날리는게 아니라 단지 1차캐시에 있는 team객체만 가져와서 이 객체에 대해 team.getMember()를 하니까 비어있는것인가요..?

0

안녕하세요, 인프런 AI 인턴입니다. 오리쉐리님께서 질문해주신 부분에 대해 설명드리겠습니다.

JPA의 영속성 컨텍스트와 데이터베이스와의 차이를 중심으로 설명드리겠습니다. 영속성 컨텍스트는 엔티티의 상태를 관리하는 일종의 1차 캐시 역할을 합니다. em.persist()를 사용하면 엔티티는 영속성 컨텍스트에 저장됩니다. 그러나 이 단계에서는 실제 데이터베이스에 저장된 것은 아닙니다.

em.flush()em.clear()를 하지 않으면 members가 비어있을까요?

  1. 영속성 컨텍스트 상태:

    • em.persist()로 인해 teammember는 영속성 컨텍스트에 존재합니다. 하지만 이때 findTeam.getMembers()를 호출하면, 영속성 컨텍스트 내의 team 객체는 members 리스트에 대한 처리가 완료되지 않은 상태입니다.
    • 이는 JPA의 기본적인 작업 방식으로, 영속성 컨텍스트는 엔티티의 상태를 프록시 객체로 유지하며 flush가 발생하기 전까지 데이터베이스에 쿼리를 보내지 않습니다.
  2. em.flush()와 차이점:

    • em.flush()를 호출하면 영속성 컨텍스트에 있는 내용을 데이터베이스에 반영합니다. 이때 memberteam의 관계가 데이터베이스에 저장됩니다.
    • em.flush()를 하지 않는 경우, findTeam.getMembers()에서 members를 프록시로 처리하여 데이터베이스에 조회 쿼리를 보내지 않습니다. 그러므로 members는 비어있게 됩니다.
  3. 데이터베이스와의 동기화:

    • em.find()는 영속성 컨텍스트에서 객체를 찾거나, 없는 경우 데이터베이스에서 새로 조회합니다. em.clear()를 하면 영속성 컨텍스트가 비워지므로, 모든 조회가 데이터베이스에 의존하게 되어 team.getMembers()는 데이터베이스에서 members를 가져옵니다.

이와 관련된 질문 중 하나로 양방향 연관관계와 연관관계의 주인 2 -주의점, 정리 게시물을 참고하시면 이해하시는데 도움이 될 것 같습니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

오리쉐리님의 프로필 이미지
오리쉐리

작성한 질문수

질문하기