JPA는 자바 객체와 DB를 매핑하기 위한 인터페이스를 제공하는 자바 ORM기술의 표준 명세입니다.
TransactionManager와 EntityManager에 대해 공부하고 나서, 간략하게 EntityManager가 Entity들의 영속성을 영속성 컨텍스트에서 관리한다라고 알고 있었는데 좀 더 구체적으로 살펴보고 싶어서 이렇게 포스팅하게 되었습니다.
EntityManager와 TransactionManager와의 관계에 대해서도 알아보도록 하죠.
즉, JPA에서는 jakarta.persistence.EntityManager를 사용하여 DB와의 통신을 합니다.
하지만, EntityManager에서는 Transaction을 관리하지 않습니다.
그래서 spring은 Transaction 관리를 위해 TransactionManager를 제공합니다.
그 이유는 EntityManager가 JPA 스펙의 일부이며, JPA의 역할은 Transaction을 관리하는 것이 아니라, 객체-관계 매핑(ORM)을 수행하는데 집중하기 떄문입니다.
따라서 EntityManager는 Transaction을 직접 시작하거나 커밋거나 롤백하지 않습니다.
대신 EntityManager는 Transaction 관리를 위한 도구로써 사용됩니다. 그러므로 일반적으로 EntityManager는 Transaction 범위 내에서 사용됩니다.
이는 Spring Framework와 함께 사용될 때 EntityManager에서 DB 작업 수행 시 Spring의 TransactionManager를 사용하여 트랜잭션 범위를 관리하므로 해당 Transaction 범위 내에서 작업이 수행되도록 보장할 수 있습니다.
JPA는 EntityManager를 사용해서 엔티티를 관리하는데, EntityManager는 여기서 영속성 컨텍스트를 내부에 두어 DB에 가기 전에 이 값이 조회된 값이면 조회하는 1차 캐시의 역할을 합니다.
Member a = em.find(Member.class, "member1");
Member b= em.find(Member.class, "member1");
System.out.println(a==b); //동일성 보장
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공한다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); //트랜잭션 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 쓰기 지연 SQL 저장소에 저장된 INSERT SQL을 flush() 한다.
trasaction.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 ? 이런 코드가 없어도 될까?
trasaction.commit();
아래의 사진과 같이 엔티티와 스냅샷을 비교해서 UPDATE SQL을 생성하여 쓰기 지연 SQL 저장소에 저장해둔다. 나중에 flush()하여 데이터베이스에 반영한다.
플러시란?
영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것이다.
영속성 컨텍스트를 플러시 하는 방법
1. em.flush() 직접 호출
2. 트랜잭션 커밋 -플러시 자동 호출
3. JPQL 쿼리 실행 - 플러시 자동 호출
JPQL 쿼리 실행할때 플러시가 자동으로 호출되는 이유?
영속성 컨텍스트의 일관성을 유지하기 위해서이다.
영속성 컨텍스트와 변경 내용 동기화하여, JPQL 실행 시점에서의 일관성을 보장한다.
그래서 쿼리 실행할 때, 영속성 컨텍스트에 반영된 변경 사항이 데이터베이스와 일치하지 않아 쿼리 결과가 예상과 다른 상황을 방지한다.
지연 로딩을 사용하면, 연관된 엔티티가 처음에는 프록시 객체로 로드된다. 실제 데이터베이스에서 데이터를 조회하는 쿼리는 해당 필드에 접근할 때 실행한다. 예를 들어, Author 엔티티를 조회하고 나서 books 필드에 접근할 때 지연 로딩이 작동하여 관련된 Book 엔티티들이 데이터베이스에서 로드된다.
지연 로딩은 연관된 엔티티를 모두 즉시 로딩해 오는것이 아니라, 필요한 순간에 추가적으로 쿼리가 나가서 얻어 오는 방식이다.
즉, TransactionManager와 EntityManager는 각각 다른 역할을 한다.