ecsimsw

JPA / 영속성 컨텍스트 / 1차 캐시 / 쓰기 지연 본문

JPA / 영속성 컨텍스트 / 1차 캐시 / 쓰기 지연

JinHwan Kim 2020. 6. 28. 05:57

"T아카데미 / JPA 프로그래밍 기본기 다지기 - 김영한 " 강좌를 듣고 정리한 글입니다.

 

영속성 컨텍스트를 영속성 컨텍스로 관리하면 어떤 이점을 갖을까

 

  1. 1차 캐시 / 엔티티 동일성 보장

 

  2. 쓰기 지연

 

  4. 로딩 지연

 

  5. 변경 감지

 

 

1차 캐시 / 엔티티 동일성 보장

 

  영속성 컨텍스트는 내부에 캐시를 갖고 있다. (id, instance)의 맵 형태를 갖고 엔티티들이 저장된다. 

 

  트렌젝션 단위의 굉장히 짧은 메모리 공간이다.

 

 

  em.persist(member)로 member가 영속성 컨텍스트에 영속되면, 1차 캐시는 이를 담는다.

 

  이후 조회 시, DB에 접근해서 member1를 탐색하는 것이 아니라 1차 캐시를 먼저 훝어 member1을 바로 찾을 수 있고,

 

  캐시에 없다면 DB에서 검색 후 해당 객체를 1차 캐시에 저장하고 반환한다.

 

 

 

  이런 1차 캐시를 거친 조회로 엔티티의 동일성 보장이 가능한 것이다.

 

 

트렌잭션을 지원하는 쓰기 지연

 

  아래 코드에서 1의 영역에서 바로 INSERT QUERY를 DB에 바로 쏠 것 같지만,

 

  sql 쿼리를 바로 전송할 수 도, 나중으로 지연 시킬 수 도 있다. 

transaction.begin();

em.persist(memberA);

em.persist(memberB);

// ---- 1 ----

em.flush();

// ---- 2 ----

transaction.commit();

 

   이는 쿼리를 쓰기 지연 SQL 버퍼에 쿼리를 담아뒀다가, 영속성 컨텍스트의 명령에 따라 DB에 전송되기 때문이다.

 

  그 과정을 보면, 아래처럼 memberA가 컨텍스트에 영속되면, 우선 1차 캐시로 들어가고 쿼리는 SQL 버퍼에 넣어진다.

 

  memberB가 따라 영속되면 마찬가지로 1차 캐시로 들어갔다가 쿼리는 SQL 버퍼에 넣어진다.  

 

 

  이후에, transaction을 commit하거나, 컨텍스트에 버퍼를 비우도록 명령하면(flush) 그제서야 버퍼의 쿼리가 DB에 넘어가는 것이다.

 

 

 

예제 

 

  이 쓰기 지연을 이해한다면 아래 코드의 출력 값이 왜 0인지, 제대로 teamA의 members를 구하고 싶다면 어떤 코드를 추가해야하는지 알 것이다.

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

Team teamA = new Team();
teamA.setName("TeamA");
em.persist(teamA);

Member member_A = new Member();
member_A.setName("memberA");
member_A.setTeam(teamA);
em.persist(member_A);

Member findMember = em.find(Member.class, member_A.getId());
Team findTeam= findMember.getTeam();

System.out.println(findTeam.getMembers().size());  // 0

tx.commit();

 

  쓰기 지연과 1차 캐시를 제대로 이해했다면 알 수 있을 것이다. 

 

  1. em.flush()가 있고 없고에 따라 어떤 출력이 일어나는지

 

  2. 쿼리 실행 전에 어떻게 memberA의 팀을 먼저 출력할 수 있는지

 

  3. INSERT table 쿼리가 몇개 작동하는지

Team teamA = new Team();
teamA.setName("TeamA");
em.persist(teamA);

Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);

Member member_A = new Member();
member_A.setName("memberA");
member_A.setTeam(teamA);

em.persist(member_A);

em.flush();  

Member findMember = em.find(Member.class, member_A.getId());
Team findTeam= findMember.getTeam();

System.out.println(findTeam.getName());
flush가 있는 경우

1. create member 

2. create team

3. insert team

4. insert member

5. print "TeamA" (memberA.getTeam())
flush가 없는 경우

1. create member 

2. create team

3. print "TeamA" (memberA.getTeam())

4. insert team

5. insert member

 

Comments