16.4.2 基于JPA的EntityManagerFactory和EntityManager来实现DAO
虽然
EntityManagerFactory
实例是线程安全的,但EntityManager
实例不是。注入的JPAEntityManager
的行为类似于从JPA Spec中定义的应用程序服务器的JNDI环境中提取的EntityManager
。它将所有调用委托给当前事务的EntityManager
(如果有);否则,它每个操作返回的都是新创建的EntityManager
,通过使用不同的EntityManager
来保证使用时的线程安全。
通过注入的方式使用EntityManagerFactory
或EntityManager
来编写JPA代码,是不需要依赖任何Spring定义的类的。如果启用了PersistenceAnnotationBeanPostProcessor
,Spring可以在实例级别和方法级别识别@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();
}
}
}
}
上面的DAO对Spring的实现是没有任何依赖的,而且很适合与Spring的应用程序上下文进行集成。而且,DAO还可以通过注解来注入默认的EntityManagerFactory
:
<beans>
<!-- bean post-processor for JPA annotations -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
如果不想明确定义PersistenceAnnotationBeanPostProcessor
,可以考虑在应用程序上下文配置中使用Spring上下文annotation-config
XML元素。这样做会自动注册所有Spring标准后置处理器,用于初始化基于注解的配置,包括CommonAnnotationBeanPostProcessor
等。
<beans>
<!-- post-processors for all standard config annotations -->
<context:annotation-config/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
这样的DAO的主要问题是它总是通过工厂创建一个新的EntityManager
。开发者可以通过请求事务性EntityManager
(也称为共享EntityManager,因为它是实际的事务性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();
}
}
@PersistenceContext
注解具有可选的属性类型,默认值为PersistenceContextType.TRANSACTION
。此默认值是开发者所需要接收共享的EntityManager
代理。替代方案PersistenceContextType.EXTENDED
则完全不同:该方案会返回一个所谓扩展的EntityManager
,该EntityManager
不是线程安全的,因此不能在并发访问的组件(如Spring管理的单例Bean)中使用。扩展实体管理器仅应用于状态组件中,比如持有会话的组件,其中EntityManager
的生命周期与当前事务无关,而是完全取决于应用程序。
方法和实例变量级别注入
指示依赖注入(例如
@PersistenceUnit
和@PersistenceContext
)的注解可以应用于类中的实例变量或方法,也就是表达式方法级注入和实例变量级注入。实例变量级注释简洁易用,而方法级别允许进一步处理注入的依赖关系。在这两种情况下,成员的可见性(public
,protected
,private
)并不重要。类级注解怎么办?
在J2EE平台上,它们用于依赖关系声明,而不是资源注入。
注入的EntityManager
是由Spring管理的(Spring可以意识到正在进行的事务)。重要的是要注意,因为通过注解进行注入,即使新的DAO实现使用通过方法注入的EntityManager
而不是EntityManagerFactory
的注入的,在应用程序上下文XML中不需要进行任何修改。
这种DAO风格的主要优点是它只依赖于Java Persistence API;不需要导入任何Spring的实现类。而且,Spring容器可以识别JPA注解来实现自动的注入和管理。从非侵入的角度来看,这种风格对JPA开发者来说可能更为自然。
16.4.3 Spring驱动的JPA事务
如果开发者还没有阅读声明式事务管理,强烈建议开发者先行阅读,这样可以更详细地了解Spring的对声明式事务支持。
JPA的推荐策略是通过JPA的本地事务支持的本地事务。 Spring的JpaTransactionManager
提供了许多来自本地JDBC事务的功能,例如针对任何常规JDBC连接池(不需要XA要求)指定事务的隔离级别和资源级只读优化等。
Spring JPA还允许配置JpaTransactionManager
将JPA事务暴露给访问同一DataSource
的JDBC访问代码,前提是注册的JpaDialect
支持检索底层JDBC连接。Spring为EclipseLink和Hibernate JPA实现提供了实现。有关JpaDialect
机制的详细信息,请参阅下一节。
16.4.4 JpaDialect和JpaVendorAdapter
作为高级功能,JpaTransactionManager
和AbstractEntityManagerFactoryBean
的子类支持自定义JpaDialect
,将其作为Bean传递给jpaDialect
属性。JpaDialect
实现可以以供应商特定的方式使能Spring支持的一些高级功能:
- 应用特定的事务语义,如自定义隔离级别或事务超时
- 为基于JDBC的DAO导出事务性JDBC连接
- 从
PersistenceExceptions
到SpringDataAccessExceptions
的异常转义
这对于特殊的事务语义和异常的高级翻译特别有价值。但是Spring使用的默认实现(DefaultJpaDialect
)是不提供任何特殊功能的。如果需要上述功能,则必须指定适当的方言才可以。
作为一个更广泛的供应商适应设施,主要用于Spring的全功能
LocalContainerEntityManagerFactoryBean
设置,JpaVendorAdapter
将JpaDialect
的功能与其他提供者特定的默认设置相结合。指定HibernateJpaVendorAdapter
或EclipseLinkJpaVendorAdapter
是分别为Hibernate或EclipseLink自动配置EntityManagerFactory
设置的最简单方便的方法。但是请注意,这些提供程序适配器主要是为了与Spring驱动的事务管理一起使用而设计的,即为了与JpaTransactionManager
配合使用的。
有关其操作的更多详细信息以及在Spring的JPA支持中如何使用,请参阅JpaDialect
和JpaVendorAdapter
的Javadoc。
16.4.5 为JPA配置JTA事务管理
作为JpaTransactionManager
的替代方案,Spring还允许通过JTA在J2EE环境中或与独立的事务协调器(如Atomikos)进行多资源事务协调。除了用Spring的JtaTransactionManager
替换JpaTransactionManager
,还有需要以下一些操作:
- 底层JDBC连接池是需要具备XA功能,并与开发者的事务协调器集成的。这在J2EE环境中很简单,只需通过JNDI导出不同类型的
DataSource
即可。有关导出DataSource
等详细信息,可以参考应用服务器文档。类似地,独立的事务协调器通常带有特殊的XA集成的DataSource
实现。 - 需要为JTA配置JPA
EntityManagerFactory
。这是特定于提供程序的,通常通过在LocalContainerEntityManagerFactoryBean
的特殊属性指定为”jpaProperties”。在使用Hibernate的情况下,这些属性甚至是需要基于特定的版本的;请查阅Hibernate文档以获取详细信息。 - Spring的
HibernateJpaVendorAdapter
会强制执行某些面向Spring的默认设置,例如在Hibernate 5.0中匹配Hibernate自己的默认值的连接释放模式“on-close”,但在5.1 / 5.2中不再存在。对于JTA设置,不要声明HibernateJpaVendorAdapter
开始,或关闭其prepareConnection
标志。或者,将Hibernate 5.2的hibernate.connection.handling_mode
属性设置为DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT
以恢复Hibernate自己的默认值。有关WebLogic的相关说明,请参考Hibernate的虚假应用服务器警告一节。 - 或者,可以考虑从应用程序服务器本身获取
EntityManagerFactory
,即通过JNDI查找而不是本地声明的LocalContainerEntityManagerFactoryBean
。服务器提供的EntityManagerFactory
可能需要在服务器配置中进行特殊定义,减少了部署的移植性,但是EntityManagerFactory
将为开箱即用的服务器JTA环境设置。