이 포스팅을 읽고 이해하려면 EntityManager, EntityManagerFactory 그리고 persistence.xml에 대한 어느 정도 기초 지식이 필요하다. 이전에 필자가 정리한 포스팅을 참고하길 바란다.
https://kimcoder.tistory.com/355
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 |
댓글