엔티티를 영구 저장하는 환경
JPA가 엔티티를 기반으로 데이터를 처리하는 데 있어서 핵심 역할을 하는 객체
메타데이터를 참조하여 애플리케이션 운용에 필요한 객체를 생성하고 관리하는 객체 persistence.xml 파일을 로딩하여 만들어진다.
영속성 컨텍스트는 EntityManager 객체를 생성할 때 자동으로 생성되며, 오로지 EntityManager 가 제공하는 메소드를 통해서만 접근할 수 있다.
영속성 컨텍스트가 관리하는 엔티티는 생성(NEW), 관리(MANAGED), 분리(DETACHED), 삭제(REMOVED) 같은 4가지 상태로 존재할 수 있다.
상태 | 의미 |
---|---|
생성(NEW) | 엔티티가 영속 컨테이너에 등록되지 않은 상태 |
관리(MANAGED) | 엔티티가 영속 컨테이너에 등록된 상태 |
분리(DETATCHED) | 엔티티가 한 번 영속 컨테이너에 등록되었다가 컨테이너에서 분리된 상태 |
삭제(REMOVED) | 엔티티가 영속 컨테이너에서 삭제 처리된 상태 |
// 엔티티 생성 및 초기화
Employee employee = new Employee
employee.setName("둘리");
영속 상태라고도 하며, 엔티티가 영속 컨테이너의 관리를 받고 있는 상태, 영속 컨테이너가 엔티티 객체를 관리하기 위해서는 엔티티 객체를 당연히 등록해야 한다.
엔티티를 영속 컨테이너에 등록하여 관리 상태로 전환하는 방법으로는 2가지가 있다.
EntityManager.persist
테이블에 실제 Insert가 발생하기 위해서는 persist 메소드 호출이 반드시 트랜잭션 안에서 이루어져야 한다. 트랜잭션이 종료되는 시점에 실질적인 INSERT가 데이터베이스에 전송된다.
관리중인 엔티티의 상태가 변경되는 순간, 변경을 감지하여 데이터베이스에 UPDATE를 처리해 준다. (더티 체킹)
EntityManager.find
엔티티를 관리 상태로 만들기 위해, 검색 기능인 find 메소드를 사용할 수도 있다.
조회하고자 하는 엔티티가 영속 컨테이너에 존재하면 엔티티를 반환하고, 없으면 데이터베이스에서 데이터를 조회하여 새로운 엔티티를 생성한다.
그리고 생성한 엔티티를 영속 컨테이너에 등록하고 나서 클라이언트로 반환한다.
준영속 상태라고도 하며, 영속 컨테이너에 있던 엔티티가 특정 작업에 의해 영속 컨테이너에서 벗어난 상태를 의미한다. 분리 상태의 엔티티는 영속 컨테이너의 통제에서 벗어나기 때문에 영속 컨테이너가 엔티티의 상태를 관리하지 못한다. (더티 체킹이 일어나지 않음)
영속 컨테이너를 벗어난 상태라는 점에서 분리 상태를 생성 상태와 비슷하다고 생각할 수 있지만 그렇지 않다. 두 상태를 구분하는 가장 중요한 기준은 식별자 값의 유무라고 할 수 있다.
생성 상태의 경우, 관리 상태로 전환된 적이 없으므로 식별자 값을 가지지 않지만 분리 상태는 한 번 관리 상태로 전환된 적이 있으므로 영속 컨테이너를 벗어났기 때문에 반드시 식별자 값을 가진다.
메소드 | 의미 |
---|---|
detach(entity) | 특정 엔티티만 분리 상태로 전환한다. |
clear() | 영속 컨테이너를 초기화한다. 이때 영속 컨테이너가 관리하던 모든 엔티티들을 분리 상태로 전환한다. |
close() | 영속 컨테이너를 종료한다. 영속 컨테이너는 종료되기 직전에 컨테이너가 관리하던 모든 엔티티들을 분리 상태로 전환한다. |
JPA는 영속 컨테이너 내부에 엔티티 캐시(cache)라는 저장 공간을 만들어서 엔티티들을 관리한다. key,value 를 쌍으로 엔티티들을 저장하고 관리하는 일종의 java.util.Map 과 같은 컬렉션
persist 메소드 기능이 엔티티를 캐시에 등록하는 것에 한정이 되어 있고, 영속 컨테이너가 캐시에 등록된 엔티티에 대해서 INSERT 까지 처리하도록 하려면 반드시 캐시에 대한 플러시(FLUSH)가 실행되어야 한다.
플러시는 저장된 엔티티의 상태 변화를 데이터베이스에 반영하는 동기화 과정으로 이해하면 된다.
플러시가 발생하는 경우는 총 3가지
플러시가 실행되면 영속 컨테이너는 캐시에 등록된 모든 엔티티들의 상태를 체크하여 SQL 구문을 만든다. 생성된 SQL 구문들을 개별적으로 데이터베이스에서 전송하는 것이 아니라 한 번의 연결로 처리한다.
여러 건의 데이터에 대하여 등록/수정/삭제할 때도 효울적이지만 일반적으로는 검색 기능을 처리할 때 매우 유용하다.
영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 1차 캐시라고 한다.
Map이 하나 있는데 키는 @Id로 매핑한 식별자고, 값은 엔티티 인스턴스이다.
첫 조회시 엔티티가 1차 캐시에 없으므로, 데이터베이스에 조회하고 이를 1차 캐시에 저장한다.
추가 조회시 1차 캐시의 데이터를 가져온다.
그래서 첫 조회와 이후 조회한 엔티티가 같은 인스턴스임을 보장할 수 잇다.
이로 인해 영속성 컨텍스트는 성능상의 이점과 엔티티의 동일성을 보장한다.
엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고, 내부 쿼리 저장소에 INSERT SQL을 모아둔다.
또 그와 동시에 1차 캐시에 저장한다.
트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스에 보내는데 이것을 쓰기지연이라고 한다.
이러한 쓰기지연의 장점은 저장을 여러번 하는 경우, 매번 데이터 베이스에 접속하지 않고 일괄처리 할 수 있어 이점이 있다.
엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해두는 스냅샷을 엔티티 인스턴스와 구분하여 저장한다.
플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾고, 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
이 변경감지는 영속성 컨텍스트가 관리하는 영속성 상태의 엔티티에만 적용 된다.
엔티티의 모든 필드를 업데이트하는 것이 기본 전략이여서 옵션을 수정하면 수정한 필드만 업데이트 할 수 있으나 모든 컬럼을 비교하는 단점이 있다.
개발을 하다보면 엔티티에 관계가 복잡해지고 이로 인해, 조인으로 인한 성능 저하를 피할 수 없게 된다.
지연로딩은 연관된 엔티티를 실제로 사용할 때 연관된 엔티티를 조회( SELECCT )하는 것을 말한다. 이 방법으로 성능 최적화를 노릴 수 있다.
참조한 책과 사이트
자바 ORM 표준 JPA 프로그래밍