이전에 em.persist()
를 해도 바로 insert 쿼리가 나가지 않았다. 그럼 중간에 무슨일이 일어나는 걸까?
결론 부터 말하면 em.persist()
후에 1차 캐시에 저장된다.
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
다음 그림처럼 동작한다.
바로 저장하지 않고 1차 캐시에 저장하지? 어떤 성능적인 이점이 있을까?
member1은 이미 DB에 저장되어있는 상태
Member member = new Member();
member.setId("member2");
member.setUsername("회원2");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member Member1 = em.find(Member.class, "member1");
Member Member2 = em.find(Member.class, "member2");
정리하면, 1차 캐시에 저장된 엔티티는 동일 트랜잭션 내에서 반복 조회 시 데이터베이스에 접근하지 않고, 메모리에서 직접 반환한다.
-> 위 예제에서 SQL을 출력하면 2개의 데이터를 조회했지만 Member2에 관한 Select 쿼리 1개만 나간다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭 션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.
em.persist()
해도 바로 DB에 저장되지 않고 SQL을 쌓아 두었다가 트랜잭션 커밋이 일어날 때 한번에 SQL을 보낸다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
em.persist();
바로 다음 그림처럼 동작한다.
transaction.commit();
이 일어나면
영속 엔티티 데이터 수정은 어떻게 할까?
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋
다음 그림처럼 내가 최초로 읽어온 데이터를 스냅샷으로 기억하고 있다가 flush가 일어날 때 변경된 부분을 비교해 Update SQL을 만들어 변경한다.
다음 포스팅에서는 플러시와 준영속 상태에 대해 좀 더 자세히 알아보자