EntityManager Factory를 통해 요청이 올 때마다 EntityManager를 생성한다.
매니저는 내부적으로 커넥션 풀을 사용하여 DB에 접근한다.
EntityManager.persist(entity);
엔티티를 영속화하는 코드이다.
엄밀히 말하면 DB에 엔티티를 저장하는 것이 아니라, 영속성 컨텍스트라는 곳에 저장하는 것이다.
영속성 컨텍스트는 눈에 보이지 않으며, EntityManager를 통해 접근한다.
비영속 → 영속 → 준영속 → 삭제
// 객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 위에 코드(비영속 상태)와 이어서
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 객체를 저장한 상태 (영속)
em.persist(member);
em.detach(member);
em.remove(member);
Member findMember = em.find(Member.class, "member1");
DB가 아니라 1차 캐시에서 엔티티를 찾는다. 만약 1차 캐시에 존재하다면, 바로 가져온다.
Member findMember = em.find(Member.class, "member2");
만약 1차 캐시에 찾는 엔티티가 없다면, DB에서 조회하여 1차 캐시에 저장한 후 반환한다.
그렇기 때문에 쿼리 로그를 찍으면, select 쿼리가 날라가지 않는 것을 볼 수 있다.
이후 member2를 또 찾게 된다면 그 때는 1차 캐시에서 바로 가져오면 된다.
하지만 비즈니스가 끝나면 엔티티 매니저가 종료되므로(= 트랜잭션 하나에서 작동), 큰 이득을 얻을 수 없다 😞
비즈니스가 굉장히 복잡할 때는 쿼리를 줄일 수 있다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); // true
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 DB가 아닌 애플리케이션 차원에서 제공한다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction()
transaction.begin();
em.persist(memberA);
em.persist(memberB);
transaction.commit();
persist() 함수가 호출될 때 바로 insert 쿼리가 날라가지 않는다.
커밋할 때까지 JPA가 쿼리를 모아뒀다가, 커밋이 됐을 때 한번에 쿼리를 DB로 보낸다.
영속성 컨텍스트에는 1차 캐시 말고, 쓰기 지연 SQL 저장소라는 것도 있다.
persist() 메소드가 호출될 때, 1차 캐시 저장과 함께 insert 쿼리가 쓰기 지연 SQL 저장소에 쌓인다.
이렇게 쌓인 쿼리는 트랜잭션이 커밋될 때 DB에 저장된다. (JPA에서는 이 과정을 flush라고 한다.)
Member memberA = em.find(Member.class, "memberA");
memberA.setUsername("hi");
// 엔티티를 다시 저장하는 코드가 필요 없다 !
// em.persist(memberA);
transaction.commit();
엔티티를 조회할 때, 엔티티와 스냅샷을 비교하여 달라졌으면 쓰기 지연 SQL 저장소에 update 쿼리가 저장되고 트랜잭션이 commit될 때 DB에 반영된다.
: 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영
플러시는 영속성 컨텍스트를 비우는 것이 아니라, 변경 내용을 데이터베이스에 동기화하는 작업이라는 것을 꼭 알아두자.
앞서 살펴본 내용을 정리하면, 플러시가 발생될 때 아래 3가지 일이 일어난다.
FlushModeType.AUTO
(기본값) - 커밋이나 쿼리를 실행할 때 플러시FlushModeType.COMMIT
- 커밋할 때만 플러시되도록이면 옵션을 바꾸지 않고 사용하는 것을 추천한다.
영속 상태에서 준영속 상태로 분리되면, 영속성 컨텍스트가 제공하는 기능을 사용하지 못하게 된다.
Member memberA = em.find(Member.class, "memberA");
memberA.setUsername("hi");
em.detach(memberA);
해당 코드를 실행하면 select 쿼리만 가고, update 쿼리는 가지 않는다. (준영속 상태가 되었기 때문!)
// 준영속 상태로 만드는 방법
em.detach(entity); // 특정 엔티티만 준영속 상태로 전환
em.clear(); // 영속성 컨텍스트를 초기화
em.close(); // 영속성 컨텍스트를 종료