본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] Spring이 제공하는 JPA

by 김코더 김주역 2022. 7. 2.
반응형

이 포스팅을 읽고 이해하려면 EntityManager, EntityManagerFactory 그리고 persistence.xml에 대한 어느 정도 기초 지식이 필요하다. 이전에 필자가 정리한 포스팅을 참고하길 바란다.

https://kimcoder.tistory.com/355

 

[JPA] EntityManager / 트랜잭션 간단 예제

이번 포스팅에서는 실제로 JPA를 이용하여 간단한 트랜잭션 체계를 만들어볼 것이다. 1. EntityManager와 EntityManagerFactory 예제에 들어가기 전에 알아둬야 할 클래스들이다. 1) EntityManager (1) 특징 - Ent..

kimcoder.tistory.com

 

 

1. LocalContainerEntityManagerFactoryBean

- Spring이 제공하는 EntityManager을 생성하기 위한 EntityManagerFactory를 만들어주는 클래스

- 이 방법을 사용하면 컨테이너에서 동작하는 JPA의 기능을 활용할 수 있고, 스프링이 제공하는 데이터 액세스 기술과 JPA 확장 기능까지 활용할 수 있게 된다.

- DB 연결 정보는 DataSource로 대체하면 되므로 persistence에 등록하지 않아도 된다.

 

1) Bean 생성

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

 

 

2) 프로퍼티

dataSource 외에도 다음과 같은 property들을 추가할 수 있다.

 

(1) persistenceUnitName

- 사용할 persistence unit의 이름을 지정한다.

- persistence.xml에 정의되어 있는 <persistence-unit>의 name 속성에서 확인할 수 있다.

<property name="persistenceUnitName" value="myPersistenceUnit" />

 

(2) persistenceXmlLocation

- LocalContainerEntityManagerFactoryBean이 persistence.xml 파일을 찾는 경로를 바꿀 때 사용한다.

- 일부 WAS에서도 persistence.xml의 디폴트 경로를 META-INF/persistence.xml로 인식하기 때문에 서버에서 불필요한 EntityManagerFactory가 같이 생성되어 충돌이 발생할 수 있으며, 이런 문제를 회피할 때 유용하다.

<property name="persistenceXmlLocation" value="META-INF/spring-persistence.xml />

 

(3) jpaProperties, jpaPropertyMap

- EntityManagerFactory를 위한 프로퍼티를 지정한다.

- persistence.xml의 <properties> 태그에 <property>를 추가하는 방법을 대신할 수 있다.

- jpaProperties는 <props>, <prop> 태그를 이용하여 프로퍼티 정보를 추가할 수 있고, jpaPropertyMap는 <map>, <entry> 태그를 이용하여 프로퍼티 정보를 추가할 수 있다.

<property name="jpaProperties">
    <props>
        <prop key="hibernate.dialect">${hibernate.dialect}</prop>
        <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
        <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
        <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
    </props>
</property>

 

(4) jpaVendorAdapter

- JPA 옵션을 지정하는 방식은 각 JPA 구현 벤더의 제품(Hibernate, EclipseLink 등)별로 다르게 지정되어 있는데, Spring이 정의해주는 JPA 설정 표준 프로퍼티를 사용하고 싶을 때 사용한다.

- 각 JPA 벤더별로 만들어진 전용 어댑터를 사용하면 된다. Spring은 EclipseLink, Hibernate 등의 벤더를 위한 jpaVendorAdapter을 지원한다.

- 예를 들어, EclipseLink에서 SQL을 로그로 출력하고 싶을 때에는 다음과 같이 설정하면 된다.

<property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
        <property name="showSql" value="true" />
    </bean>
</property>

 

(5) loadTimeWeaver

- JPA 벤더에 종속적이지 않은 방식으로 loadtime weaving 기능을 적용할 수 있다.

※ Loadtime weaving : 런타임 시에 클래스를 로딩하면서 기능을 추가하는 것

- 전체 가상머신에 있는 클래스를 일일이 다 확인하는 비효율적인 방식을 대신하기 위해, Spring은 클래스 로더를 이용한 loadtime weaving 방식을 제공한다.

- lib 폴더에 org.springframework.instrument-3.0.7.RELEASE.jar 파일이 들어있다면 JVM 기동 옵션과 loadTimeWeaver 설정을 아래와 같이 진행한다.

-javaagent:lib/org.springframework.instrument-3.0.7.RELEASE.jar

 

<property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>

 

(6) packagesToScan

- JPA Entity 클래스가 담긴 패키지를 지정하여 스캐닝 대상으로 설정한다. 이러한 클래스 스캐닝 기술까지 적용하면 persistence.xml을 아예 생략할 수도 있다.

<property name="packagesToScan" value="com.example.demo.spring31.jpa />

 

 

 

2. TransactionManager

- Spring의 EntityManager를 사용하기 위해 트랜잭션 매니저가 등록되어 있어야 한다. JPA는 반드시 트랜잭션 내에서 동작하도록 설계되어 있기 때문이다.

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    ...
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="emf" />
</bean>

