JPA를 사용하여 CRUD 프로그램을 구현하는데 사용되는 API들과 엔티티의 상태 및 라이프 사이클
JPA가 EntityManager 객체를 얻기까지의 과정
EntityManager를 얻은 후에는, 이 객체를 이용하여 CRUD 작업을 진행하면 된다. 다만 주의할 점은 등록, 수정, 삭제 작업은 반드시 트랜잭션 내에서 끝내주는 것
EntityManager가 제공하는 CRUD 기능의 메소드
메소드 | 기능 |
---|---|
persist(Object Entity) | 엔티티 영속화 insert |
merge(Object Entity) | 준영속 상태의 엔티티 영속화 update |
remove(Object Entity) | 영속 상태의 엔티티 제거 delete |
find(Class entityClass, Object pk) | 하나의 엔티티 검색 select one |
createQuery(String jpql, Class resultClass) | JPQL에 해당하는 엔티티 목록 검색 select list |
영속성 컨텍스트
EntityManager = 영속성 컨텍스트라고 생각하면 됨
영속성 컨텍스트에 등록된 엔티티가 가지는 상태는 아래와 같다
persist()
, find()
detach()
, clear()
, close()
merge()
메소드를 통해 다시 영속 상태로 전환 가능remove()
영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다. 1차 캐시는 일종의 Map과 같은 컬렉션으로 Key와 Value로 엔티티를 관리한다.
이 때 Key 값은 @Id
로 매핑한 식별자 값이고 Value값은 엔티티 객체가 된다.
persist()
를 통한 엔티티 영속화@Id
: 엔티티 객체)이 때 중요한 점은 1차 캐시에 저장된 엔티티는 바로 실제 데이터베이스에 반영되지 않고, EntityTransaction의 commit()
으로 트랜잭션을 종료할 때 실제로 반영된다는 것
이렇게 영속성 컨텍스트에 저장된 엔티티를 데이터베이스에 반영하는 과정을 Flush라고 한다.
영속성 컨텍스트는 내부에 1차 캐시뿐만 아니라 SQL 저장소도 가진다
영속성 컨텍스트에 엔티티를 persist()
하여 저장하면 영속성 컨텍스트는 두 가지 작업을 순차적으로 처리한다.
이렇게 계속 새로운 엔티티를 누적하다가 commit()
메소드를 통해 트랜잭션을 종료하면 SQL 저장소에 저장했던 모든 SQL을 한번에 데이터 베이스로 전달한다.
→ 성능 최적화
엔티티를 검색한 후, 검색된 엔티티를 수정한다고 가정한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ex01");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Board board = em.find(Board.class, 1L);
board.setTitle("Changed Title");
tx.commit();
JPA는 검색된 엔티티를 영속성 컨텍스트에 저장 시, 엔티티의 복사본을 만들어서 별도의 컬렉션에 저장하는데 이 저장공간을 스냅샷이라고 한다.
트랜잭션이 종료될 때 스냅샷에 저장된 원래의 엔티티와 1차 캐시에 수정된 엔티티를 비교하여서 UPDATE 작성하여 이를 SQL 저장소에 저장된 다른 SQL들과 함께 전송한다.
remove()
메소드로 엔티티 삭제 시
테이블에 저장된 특정 데이터의 상세 조회 → find()
목록 조회 → JPQL(Java Persistence Query Language) 이라는 JPA에서 제공하는 별도의 쿼리 명령어 사용
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ex01");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
String jpql = "SELECT b FROM Board b order by b.seq desc";
List<Board> list =
em.createQuery(jpql, Board.class).getResultList();
for(Board board : list) {
System.out.println(board.toString());
}
JPQL의 검색대상은 테이블이 아니라 엔티티이다
검색대상이 엔티티이기 때문에 엔티티 이름과 엔티티가 가지고 있는 변수를 사용하여 쿼리를 구성해야한다.