프로젝트 개발에 사용한 것을 정리한다.
JPA, ORM
JPA는 ORM(Object-relational mapping)이며 객체 지향과 DB간 발생하는 차이를 매핑해 준다.
즉, 객체는 객체대로 설계하고, DB와의 차이점에 대해서는 ORM을 통해서 매핑하게 된다.
SQL 중심적 개발에서 객체 중심으로 개발할 수 있게 되며, DB에 종속적이지 않게 된다.
JPA는 JDBC API를 통해서 DB에 접근하며, Java Application과 JDBC API 사이에 존재하게 된다.
JPA는 인터페이스의 모음이며, Hibernates는 그 구현체이다.
이 JPA에서 중요한 것은 EntityManager와 Persistence Context(영속성 컨텍스트)이다.
영속성
데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터의 특성
(영속성을 갖지 않으면 데이터는 메모리에서만 존재하게 되고 프로그램이 종료되면 해당 데이터는 모두 사라지게 된다.)
Entity Manager Factory & Entity Manager
Entity Manager Factory
Entity Manager Factory는 Entity Manager를 만들고 구성하는 방법을 제공하는 인터페이스이다.
일반적으로 JPA의 구현체인 Hibernate에 의해서 생성되며 Entity Manager 인스턴스를 생성하는데 사용된다.
EntityManager
EntityManager는 DB table과 매핑된 객체인 Entity에 대한 CRUD 작업을 수행하기 위한 메서드들을 제공하며, Entity의 라이프 사이클과 영속성 관리 등을 담당한다.
Persistence Context (영속성 컨텍스트) 란
OracleDoc에서 Persistence Context는 영속화 되어있는 Entity identity에 대해 unique한 entity 인스턴스가 존재하는 entity 인스턴스의 집합이라고 한다.
즉, Persistence Context는 entity의 영속화에 관여하며, entity들이 DB로 바로 가지 않고 entity를 저장하는 환경으로서의 역할을 한다.
Persistence Context는 논리적인 개념이며, 아래 코드처럼 EntityManager를 통해서 Persistence Context에 접근한다.
EntityManager.persist(entity);
`persist()` 메서드를 활용하여 해당 `entity`를 Persistence Context로 보내고,
Persistence Context를 통해서 해당 데이터를 영속화 한다.
Persistence Context (영속성 컨텍스트) Type
Persistence Context는 Transaction-scope persistence context와 Extended-scope persistence context 두가지 type을 허용한다.
Transaction-scope persistence context
Transaction-scope persistence context는 transaction에 바인딩 된다.
transaction이 완료되는 즉시 Persistence Context에 있는 Entity들은 영구 스토리지인 DB로 flush 된다.
transaction 내부에서 작업을 수행할 때 EntityManager는 Persistence Context가 하나라도 존재한다면 존재하는 것을 사용하며, 하나라도 없다면 Persistence Context를 생성한다.
Default persistence context type은 TRANSACTION이다.
@PersistenceContext
private EntityManager entityManager;
EntityManager에 Persistence Context를 사용하게 하기 위해 `@PersistenceContext` 어노테이션을 달아준다.
Extended-scope persistence context
Extended-scope persistence context는 Persistence Context가 multiple transaction에 걸쳐서 존재할 수 있다.
transaction 없이 Entity를 Persistence Context에 유지시킬 수는 있지만 transaction 없이 DB에 flush 할 수는 없다.
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
EntityManager에 Persistence Context를 사용하게 하기 위해 `@PersistenceContext` 어노테이션을 달아주고,
type 값으로 `PersistenceContextType.EXTENDED`를 명시해야 한다.
Persistence Context (영속성 컨텍스트) 장점
- 성능 향상
- Persistence Context를 사용하면 Entity를 메모리에 캐시함으로써 빈번하게 DB에 접근하는 것을 줄일 수 있고, DB 로드 또한 줄어든다.
- 간소화된 DB 엑세스
- Entity를 관리하기 위한 단순화된 API를 제공하여 DB 엑세스를 단순화한다.
- 이를 통해 개발자는 낮은 수준의 DB 액세스를 위한 쿼리에 집중하기 보다는 Application의 비즈니스 로직에 집중할 수 있다.
- 데이터 일관성의 향상
- Persistence Context는 Entity 라이프 사이클(Entity 상태에 대한 관리)를 자동적으로 관리함으로써 Entity가 일관성있도록 보장해 준다.
- 향상된 scalability
- Persistence Context는 DB에 대한 접근과 Entity의 상태관리를 자동적으로 하기 때문에 확장성에서도 이점을 갖는다.
@PersistenceContext
EntityManager를 bean으로 주입할 때 사용하는 어노테이션이다.
Spring에서는 영속성 관리를 위해 EntityManager가 존재한다.
그래서 Spring Container가 시작될 때 EntityManager를 만들어서 bean으로 등록해 둔다.
이때 Spring이 만들어둔 EntityManager를 주입받을 때 사용한다.
`@PersistenceContext`로 지정된 프로퍼티에 두 가지 중 한 가지로 EntityManager를 주입해 준다.
- EntityManagerFactory에서 새로운 EntityManager 생성
- Transaction에 의해 기존에 생성된 EntityManager 반환
@PersistenceContext 사용해야 하는 이유
EntityManager를 사용할 때 주의해야 할 점은 여러 쓰레드가 동시에 접근하면 동시성 문제가 발생하여 쓰레드 간에는 EntityManager를 공유해서는 안된다.
일반적으로 Spring은 Singleton 기반으로 동작하기 때문에 bean은 모든 쓰레드가 공유한다.
그러나 `@PersistenceContext`로 EntityManager를 주입받으면 동시성 문제가 발생하지 않는다.
`@PersistenceContext`를 사용하면 동시성 문제가 발생하지 않는 이유는
Spring Container가 초기화되면서 `@PersistenceContext`로 주입받은 EntityManager를 Proxy로 감싼다.
그리고 EntityManger를 호출할 때마다 Proxy를 통해 EntityManager를 생성하여 Thread-Safe를 보장한다.
References
https://colevelup.tistory.com/21