《Spring 5 官方文檔》16.ORM和數據訪問(三)
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環境設置。
最後更新:2017-05-18 10:33:01