이번 포스팅에서는 엔티티매니저와 영속컨텍스트에 대해서 알아보려한다. 그전에 엔티티와 엔티티매니저가 무엇인지 알아야 하는데 이에 대해서는 이전에  정리한 내용이 있으니 참고하길 바란다.

+ JPA


JPA를 이용하여 개발한 프로그램 코드는 보통 다음과 같은 패턴으로 개발된다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
EntityManager emf = Persistence.createEntityManagerFactory("jpastart");
EntityManager em = emf.createManager();
EntityTransaction transaction = em.getTransaction();
 
try {
    transaction.begin();
    Sight sight = em.find(Sight.class1);
    sight.setDetail(new SightDetail('오전9~오후5시''연중무휴''100여대 주차가능'));
    transaction.commit();
catch(Exception e) {
    transaction.rollback();
    throw e;
finally {
    em.close();
emf.close();
}
cs


엔티티매니저를 생성한 이후 트랜잭션을 시작하고 엔티티매니저를 사용하여 엔티티(Sight)를 검색하고 명소정보(SightDetail)를 지정한 이후 트랜잭션을 커밋한다. 얼핏 일반적인 jdbc를 이용한 프로그래밍 패턴과 비슷하지만 JPA는 큰 차이점이 있다.



영속 컨텍스트

앞서 JPA의 가장 큰 특징은 영속성(Persistence)이다. JPA를 사용해 검색한 DB매핑정보(엔티티)는 메모리(영속 컨텍스트)에 저장되고 이러한 엔티티는 영속객체라 부른다. JPA는 매번 데이터베이스에 접근하는 것이 아니라 엔티티매니저를 사용하여 메모리상에 작업을 한후 트랜잭션이 커밋되는 시점에 데이터베이스에 반영되는 구조를 가지고 있다. 


엔티티, 엔티티매니저와 영속 컨텍스트의 관계는 어느정도 이해가 됐을 것 같은데 간단히 정리하면 아래의 그림과 같다.


프로그램에서 엔티티매니저를 이용하여 데이터를 검색요청하면 영속 컨텍스트에 데이터가 없을 경우 DB에서 검색하여 영속 컨텍스트에 엔티티로 저장한다. 이후에 같은 식별자를 가지는 데이터를 요청할 경우에는 DB에서 검색하는 것이 아니라 영속 컨텍스트에서 검색한 결과를 반환한다.



전체적인 흐름은 위와 같고 먼저 엔티티매니저에 대해 자세히 알아보도록 하자.



EntityManager(엔티티매니저)


앞서 엔티티매니저는 영속컨텍스트에 저장되어 있는 엔티티를 관리하기위한 객체라고 설명했었다. 엔티티매니저를 관리하는 주체에 따라 종류가 구분된다

 종류

설명 

애플리케이션 관리 엔티티매니저 

 애플리케이션에서 직접 emf를 생성하고 관리

컨테이너관리 엔티티매니저 

 JBoss EAP, 웹로직, TomEE와 같은 JEE 컨테이너가 EMF를 생성하고 관리


전자는 앞선 코드에서 처럼 프로그램 코드에서 직접 EntityManager를 직접 생성하고 종료하는 방식을 말한다. 반면 컨테이너 관리 엔티티매니저는 프로그램 코드가 아닌 JEE 컨테이너[각주:1]에서 EntityManager의 라이프사이클(생성~종료)을 관리한다. 

 아래의 예제에서 보이다시피 EntityManager에 대한 생성과 종료에 대한 코드가 없다. JEE 컨테이너는 @Transactional 애노테이션이 적용된 메서드를 트랜잭션 범위에서 실행한다. 즉 메소드가 실행될때 트랜잭션이 시작(begin)되고 메서드가 종료될 때 트랜잭션이 완료(commit)되는 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
@PersistenceContext EntityManager em;
 
@Transactional
public void withdraw(String email) {
    User user = em.find(User.class, email);
    if(user == null) {
        throw new UserNotFoundException();
    }
    
    em.remove(user);
}
cs



트랜잭션

컨테이너관리 엔티티매니저를 사용했을 때 트랜잭션에 대해 상세한 제어가 필요한 경우가 있다. 애플리케이션에서 관리하는 앤티티매니저의 경우는 개발자가 직접 트랜잭션을 제어할 수 있기 때문에 큰 어려움은 없겠지만 컨테이너가 관리하는 엔티티매니저의 경우는 전파방법에 대해 알아두어야 한다.

그전에 먼저 트랜잭션 종류에 대해 알아보자. JPA는 자원 로컬(Resource Local) 트랜잭션 타입과 JTA(Java Transaction Api) 타입의 두 가지 트랜잭션 타입을 지원한다. 트랜잭션 타입은 persistence.xml 파일의 transaction-type 속성값을 설정하면 된다. 

먼저 자원 로컬 트랜잭션은 JPA가 제공하는 EntityTransaction을 이용하는 방식이다. 아래의 코드는 JPA가 제공하는 자원 로컬 트랜잭션을 사용한 예시이다.
1
2
3
4
5
6
7
8
9
10
11
12
EntityManager em = emf.createEntityManager();
Transaction tx = em.createTransaction();
try {
    tx.begin();
    //...
    tx.commit();
catch(Exception e) {
    tx.rollback();
finally {
    em.close();
}
 
cs


다음은 JTA 트랜잭션 방식인데 JTA 트랜잭션을 사용하면 JPA에서 트랜잭션을 관리하지 않는다. 대신 EntityManager를 JTA 트랜잭션에 참여시켜 트랜잭션을 관리한다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UserTransaction utx = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
utx.begin(); // JTA 트랜잭션 시작
 
EntityManager em = emf.createEntityManager();
em.joinTransaction(); // JTA 트랜잭션에 참여
 
try {
    // ...
    utx.commit();
catch(Exception e) {
     utx.rollback();
finally {
    em.close()
}
 
cs


위의 코드에서 5라인의 joinTransaction()을 호출하지 않으면 엔티티의 변경이 있을 경우 실제 DB에 반영되지 않는다. 그 이유는 JPA는 트랜잭션 내에서의 변경만 DB에 반영하는데 JTA트랜잭션 방식을 사용하면 엔티티매니저는 트랜잭션에 관여하지 않고 엔티티매니저가 JTA 트랜잭션에 참여하지 않았으니 실제 DB에 반영되지 않는 것이다. 


엔티티매니저의 영속 컨텍스트 전파


  1. Enterprise급 개발에 필요한 모든 기술을 담고 있는 기술 (트랜잭션, JSP, 서블릿 등..)을 사용할 수 있는 환경 [본문으로]

'웹 개발 > JPA 프로그래밍' 카테고리의 다른 글

@Embeddable  (0) 2019.02.19
JPA 식별자 생성 방식  (0) 2019.01.29
엔티티(Entity)와 엔티티매니저(EntityManager)  (0) 2019.01.01
JPA란  (0) 2018.12.29

+ Recent posts