입문주차에서 Spring Data JPA(JPA를 랩핑해서 사용하기 편하게 하주는 라이브러리)를 사용했다. 숙련 주차에서는 공부를 위해 JPA만 사용하던 시절의 코드를 학습한다.
하나의 큰일을 여러 스레드가 동시에 처리하면 동시성 문제가 생길 수 있다. 이를 방지하기위해 특정 리소스나 정보는 공유하지 못하게 하는 등의 처리가 필요하다.
EntityManager에는 공유하면 안되는 특정 리소스나 정보가 있고, 여러 스레드가 하나의 엔티티 매니저를 이용 할 수 없도록 처리해야한다. 그래서 EntityManagerFactory에서 필요할 때마다 여러개의 EntityManager를 만드는 구조로 설계되었다.
하나의 Database에 하나의 EntityManagerFactory가 매핑된다. 같은 EMF에서 나온 EM는 같은 DB에 접속한다. EM은 EMF를 통해 사용자의 요청 하나당 하나씩 생성되어 DB커넥션풀을 통해 DB에 CRUD를 요청한다. 하나의 트랜잭션에는 한개의 EM만 존재한다. 즉, 멀티쓰레드에서는 EM의 공유가 불가능하다.
Spring Boot에는 application.properties를 통해 옵션 조정이 가능하고 EMF를 자동으로 생성한다.
영속성 컨텍스트 : Persistence Context
Entity를 영구 저장하는 환경. 어플리케이션이 DB에서 꺼내온 데이터 객체를 보관하는 역할.
영속성 컨텍스트는 Entity Manager를 통해 Entity를 조회하거나 저장할때 보관/관리한다.
Entity Manager마다 개별적으로 부여되는 어떤 논리적 공간같은 개념.
기본적인 자바(J2SE)에서는 EM : 영속성컨텍스트가 1 : 1이지만,
Spring 같은 컨테이너 환경(J2EE)에서는 N : 1이다(하나의 컨텍스트에 여러 EM이 접근가능).
// 엔티티를 생성
Member minsook = new Member();
member.setId("minsook");
member.setUsername("민숙");
em.persist(entity); // 엔티티 매니저를 통해 영속성 컨텍스트에 엔티티를 저장
// 특정 엔티티를 영속성 컨택스트에서 분리. 1차캐시, 쓰기지연SQL저장소 정보 제거. DB에 저장불가
em.detach(entity);
// 영속성 컨텍스트를 비우기. 모든 Entity를 준영속상태로. 틀은 유지 내용은 비움
em.clear();
// 영속성 컨택스트를 종료. 영속상태의 모든 Entity를 준영속으로
em.close();
// 준영속 상태의 entity를 다시 영속상태로 전환 및 병합
em.merge(entity);
em.remove(entity)
영속성 컨텍스트는 내부에 1차 Cache가 존재. 영속상태의 Entity는 해당 캐시에 저장된다.
Map형태로 되어있으며 key는 @Id로 매핑한 식별자며 DB의 기본키와 매핑. value는 Entity의 인스턴스. 즉, 영속성컨텍스트에서 데이터를 저장하고 조회하는 모든 기준은 DB 기본키 값.
DB를 사용하는 작업은 상대적으로 부하와 비용이 심해 작업 사용을 줄여야 할 필요가 있다.
데이터를 조회할 때마다 SQL쿼리를 낼 수는 없다. 그래서 영속성 컨텍스트 내부에 1차캐시
를 두고
EM은 EM안의 쓰기 지연 SQL 저장소에 INSERT SQL을 별도로 저장하고 있다가, 트랜잭션을 커밋하면, 저장소의 모든 INSERT SQL을 DB에 요청한다. 커밋 전까지는 DB에 저장요청 x.
트랜잭션이 커밋될때 EM은 flush를 실행한다.
위와 비슷한 맥락으로 MemberA, MemberB를 생성할 때 마다 DB를 다녀오는건 비효율적.
여러번 DB를 방문하지 않게 하기위해 영속성 컨텍스트 내부에 쓰기 지연 SQL 저장소
를 두고
entityManager.commit()
메서드를 호출Flush
가 일어나고: JPA는 1차캐시와 쓰기 지연 SQL 저장소를 이용해 변경과 수정을 감시해준다.
스프링에서는 다대일로 여러 EM이 하나의 영속성 컨텍스트에 접근 가능하다.
Spring Container는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.
즉, 트랜잭션의 범위와 영속성 컨텍스트의 생존 범위가 같다.
트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날때 PC를 종료한다.
Controller까진 준영속 상태이다가 Service-Repository(트랜잭션 범위)에서 영속상태이다.
@Transactional을 사용해서 트랜잭션을 시작하고 종료한다.
트랜잭션을 커밋하면 JPA는 먼저 PC를 flush해서 변경 내용을 DB에 반영한 뒤, 트랜잭션을 커밋해서 변경내용을 저장한다.
예외가 발생하면 트랜잭션을 롤백하고 종료하는데 이때는 flush를 호출하지 않는다.
트랜잭션의 원칙
같은 트랜잭션 내에서 여러 EM을 쓰더라도, 같은 영속성 컨텍스트를 사용한다.
같은 EM을 쓰더라도, 트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다.