- @Transactional 어노테이션이나 트랜잭션 AOP를 적용해주면 자동으로 트랜잭션이 적용된다.

- 같은 DataSource를 공유하는 JDBC DAO와 트랜잭션을 공유할 수도 있다.

- JPA가 JTA 트랜잭션을 이용하는 경우에는 JtaTransactionManager을 사용해야 한다.

 

 

 

3. JpaTemplate

- 템플릿 방식으로 JPA 코드를 작성할 수 있게 해준다.

- JpaTemplate 객체는 다음과 같이 생성한다.

private JpaTemplate jpaTemplate;

@Autowired
public void init(EntityManagerFactory emf) {
    jpaTemplate=new JpaTemplate(emf);
}

※ JpaDaoSupport 클래스를 상속해서 DAO를 만드는 경우에는 JpaTemplate을 직접 생성하는 대신 getJpaTemplate() 메소드로 JpaTemplate을 가져올 수 있다.

- JpaTemplate을 사용하는 방법에는 실행 메소드에 JpaCallback 객체를 전달하는 방법도 있지만 대부분 JpaTemplate이 제공하는 일반적인 메소드들로 충분하다.

- JPA의 예외를 spring의 데이터 엑세스 예외 추상화 클래스인 DataAccessException 타입의 예외로 변환해준다. 그런데 JPA도 런타임 예외를 발생시키긴 한다.

- Spring 3.1에서 @deprecated가 추가됐으며, JpaTemplate은 더 이상 사용이 권장되지 않는다.

 

 

 

4. EntityManager

- JpaTemplate이 아니어도 EntityManager을 이용하여 JPA의 모든 기능을 이용할 수 있다. 사실, JpaTemplate은 DAO 작성에 자주 사용되지는 않는다.

 

1) 애플리케이션이 관리하는 EntityManager의 생성

- 등록된 EntityManagerFactory bean의 createEntityManager() 메소드를 통해 가져올 수 있다. 이 때, EntityManagerFactory는 @Autowired를 통해 주입 받거나, 다음과 같이 javax.persistence 패키지의 @PersistenceUnit 어노테이션을 통해 주입 받는다.

@PersistenceUnit EntityManagerFactory emf;

※ @PersistenceUnit 방식은 spring에 대한 의존도가 없다.

- 자주 쓰이지는 않는 방법이다. EntityManagerFactory 인터페이스에서 제공하는 기능이 필요할 때만 사용하는 것이 좋다.

 

 

2) 컨테이너가 관리하는 EntityManager의 생성

- JPA 코드를 작성하는 대표적인 방법으로, JavaEE 컨테이너가 관리하는 EntityManager을 주입받을 수 있다.

- @PersistenceContext 어노테이션을 이용하여, 등록된 EntityManagerFactory bean으로부터 EntityManager를 주입 받는다.

@PersistenceContext EntityManager em;

※ @PersistenceContext를 통해 주입 받는 EntityManager는 하나의 오브젝트를 공유하는 듯 보이지만, 실제로는 현재 진행중인 트랜잭션에 연결시켜주는 프록시로, 트랜잭션마다 다른 EntityManager 오브젝트를 사용한다.

※ EntityManager의 type 요소는 기본적으로 PersistenceContextType.TRANSACTION이다. type을 Persistence.EXTENDED로 변경하면 사용자별로 독립적으로 장기간 보존되는 session 빈으로 생성된다. 물론, 여전히 싱글톤으로는 사용 불가능하다.

@PersistenceContext(type=PersistenceContextType.TRANSACTION) EntityManager em; // type을 생략해도 동일!
@PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

 

 

 

5. JPA 예외 변환 AOP

- Spring의 AOP를 이용하여 JPA 예외를 Spring 프레임워크가 추상화한 DataAccessException 계열의 예외로 변환할 수 있다.

- 이 방법을 이용하려면 예외 변환이 필요한 DAO 클래스에 @Repository 어노테이션을 붙여야 하고, 이 @Repository 클래스에 AOP Advice를 적용해주기 위한 후처리기를 다음과 같이 bean으로 등록해야 한다.

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

  - JPA의 예외는 분류하기 힘들기 때문에 다양한 DataAccessException의 서브클래스로 매핑되어서 던져지지는 못한다. JPA 스펙이 정의하는 예외는 제한적이다.

 

 

 

● 참고 자료 : 토비의 스프링 3.1 Vol.2 

 

반응형

'Spring Series > Spring Framework' 카테고리의 다른 글

[Spring] @Transactional의 속성  (0) 2022.07.09
[Spring] Hibernate  (0) 2022.07.06
[Spring] 더 편리해진 Spring JDBC  (0) 2022.06.29
[Spring] .gitignore 적용하기  (0) 2022.06.29
[Spring] Bean의 id와 name  (0) 2022.06.23

댓글