Spring JPA
在Spring包 org.springframework.orm.jpa中提供了对JPA的支持。
在Spring中设置JPA
Spring JPA提供了3张方式来设置JPA的 EntityManagerFactory,该类用来获取entity manager示例。
LocalEntityManagerFactoryBean
该方法通常用于比较简单的环境,如stand-alone或者测试应用程序。
LocalEntityManagerFactoryBean创建了一个EntityManagerFactory,其适用于只使用JPA来访问数据的应用程序。
该factory bean使用JPA PersistenceProvider的自动检测机制,并且需要指定persistence unit name;例如:
<beans>
<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="myPersistenceUnit"/>
</bean>
</beans>
该种形式的JPA使用是最简单同时也是受限最多的。不能应用JDBC的DataSource bean,不支持全局事务。而且需要特殊的JVM设置。
从JNDI获得一个EntityManagerFactory
该选项用于Java EE5 server。
XML配置如下:
<beans>
<jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>
</beans>
LocalContainerEntityManagerFactoryBean
该选项在基于Spring的应用程序中可以使用JPA的全部特性,例如使用在Tomcat或者stand-alone的应用程序中。
LocalContainerEntityManagerFactoryBean基于persistence.xml文件创建了一个PersistenceUnitInfo示例,其包含dataSourceLookUp strategy, loadTimeWeaver属性。其可以使用除JNDI之外的自定义data source,并控制织入过程。
典型的bean 定义如下:
<beans>
<bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="someDataSource"/>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
</bean>
</beans>
该使用方式可能同Java EE5 server内建的JPA功能相冲突,server会从JNDI获得EntityManagerFactory。Java EE5 server只会寻找默认的META-INF/persistence.xml文件,因此,在LocalContainerEntityManagerFactoryBean中指定一个自定以的persistenceXmlLocation值,如META-INF/my-persistence.xml,这将避免冲突。
load-time weaving
不是所有的JPA实现都要求一个JVM agent。Hibernate即不要求。如果使用的JPA实现不要求JVM agent或者有其他的替代方式,那么不应该使用load-time weaver。
定义多个persistence units
对于依赖与多个persistence units的应用程序,Spring提供了PersistenceUnitManager作为一个central repository,避免了persistence units的检测过程(该过程耗时)。默认的实现可以指定多个persistence units locations,解析并通过persistence unit name获取。
例如:
<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
<value>classpath:/my/package/**/custom-persistence.xml</value>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<property name="dataSources">
<map>
<entry key="localDataSource" value-ref="local-db"/>
<entry key="remoteDataSource" value-ref="remote-db"/>
</map>
</property>
<!-- if no datasource is specified, use this one -->
<property name="defaultDataSource" ref="remoteDataSource"/>
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="pum"/>
<property name="persistenceUnitName" value="myCustomUnit"/>
</bean>
使用JPA实现DAOs
EntityManagerFactory是线程安全的,但是EntityManager不是。
使用@PersistenceUnit注解EntityManagerFactory实现DAOs
注入的JPA EntityManager的表现同从application server's JDNI中得到的EntityManager一样。如果存在可用的,其会代理当前可用的EntityManager,否则会创建一个新的EntityManager。从而保证EntityManager使用的线程安全性。
Although |
通过使用注入EntityManagerFactory或者EntityManger,可以不依赖任何Spring特性而使用JPA。如果PersistenceAnnotationBeanPostProcessor被激活,Spring可以在field和method层次上使用@PersistenceUnit和@PersistenceContext。
使用@PersistenceUnit注解的原生JPA DAO实现如下:
public class ProductDaoImpl implements ProductDao {
private EntityManagerFactory emf;
@PersistenceUnit
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
public Collection loadProductsByCategory(String category) {
EntityManager em = this.emf.createEntityManager();
try {
Query query = em.createQuery("from Product as p where p.category = ?1");
query.setParameter(1, category);
return query.getResultList();
}
finally {
if (em != null) {
em.close();
}
}
}
}
<beans>
<!-- bean post-processor for JPA annotations -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
上述的DAO实现不依赖与Spring,并可用于Spring应用程序上下文。
另一种不显式定义PersistenceAnnotationBeanPostProcessor的方式是在application context配置文件中使用xml元素: context:annotation-config,
<beans>
<!-- post-processors for all standard config annotations -->
<context:annotation-config/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
* 使用@PersistenceContext注入EntityManager实现DAO
上述DAO实现的主要问题在于其总是会通过factory创建一个新的EntityManager。可以通过注入一个transactional EntityManager(而不是注入一个EntityManagerFactory)来避免上述问题,transactional EntityManager 也称为shared EntityManager,其是实际的transactional EntityManager的共享的,线程安全的代理。
public class ProductDaoImpl implements ProductDao {
@PersistenceContext
private EntityManager em;
public Collection loadProductsByCategory(String category) {
Query query = em.createQuery("from Product as p where p.category = :category");
query.setParameter("category", category);
return query.getResultList();
}
}
注入的EntityManager是由Spring来进行管理的。
使用该种DAO方式的优点在于其仅仅依赖于JPA API,没有引任何的Spring类。
事务管理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf"/>
</bean>
<bean id="myProductService" class="product.ProductServiceImpl">
<property name="productDao" ref="myProductDao"/>
</bean>
<aop:config>
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="myTxManager">
<tx:attributes>
<tx:method name="increasePrice*" propagation="REQUIRED"/>
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>
JpaDialect
JpaTemplate的一个高级特性是,JpaTransactionManager和AbstractEntityManagerFactoryBean的子类支持自定义的JpaDialect作为jpaDialect bean属性。在这种情况下,DAOs不会获取一个EntityManagerFactory应用,而是使用JpaTemplate实例。JpaTemplate实现可以利用一些Spring提供的高级特性:
- Applying specific transaction semantics such as custom isolation level or transaction timeout)
- Retrieving the transactional JDBC Connection for exposure to JDBC-based DAOs)
- Advanced translation of PersistenceExceptions to Spring DataAccessExceptions
默认的实现使用DefaultJpaDialect,不提供任何上述的能力。