// 엔티티 매니저 팩토리 생성
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("persistence-unit name");
// 엔티티 매니저 생성
EntityManger em = emf.createEntityManager();
엔티티 매니저 팩토리를 생성하는 비용은 상당히 크다.
따라서 한 개만 만들어서 애플리케이션 전체에서 공유하도록 설계한다.
반면, 엔티티 매니저를 생성하는 비용하는 비용은 거의 들지 않는다.
따라서 엔티티 매니저 팩토리 하나에서 다수의 엔티티 매니저를 생성시킨다.
엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하므로 서로 다른 스레드 간에 공유해도 되지만, 엔티티 매니저는 여러 쓰레드가 동시에 접근하면 동시성 문제가 발생하므로 쓰레드 간에 절대 공유하면 안된다.
스프링 프레임워크는 엔티티 매니저를 쓰레드에서 안전하게 동작하도록 지원해준다.
따라서 실무에서 엔티티 매니저에 동시성 문제가 발생하는 일은 거의 없다.
반면에 스프링을 사용하지 않고, 엔티티 매니저를 직접 생성하고 관리할 때는 동시성 문제가 발생할 수 있다.
하나의 엔티티 매니저를 생성해서 공유변수 같은 곳에 넣어버리면 큰일 나는 것이다.
JPA 를 이해하는 데 가장 중요한 용어 중 하나는 영속성 컨텍스트(persistence context)이다.
직역하자면 엔티티를 영구 저장하는 환경 이라는 뜻이다.
persist()
메서드는 정확히 말하자면
엔티티 매니저를 사용해서 엔티티를 영속성 컨텍스트에 저장한다.
영속성 컨텍스트는 논리적인 개념에 가깝고 눈에 보이지도 않는다.
그리고 엔티티 매니저를 생성할 때 하나 만들어진다.
이 엔티티 매니저를 통해 영속성 컨텍스트에 접근하고 관리한다.여러 엔티티 매니저가 같은 영속성 컨텍스트에 접근할 수도 있다. 지금은 위와 같이 생각하자.
4가지 상태가 존재한다.
// 객체를 생성한 상태(비영속)
Member member = new Member();
// 객체를 저장한 상태(영속)
em.persist(member);
// 객체를 영속성 컨텍스트에서 분리(준영속)
em.detech(member);
// 객체를 삭제한 상태(삭제)
em.remove(member);
1차 캐시
(1차 캐시에 저장)
(1차 캐시에서 조회)
(데이터베이스에서 조회)
동일성(identity) 보장
1차 캐시로 반복 가능한 읽기(repeatable read) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공
트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
트랜잭션 커밋을 하는 순간 INSERT
SQL 을 보낸다.
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
ts.begin();
em.persist(memberA);
em.persist(memberB);
// 아직 INSERT SQL 을 보내지 않음
// 커밋하는 순간 INSERT SQL 을 보냄
ts.commit(); // 트랜잭션 커밋
// 엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
ts.begin();
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memeberA.setUsername("kim");
// em.update(member) 같은 코드가 있어야하지 않을까 하지만 update 메서드는 존재하지도 않을 뿐더러 필요가 없다.
// 커밋하는 순간 dirty checking 을 통해서 해당 변경사항을 데이터 베이스에 동기화 시킨다.
ts.commit();
영속성 컨텍스트의 변경내용을 데이터베이스에 반영
em.detach(entity)
특정 엔티티만 준영속 상태로 전환
em.clear()
영속성 컨텍스트를 완전히 초기화
em.close()
영속성 컨텍스트를 종료