영속성 컨텍스트를 알기 위해서는 EntityManagerFactory와 EntityManager를 알아야 한다.
EntityManager를 만드는 공장
이다.한 개
만 생성된다.EntityManagerFactory
를 이용하여 생성된다.스레드간 공유
할 수 있다. 스레드 간 공유를 하면 안된다
.JPA의 경우 다음과 같은 구조로 동작한다.
EntityManager는 데이터베이스 커넥션을 바로 사용하는 것이 아니다. 꼭 데이터베이스와 연결이 필요한 시점에 사용한다.
객체를 보관
하는 역할을 한다.entityManager.persist(entity);
를 통해 영속성 컨텍스트에 저장할 수 있다.영속성 컨텍스트의 값이 DB에 반영되는 경우
1. flush를 통해 개발자가 직접 반영하는 경우
2. Transaction이 끝나서 해당 쿼리가 commit 되는 시점
3. 복잡한 조회 조건에 JPQL Query가 실행되는 시점
(사전에 영속성 콘텍스트에 추가한 데이터들이 flush()되지 않아서 DB에 업데이트 되지 않았다면 JPQL쿼리문을 수행하는데 오류가 발생할 수 있기에 JPQL 쿼리문을 실행하기 전에 자동으로 flush()를 호출한다.)
@Autowired
private EntityManager entityManager;
비영속 (new/transient) :
영속(managed) :
준영속(detached) :
삭제(removed) :
// 비영속 상태 = 단순 객체 생성
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
EntityManager em = emg.createEntityManager();
em.getTransaction().begin();
// 영속 상태가 된다.
em.persist(member);
// 준영속 상태로 만들기 = 회원 엔티티를 영속성 컨텍스트에서 분리
em.detach(member);
// 삭제
em.remove(member);
JPA는 em.persist(member);를 실행하면 위 그림처럼 1차 캐시에 엔티티를 저장한다. 즉, 아직까지 DB에 저장된 상태는 아니다.
그 다음 아래의 코드를 실행하여 Entity를 조회하자.
Member member = em.find(Member.class, "member1");
위의 코드를 실행하면 JPA는 우선 1차 캐시에서 Entity를 찾고, 만약 찾는 Entity가 1차 캐시에 없으면 DB를 조회한다.
즉, DB에 바로 접근하는 것이 아닌, 1차 캐시에서 저장 및 관리를 함으로써 JPA의 조회 성능이 올라간다는 장점이 있다.
아래의 코드와 같이 id로 반복적을 조회하는 경우, @Transactional이 붙는다면, JPA는 처음에만 DB에서 조회하고, 다음 조회 때에는 1차 캐시에 있는 값을 반환한다.
@Test
void cacheFindTest() {
System.out.println(userRepository.findById(1L).get());
System.out.println(userRepository.findById(1L).get());
System.out.println(userRepository.findById(1L).get());
}
User findUser1 = entityManager.find("User.class", "1L");
User findUser2 = entityManager.find("User.class", "1L");
System.out.print(findUser1 == findUser2) // Result: true
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit();
엔티티의 수정이 일어나도 개발자는 영속성 컨텍스트에 따로 알려주지 않아도 영속성 컨텍스트가 알아서 변경 사항을 체크해준다. 이것을 Dirty checking이라고 한다.
1차 캐시에 Entity를 저장할 때, 스냅샷 필드도 저장하여 commit이나 flush를 할 때 해당 Entity와 스냅샷을 비교하여 변경사항이 있는 경우 자동으로 UPDATE SQL을 만들어서 DB에 전송한다.
// 영속 엔티티 조회
Member memberA = em.find(Member.class, 101L);
// 영속 엔티티 데이터 수정
memberA.setName("hi");
tx.commit();
위의 코드를 실행하면 자동으로 update query 를 실행한다. 다음과 같은 동작 순서에 의해 발생한다.
변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 Entity에만 적용.