?
Dieses Dokument verwendet PHP-Handbuch für chinesische Websites Freigeben
(org.springframework.orm.jpa
包下的)Spring JPA以類似整合Hibernate或者JDO的方式,
提供了對(duì) Java Persistence API
的全面支持,同時(shí)為提供附加的特性,必須了解底層的實(shí)現(xiàn)。
Spring JPA 提供了三種方法創(chuàng)建JPA EntityManagerFactory
:
LocalEntityManagerFactoryBean
負(fù)責(zé)創(chuàng)建一個(gè)適合于僅使用JPA進(jìn)行數(shù)據(jù)訪問(wèn)的環(huán)境的
EntityManager
。
Factory bean將使用JPA PersistenceProvider
類的自動(dòng)檢測(cè)機(jī)制(根據(jù)JPA的
Java SE啟動(dòng)),而在絕大多數(shù)情況下,只需要指定persistence unit名稱:
<beans> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="myPersistenceUnit"/> </bean> </beans>
這種JPA部署方式最為簡(jiǎn)單,但卻最受限制。例如,不能連接到現(xiàn)有的JDBCDataSource
,
并且不支持全局事務(wù)。甚至,持久化類的織入(字節(jié)碼轉(zhuǎn)換)也是特定于提供者的,經(jīng)常需要在啟動(dòng)時(shí)指定一個(gè)特定的JVM代理。
總之,這種方法實(shí)際上只適用于獨(dú)立的應(yīng)用程序和測(cè)試環(huán)境(這正是JPA規(guī)范設(shè)計(jì)它的原因)。
僅在簡(jiǎn)單部署環(huán)境中只使用這種方式,比如獨(dú)立的應(yīng)用程序和集成測(cè)試。
從JNDI獲取 EntityManagerFactory
(例如在Java EE 5環(huán)境中),僅通過(guò)修改XML配置即可實(shí)現(xiàn):
<beans> <jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/myPersistenceUnit"/> </beans>
在標(biāo)準(zhǔn)的Java EE 5啟動(dòng)過(guò)程中,Java EE服務(wù)器自動(dòng)檢測(cè)持久化單元(例如應(yīng)用程序文件包中的META-INF/persistence.xml
)
,以及Java EE部署描述符中定義給那些持久化單元命名上下文位置的環(huán)境的persistence-unit-ref
項(xiàng)(例如web.xml
)。
在這種情況下,整個(gè)持久化單元部署,包括持久化類的織入(字碼碼轉(zhuǎn)換)都取決于Java EE服務(wù)器。
JDBC DataSource
通過(guò)在META-INF/persistence.xml
文件中的JNDI位置進(jìn)行定義;EntityManager事務(wù)與服務(wù)器的JTA子系統(tǒng)整合。Spring僅僅用獲得的EntityManagerFactory
,
通過(guò)依賴注入將它傳遞給應(yīng)用程序?qū)ο?,并為它管理事?wù)(一般通過(guò)JtaTransactionManager
)。
注意,如果在同一個(gè)應(yīng)用程序中使用了多個(gè)持久化單元,JNDI獲取的這種持久化單元的bean名稱
應(yīng)該與應(yīng)用程序用來(lái)引用它們的持久化單元名稱相符(例如@PersistenceUnit
和
@PersistenceContext注解)。
在部署到Java EE 5服務(wù)器時(shí)使用該方法。關(guān)于如何將自定義JPA提供者部署到服務(wù)器,以及允許使用服務(wù)器提供的缺省提供者之外的JPA提供者,請(qǐng)查看服務(wù)器文檔的相關(guān)說(shuō)明。
LocalContainerEntityManagerFactoryBean
提供了對(duì)JPA EntityManagerFactory
的全面控制,非常適合那種需要細(xì)粒度定制的環(huán)境。LocalContainerEntityManagerFactoryBean
將基于
persistence.xml
文件創(chuàng)建 PersistenceUnitInfo
類,并提供 dataSourceLookup
策略和 loadTimeWeaver
。
因此它可以在JNDI之外的用戶定義的數(shù)據(jù)源之上工作,并控制織入流程。
<beans> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="someDataSource"/> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> </property> </bean> </beans>
這是最為強(qiáng)大的JPA配置方式,允許在應(yīng)用程序中靈活進(jìn)行本地配置。它支持連接現(xiàn)有JDBC DataSource
,
支持本地事務(wù)和全局事務(wù)等等。然而,它也將需求強(qiáng)加到了運(yùn)行時(shí)環(huán)境中,例如,如果持久化提供者需要字節(jié)碼轉(zhuǎn)換,則必須有織入ClassLoader的能力。
注意,這個(gè)選項(xiàng)可能與Java EE 5服務(wù)器內(nèi)建的JPA功能相沖突。因此,當(dāng)運(yùn)行在完全Java EE 5環(huán)境中時(shí),
要考慮從JNDI獲取EntityManagerFactory
。另一種可以替代的方法是,在
LocalContainerEntityManagerFactoryBean
定義中通過(guò)“persistenceXmlLocation”指定相關(guān)位置,
例如“META-INF/my-persistence.xml”,并且只將包含該名稱的描述符放在應(yīng)用程序包文件中。因?yàn)镴ava EE 5服務(wù)器將只
查找默認(rèn)的META-INF/persistence.xml
文件,它會(huì)忽略這種定制的持久化單元,因而避免與前面Spring
驅(qū)動(dòng)的JPA配置沖突。(例如,適用于Rdsin 3.1)。
在基于Spring的應(yīng)用程序環(huán)境中使用該方式可獲得全部JPA功能。這包括web容器,如Tomcat, 以及獨(dú)立的應(yīng)用程序和包含復(fù)雜持久化需求的集成測(cè)試。
LoadTimeWeaver
接口由Spring提供,允許JPA ClassTransformer
實(shí)例
能夠根據(jù)環(huán)境(web容器/應(yīng)用服務(wù)器)以特定的方式插入。
通過(guò)Java 5 代理掛鉤
ClassTransformers
經(jīng)常是無(wú)效的 ―― 代理通常在 整個(gè)虛擬機(jī) 環(huán)境下工作,并且監(jiān)控
每一個(gè) 被加載的類 ―― 這在生產(chǎn)環(huán)境下一般是不提倡的。
Spring提供了大量用于不同環(huán)境的 LoadTimeWeaver
實(shí)現(xiàn)類,
允許 ClassTransformer
實(shí)例能夠僅用于每個(gè)classloader ,而不是每個(gè)虛擬機(jī)。
接下來(lái)的一節(jié)將討論在Tomcat以及使用Spring的VM代理情況下的典型JPA織入配置。關(guān)于設(shè)置常用加載時(shí)織入的詳細(xì)內(nèi)容, 請(qǐng)參見(jiàn)AOP一章中的第?6.8.4.5?節(jié) “Spring配置”一節(jié),它涵蓋了Tomcat、VM代理以及WebLogic、OC4J、GlassFish和Resin。
Apache Tomcat 缺省的ClassLoader(類裝載器)并不支持類的切換,
但是它允許使用用戶自定義的類裝載器。Spring提供了 TomcatInstrumentableClassLoader
類
(在org.springframework.instrument.classloading.tomcat
包中),這個(gè)類繼承自Tomcat的類裝載器
(WebappClassLoader
)并且允許JPA ClassTransformer
的實(shí)例來(lái)“增強(qiáng)”所有由它加載的類。
簡(jiǎn)單說(shuō),JPA轉(zhuǎn)化器(JPA transformer)僅僅在(使用 TomcatInstrumentableClassLoader
的)特定web應(yīng)用程序中才能被使用。
為使用用戶自定義的類裝載器:
將 spring-tomcat-weaver.jar
復(fù)制到 $CATALINA_HOME/server/lib 下
(其中$CATALINA_HOME 表示Tomcat的安裝路徑)。
通過(guò)修改web application context使Tomcat使用用戶自定義的類裝載器(而不是默認(rèn)的類裝載器):
<Context path="/myWebApp" docBase="/my/webApp/location"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/> </Context>
Tomcat 5.0.x 和 5.5.x 系列支持多個(gè)上下文路徑(context locations): 服務(wù)器配置文件($CATALINA_HOME/conf/server.xml), 默認(rèn)的上下文配置($CATALINA_HOME/conf/context.xml)會(huì)影響所有被部署的web應(yīng)用程序、 單獨(dú)部署在Server端的web應(yīng)用程序的配置($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml) 或者與web應(yīng)用程序一起(your-webapp.war/META-INF/context.xml)。從效率的角度說(shuō), 我們推薦在web-app的內(nèi)部配置的方式,因?yàn)閮H僅使用JPA的應(yīng)用程序會(huì)使用用戶自定義的類裝載器。 更多具體有關(guān)可用的上下文路徑的內(nèi)容請(qǐng)參見(jiàn)Tomcat 5.x的文檔。
注意,5.5.20之前的版本有一個(gè)XML配置解析的bug,造成 server.xml
中無(wú)法使用Loader
標(biāo)簽,無(wú)論是否指定了classloader,也不管這個(gè)classloader是官方的還是自定義的。
如果你正在使用的是Tomcat 5.5.20以上的版本,就可以將useSystemClassLoaderAsParent設(shè)置成
false
來(lái)解決這個(gè)問(wèn)題:
<Context path="/myWebApp" docBase="/my/webApp/location"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" useSystemClassLoaderAsParent="false"/> </Context>
將spring-tomcat-weaver.jar
復(fù)制到$CATALINA_HOME/lib (where
$CATALINA_HOME表示Tomcat安裝根目錄的位置)。
通過(guò)編輯web應(yīng)用程序上下文文件,使Tomcat使用自定義的ClassLoader(而不是默認(rèn)的ClassLoader):
<Context path="/myWebApp" docBase="/my/webApp/location"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/> </Context>
Tomcat 6.0.x (類似于5.0.x/5.5.x)系列支持幾種上下文路徑:(context locations): 服務(wù)器配置文件($CATALINA_HOME/conf/server.xml), 默認(rèn)的上下文配置($CATALINA_HOME/conf/context.xml)會(huì)影響所有被部署的web應(yīng)用程序、 單獨(dú)部署在Server端的web應(yīng)用程序的配置($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml) 或者與web應(yīng)用程序一起(your-webapp.war/META-INF/context.xml)。從效率的角度說(shuō), 我們推薦在web-app的內(nèi)部配置的方式,因?yàn)閮H僅使用JPA的應(yīng)用程序會(huì)使用用戶自定義的類裝載器。 更多具體有關(guān)可用的上下文路徑的內(nèi)容請(qǐng)參見(jiàn)Tomcat 5.x documentation。
Tomcat 5.0.x/5.5.x
Tomcat 6.0.x
所有Tomcat版本所需的最后一步,是在配置LocalContainerEntityManagerFactoryBean
的時(shí),使用
相應(yīng)的LoadTimeWeaver
:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/> </property> </bean>
利用這種方法,依賴工具的JPA應(yīng)用程序無(wú)需代理就可以在Tomcat上運(yùn)行。這在宿主應(yīng)用程序依賴不同的JPA實(shí)現(xiàn)時(shí)尤為重要, 因?yàn)镴PA轉(zhuǎn)化器只適用于ClassLoader級(jí)別,它們之間是彼此隔離的。
如果Tomcat使用TopLink作為JPA提供者,請(qǐng)將核心的toplink jar包放在$CATALINA_HOME/shared/lib文件夾中,而不再放到war中。
對(duì)于需要類工具,同時(shí)現(xiàn)有的LoadTimeWeaver實(shí)現(xiàn)不提供這種支持的環(huán)境,JDK代理是唯一的解決方案。對(duì)于這種情況,Spring提供了
需要特定于Spring(但非常常用)的VM代理(spring-agent.jar
)的InstrumentationLoadTimeWeaver
:
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="loadTimeWeaver"> <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/> </property> </bean>
請(qǐng)注意在啟動(dòng)虛擬機(jī)時(shí),同時(shí)啟動(dòng)Spring代理,方法是提供下列JVM選項(xiàng):
-javaagent:/path/to/spring-agent.jar
自Spring 2.5,可以使用context:load-time-weaver
元素來(lái)配置
上下文范圍的LoadTimeWeaver
了。這種“全局”織入器由所有JPA
LocalContainerEntityManagerFactoryBeans
自動(dòng)揀選。
這是配置加載時(shí)織入器的推薦方法,提供平臺(tái)(ebLogic, OC4J, GlassFish, Tomcat, Resin, VM agent)的自動(dòng)檢測(cè),以及織入器到所有織入器知道的bean的自動(dòng)傳播。
<context:load-time-weaver/> <bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> ... </bean>
關(guān)于如何建立常用加載時(shí)織入的詳細(xì)內(nèi)容,請(qǐng)參見(jiàn)第?6.8.4.5?節(jié) “Spring配置”一節(jié),它涵蓋了 Tomcat、VM代理,以及WebLogic, OC4J, GlassFish和Resin。
對(duì)于那些依靠多個(gè)持久化單元位置(例如存放在classpath中的多個(gè)jar中)的應(yīng)用程序,
Spring提供了作為中央倉(cāng)庫(kù)的PersistenceUnitManager
,
避免了持久化單元查找過(guò)程(的潛在開(kāi)銷)。缺省實(shí)現(xiàn)允許指定多個(gè)位置
(默認(rèn)情況下classpath會(huì)搜索META-INF/persistence.xml文件),它們會(huì)被解析然后通過(guò)持久化單元名稱被獲?。?/p>
<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocation">
<list>
<value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
<value>classpath:/my/packagecustom-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"/>
</bean>
要注意的是,缺省實(shí)現(xiàn)允許在將持久化單元信息傳入JPA provider之前用
PersistenceUnitPostProcessor
(它允許選擇持久化單元)修改它們,
傳入的過(guò)程可以是通過(guò)屬性聲明式地傳入(影響其中所有的單元)或編程式地傳入。
如果沒(méi)有指定persistenceUnitManager,LocalContainerEntityManagerFactoryBean
會(huì)創(chuàng)建一個(gè)并在內(nèi)部使用它。
每個(gè)基于JPA的DAO都將通過(guò)依賴注入接收一個(gè) EntityManagerFactory
實(shí)例。
這樣的DAO可以通過(guò)指定的 EntityManagerFactory
來(lái)操作原生JPA的API進(jìn)行數(shù)據(jù)訪問(wèn),
也可以直接使用Spring的 JpaTemplate
:
<beans> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> </beans>
public class JpaProductDao implements ProductDao {
private JpaTemplate jpaTemplate;
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.jpaTemplate = new JpaTemplate(emf);
}
public Collection loadProductsByCategory(final String category) throws DataAccessException {
return (Collection) this.jpaTemplate.execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
Query query = em.createQuery("from Product as p where p.category = :category");
query.setParameter("category", category);
List result = query.getResultList();
// do some further processing with the result list
return result;
}
});
}
}
JpaCallback
實(shí)現(xiàn)允許任意類型的JPA數(shù)據(jù)訪問(wèn)。
JpaTemplate
將確保 EntityManager
正確地打開(kāi)和關(guān)閉,并且能夠自動(dòng)地參與到事務(wù)中去。
除此之外,JpaTemplate
能夠恰當(dāng)?shù)靥幚懋惓#_保資源的及時(shí)清理以及必要時(shí)的事務(wù)回滾。
Template實(shí)例不僅是線程安全的,而且它是可重用的,因而它能夠作為實(shí)例變量被一個(gè)類持有。
注意, JpaTemplate
提供了簡(jiǎn)單的諸如find、load、merge等操作的快捷函數(shù)來(lái)替代默認(rèn)的回調(diào)實(shí)現(xiàn)。
不僅如此,Spring還提供了一個(gè)方便的 JpaDaoSupport
基類,提供了
get/setEntityManagerFactory
方法以及 getJpaTemplate()
方法供子類調(diào)用:
public class ProductDaoImpl extends JpaDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException { Map<String, String> params = new HashMap<String, String>(); params.put("category", category); return getJpaTemplate().findByNamedParams("from Product as p where p.category = :category", params); } }
除了直接使用Spring的 JpaTemplate
之外,也可以使用原生JPA的API來(lái)實(shí)現(xiàn)基于Spring的DAO,
此時(shí)你需要自行明確地處理EntityManager
。正如在相應(yīng)的Hibernate章節(jié)描述的一樣,這種做法的主要優(yōu)
點(diǎn)在于你的數(shù)據(jù)訪問(wèn)代碼可以在整個(gè)過(guò)程中拋出checked exceptions。JpaDaoSupport
為這種情況提
供了多種函數(shù)支持,包括獲取和釋放一個(gè)具備事務(wù)管理功能的 EntityManager
和相關(guān)的異常轉(zhuǎn)化。
JpaTemplate主要以JdoTemplate和HibernateTemplate的同胞而存在,為使用它的人們提供相同的風(fēng)格。
對(duì)于新啟動(dòng)的項(xiàng)目,要考慮采用原生的JPA風(fēng)格編寫(xiě)數(shù)據(jù)訪問(wèn)對(duì)象的代碼,基于通過(guò)JPA@PersistenceContext
注解
獲取的一個(gè)“共享EntityManager”引用(利用Spring的PersistenceAnnotationBeanPostProcessor
;詳情如下。)
雖然EntityManagerFactory
實(shí)例是線程安全的,
但EntityManager
實(shí)例確實(shí)不是這樣。被注入的JPA
EntityManager
的行為和從應(yīng)用服務(wù)器JNDI環(huán)境中獲取的沒(méi)有區(qū)別,
和JPA規(guī)范定義的一樣。如果存在一個(gè)被注入的JPA EntityManager
,
它會(huì)代理對(duì)當(dāng)前事務(wù)化的EntityManager
的所有調(diào)用;
否則每個(gè)操作都會(huì)創(chuàng)建一個(gè)EntityManager
,確保線程安全。
你完全可以使用原生的JPA的API進(jìn)行編程,而無(wú)需對(duì)Spring產(chǎn)生任何依賴,這可以通過(guò)一個(gè)被注入的
EntityManagerFactory
或 EntityManager
來(lái)完成。
注意,如果 PersistenceAnnotationBeanPostProcessor
被啟用,Spring就能夠識(shí)別字段或者方法級(jí)別的 @PersistenceUnit
和
@PersistenceContext
注解。相應(yīng)的DAO實(shí)現(xiàn)看起來(lái)像這樣:
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不對(duì)Spring產(chǎn)生任何依賴,而它就如同使用Spring的 JpaTemplate
那樣,仍然非常適合
在Spring的application context中進(jìn)行配置。此外,這樣的DAO可以利用注解來(lái)要求缺省EntityManagerFactory
的注入:
<beans>
<!-- bean post-processor for JPA annotations -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
注意:作為顯式定義PersistenceAnnotationBeanPostProcessor
的另一種方法,
可以考慮在應(yīng)用程序上下文配置中使用Spring 2.5的context:annotation-config
XML元素。
它會(huì)自動(dòng)為基于注解的配置注冊(cè)Spring所有標(biāo)準(zhǔn)的后置處理器(包括CommonAnnotationBeanPostProcessor
等等)。
<beans>
<!-- post-processors for all standard config annotations -->
<context:annotation-config/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
這類DAO的主要問(wèn)題在于每次總是從工廠中獲取一個(gè)新的 EntityManager
實(shí)例。
這一點(diǎn)可以通過(guò)對(duì) EntityManager
而不是factory進(jìn)行注入來(lái)解決:
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
注解有一個(gè)可選的屬性type
,它的默認(rèn)值是
PersistenceContextType.TRANSACTION
。該默認(rèn)需要你接收一個(gè)“共享EntityManager”代理。
另一種方式,PersistenceContextType.EXTENDED
則完全不同:它會(huì)產(chǎn)生一個(gè)所謂的“擴(kuò)展EntityManager”,
這個(gè)不是線程安全的,因此不應(yīng)該用在當(dāng)前被訪問(wèn)的組件中,例如Spring管理的singleton bean。
擴(kuò)展的EntityManager只能用在有狀態(tài)的組件中,例如,在會(huì)話中,EntityManager的生命周期與當(dāng)前事務(wù)無(wú)關(guān),完全取決于應(yīng)用程序。
被注入的 EntityManager
由Spring管理(能夠感知當(dāng)前的事務(wù))。
值得注意的是,即使這種新的實(shí)現(xiàn)更傾向于方法級(jí)別的注入(使用 EntityManager
而不是 EntityManagerFactory
),
對(duì)于注解的使用,application context的XML配置則無(wú)需任何改變。
這種DAO風(fēng)格的主要優(yōu)點(diǎn)在于它僅僅依賴JPA,而無(wú)需依賴任何的Spring的類。除此之外,由于JPA的注解可被理解,注入能夠被Spring容器自動(dòng)應(yīng)用。 從無(wú)入侵性的角度來(lái)說(shuō),這一點(diǎn)非常有吸引力,它對(duì)于JPA開(kāi)發(fā)者來(lái)說(shuō)也更自然。
然而,DAO不僅會(huì)拋出普通的 PersistenceException
異常(這是一個(gè)無(wú)需聲明和捕獲的unchecked exception),
還會(huì)拋出諸如 IllegalArgumentException
和 IllegalStateException
之類的異常。
這意味著,DAO的調(diào)用者只能以普通的錯(cuò)誤來(lái)處理這些異常,除非完全依賴JPA自身的異常體系。因而,除非你將DAO的調(diào)用者綁定到具體的實(shí)現(xiàn)策略上去,
否則將無(wú)法捕獲特定的異常原因(諸如樂(lè)觀鎖異常)。這種折中平衡或許可以被接受,如果你的應(yīng)用完全基于JPA或者無(wú)需進(jìn)行特殊的異常處理。不過(guò),
Spring提供了一個(gè)允許你進(jìn)行透明的異常轉(zhuǎn)化的解決方案:通過(guò)使用 @Repository
注解:
@Repository
public class ProductDaoImpl implements ProductDao {
// class body here...
}
<beans>
<!-- Exception
translation bean post processor -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="myProductDao" class="product.ProductDaoImpl"/>
</beans>
后置處理器將自動(dòng)地尋找所有的異常轉(zhuǎn)化器(PersistenceExceptionTranslator
接口的實(shí)現(xiàn)),
并通知所有標(biāo)有 @Repository
注解的bean,從而能夠使得被找到的異常轉(zhuǎn)化器能夠在拋出異常時(shí)進(jìn)行相應(yīng)的異常轉(zhuǎn)化工作。
總之:DAO能夠基于普通的Java持久層API和注解來(lái)實(shí)現(xiàn),但同樣也能享受到由Spring管理事務(wù)、IoC和透明的異常轉(zhuǎn)化 (轉(zhuǎn)化成為Spring的異常體系)等好處。