?
Dokumen ini menggunakan Manual laman web PHP Cina Lepaskan
Spring從Spring 1.2版本開(kāi)始支持Oracle TopLink (http://www.oracle.com/technology/products/ias/toplink)
作為數(shù)據(jù)訪問(wèn)策略,同樣遵循類似于Spring對(duì)Hibernate的支持風(fēng)格。Spring對(duì)其的支持包括
TopLink 9.0.4(Spring 1.2支持的產(chǎn)品版本)和TopLink 10.1.3(Spring 1.2支持的,依然處于beta版)。
對(duì)應(yīng)的支持與整合類位于 org.springframework.orm.toplink
包中。
TopLink支持由Spring和Oracle TopLink團(tuán)隊(duì)共同開(kāi)發(fā)。 非常感謝TopLink團(tuán)隊(duì),尤其是Jim Clark,替我們闡明了所有方面的細(xì)節(jié)!
TopLink本身并沒(méi)有提供SessionFactory抽象層邏輯,多線程的數(shù)據(jù)訪問(wèn)是建立在中央
ServerSession
上的。對(duì)于單線程訪問(wèn),
這個(gè)中央 ServerSession
會(huì)為它一個(gè)
ClientSession
的實(shí)例供其使用。為了提供靈活便捷的創(chuàng)建選項(xiàng),
Spring為TopLink定義了一個(gè) SessionFactory
接口,
從而使你可以任意地在不同的 Session
創(chuàng)建策略之間進(jìn)行切換。
作為一個(gè)一站式的商店,Spring提供了一個(gè) LocalSessionFactoryBean
類,
允許你以bean風(fēng)格的配置方式來(lái)定義一個(gè)TopLink的 SessionFactory
。
需要進(jìn)行配置的地方主要是TopLink session配置文件,通常來(lái)說(shuō)還需配置一個(gè)受到Spring管理的JDBC
DataSource
。
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="mySessionFactory" class="org.springframework.orm.toplink.LocalSessionFactoryBean"> <property name="configLocation" value="toplink-sessions.xml"/> <property name="dataSource" ref="dataSource"/> </bean> </beans>
<toplink-configuration> <session> <name>Session</name> <project-xml>toplink-mappings.xml</project-xml> <session-type> <server-session/> </session-type> <enable-logging>true</enable-logging> <logging-options/> </session> </toplink-configuration>
通常情況下,LocalSessionFactoryBean
在底層將持有一個(gè)多線程的TopLink
ServerSession
并創(chuàng)建合適的客戶端 Session
:
它或者是一個(gè)普通的 Session
(典型情況) ―― 一個(gè)受管理的 ClientSession
;
或者是一個(gè)具備事務(wù)功能的 Session
(后者主要在Spring內(nèi)部對(duì)TopLink的支持中被使用)。
還有一種情況,LocalSessionFactoryBean
可能會(huì)持有一個(gè)單線程的TopLink的
DatabaseSession
,這是非常特殊的情況了。
每個(gè)基于TopLink的DAO將通過(guò)IoC被注入一個(gè) SessionFactory
,
你可以通過(guò)Setter方式注入,也可以用構(gòu)造函數(shù)方式注入。這樣的DAO可以直接操作原生的TopLink API,通過(guò)
SessionFactory
來(lái)獲取一個(gè) Session
,
但是通常情況下,你更愿意使用Spring的 TopLinkTemplate
:
<beans> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> </beans>
public class TopLinkProductDao implements ProductDao {
private TopLinkTemplate tlTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.tlTemplate = new TopLinkTemplate(sessionFactory);
}
public Collection loadProductsByCategory(final String category) throws DataAccessException {
return (Collection) this.tlTemplate.execute(new TopLinkCallback() {
public Object doInTopLink(Session session) throws TopLinkException {
ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class);
findOwnersQuery.addArgument("Category");
ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder();
findOwnersQuery.setSelectionCriteria(
builder.get("category").like(builder.getParameter("Category")));
Vector args = new Vector();
args.add(category);
List result = session.executeQuery(findOwnersQuery, args);
// do some further stuff with the result list
return result;
}
});
}
}
一個(gè)回調(diào)的實(shí)現(xiàn)能夠有效地在任何TopLink數(shù)據(jù)訪問(wèn)中使用。TopLinkTemplate
會(huì)確保當(dāng)前的 Session
對(duì)象的正確打開(kāi)和關(guān)閉,并自動(dòng)參與到事務(wù)管理中去。
Template實(shí)例不僅是線程安全的,同時(shí)它也是可重用的。因而他們可以作為外部對(duì)象的實(shí)例變量而被持有。對(duì)于那些簡(jiǎn)單的諸如
executeQuery
、readAll
、readById
和
merge
操作的調(diào)用,TopLinkTemplate
(譯者注:原文誤寫(xiě)成JdoTemplate
)提供可選擇的快捷函數(shù)來(lái)替換這種回調(diào)的實(shí)現(xiàn)。
不僅如此,Spring還提供了一個(gè)簡(jiǎn)便的 TopLinkDaoSupport
基類,這個(gè)類提供了
setSessionFactory(..)
方法來(lái)接受一個(gè) SessionFactory
對(duì)象,同時(shí)提供了 getSessionFactory()
和 getTopLinkTemplate()
方法給子類使用。綜合了這些,對(duì)于那些典型的業(yè)務(wù)需求,就有了一個(gè)非常簡(jiǎn)單的DAO實(shí)現(xiàn)。
public class ProductDaoImpl extends TopLinkDaoSupport implements ProductDao { public Collection loadProductsByCategory(String category) throws DataAccessException { ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class); findOwnersQuery.addArgument("Category"); ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder(); findOwnersQuery.setSelectionCriteria( builder.get("category").like(builder.getParameter("Category"))); return getTopLinkTemplate().executeQuery(findOwnersQuery, new Object[] {category}); } }
邊注:TopLink查詢對(duì)象是線程安全的,并且能夠在DAO層被緩存。在一開(kāi)始被創(chuàng)建時(shí)以實(shí)例變量的方式被保持。
作為不使用Spring的 TopLinkTemplate
來(lái)實(shí)現(xiàn)DAO的替代解決方案,
你依然可以通過(guò)原生TopLink API對(duì)那些基于Spring的DAO進(jìn)行編程,此時(shí)你必須明確地打開(kāi)和關(guān)
閉一個(gè) Session
。正如在相應(yīng)的Hibernate章節(jié)描述的
一樣,這種做法的主要優(yōu)點(diǎn)在于你的數(shù)據(jù)訪問(wèn)代碼可以在整個(gè)過(guò)程中拋出checked exceptions。
TopLinkDaoSupport
為這種情況提供了多種函數(shù)支持,包括獲取和釋放
一個(gè)具備事務(wù)的 Session
并做相關(guān)的異常轉(zhuǎn)化。
我們可以直接操作TopLink API來(lái)實(shí)現(xiàn)DAO,直接使用一個(gè)注入的 Session
而無(wú)需對(duì)Spring產(chǎn)生的任何依賴。它通常基于一個(gè)由 LocalSessionFactoryBean
定義的 SessionFactory
,并通過(guò)Spring的
TransactionAwareSessionAdapter
暴露成為一個(gè) Session
類型的引用。
TopLink的 Session
接口中定義的
getActiveSession()
方法將返回當(dāng)前具備事務(wù)管理功能的
Session
對(duì)象。如果當(dāng)前沒(méi)有處于活躍狀態(tài)的事務(wù),
這個(gè)函數(shù)將返回一個(gè)共享的TopLink的 ServerSession
,也就是說(shuō),
這種情況應(yīng)該只是一個(gè)直接使用的只讀訪問(wèn)。另外還有一個(gè) getActiveUnitOfWork()
方法, 返回TopLink的與當(dāng)前事務(wù)綁定的 UnitOfWork
(如果沒(méi)有當(dāng)前事務(wù)則返回 null
)。
一個(gè)相應(yīng)的DAO實(shí)現(xiàn)類看上去就像下面那樣:
public class ProductDaoImpl implements ProductDao { private Session session; public void setSession(Session session) { this.session = session; } public Collection loadProductsByCategory(String category) { ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class); findOwnersQuery.addArgument("Category"); ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder(); findOwnersQuery.setSelectionCriteria( builder.get("category").like(builder.getParameter("Category"))); Vector args = new Vector(); args.add(category); return session.getActiveSession().executeQuery(findOwnersQuery, args); } }
上面我們所列出的DAO完全遵循IoC:它如同使用Spring的 TopLinkTemplate
進(jìn)行編碼那樣,非常適合在application context中進(jìn)行配置。Spring的
TransactionAwareSessionAdapter
將暴露一個(gè)
Session
類型的bean的引用,并傳入到DAO中去:
<beans> <bean id="mySessionAdapter" class="org.springframework.orm.toplink.support.TransactionAwareSessionAdapter"> <property name="sessionFactory" ref="mySessionFactory"/> </bean> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="session" ref="mySessionAdapter"/> </bean> </beans>
這種DAO風(fēng)格的主要好處在于它僅僅依賴于TopLink自身的API,而無(wú)需引入任何的Spring 的類。從無(wú)入侵性的角度來(lái)看,這一點(diǎn)非常吸引人。同時(shí),對(duì)于TopLink的開(kāi)發(fā)人員來(lái)說(shuō)也更自然。
然而,這樣的DAO訪問(wèn)方式會(huì)拋出 TopLinkException
(這是一個(gè)無(wú)需聲明或捕獲的unchecked exception),這意味著,DAO的調(diào)用者只能以普通的
錯(cuò)誤來(lái)處理這些異常,除非完全依賴TopLink自身的異常體系。因而,除非你將DAO的調(diào)用者綁定
到具體的實(shí)現(xiàn)策略上去,否則你將無(wú)法捕獲特定的異常原因(諸如樂(lè)觀鎖異常)。這種折中平衡或許
可以被接受,如果你的應(yīng)用完全基于TopLink或者無(wú)需進(jìn)行特殊的異常處理。
這樣的DAO風(fēng)格有一個(gè)不利因素在于TopLink的標(biāo)準(zhǔn)的 getActiveSession()
函數(shù)僅僅在JTA事務(wù)中有效。而對(duì)于其他的事務(wù)管理策略尤其時(shí)本地的TopLink事務(wù),它將 無(wú)法 工作。
幸運(yùn)的是,Spring的 TransactionAwareSessionAdapter
為TopLink的 ServerSession
暴露了一個(gè)相應(yīng)的代理類。
這個(gè)代理類能夠在任何的事務(wù)策略之上支持TopLink的 Session.getActiveSession()
和 Session.getActiveUnitOfWork()
函數(shù),返回當(dāng)前收到Spring管理
(即便由 TopLinkTransactionManager
管理)的具備事務(wù)管理功能的
Session
實(shí)例。當(dāng)然,這個(gè)函數(shù)的標(biāo)準(zhǔn)行為依然有效:
返回與當(dāng)前的JTA事務(wù)綁定的 Session
對(duì)象。
(無(wú)論這個(gè)JTA事務(wù)是由Spring的 JtaTransactionManager
、
EJB CMT或者普通的JTA所驅(qū)動(dòng)的事務(wù))。
總體來(lái)說(shuō),DAO可以基于TopLink的原生API實(shí)現(xiàn),同時(shí),它依舊需要能夠參與到Spring的事務(wù)管理中。
這對(duì)于那些已經(jīng)對(duì)TopLink非常熟悉的人來(lái)說(shuō)很有吸引力,因?yàn)檫@種方式更加自然。不過(guò),這種DAO將拋出
TopLinkException
,因而,如果有必要的話需要明確地去做由
TopLinkException
到Spring的 DataAccessException
的轉(zhuǎn)化。
將事務(wù)管理納入到Service操作的執(zhí)行中,你可以使用Spring通用的聲明式的事務(wù)管理功能,參加下面的例子:
<?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-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="myTxManager" class="org.springframework.orm.toplink.TopLinkTransactionManager"> <property name="sessionFactory" ref="mySessionFactory"/> </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>
注意,TopLink要求你必須在一個(gè)活躍的 工作單元(UnitOfWork)
中修改一個(gè)持久化對(duì)象(你通常不能修改由普通的TopLink的 Session
查詢返回的對(duì)象,因?yàn)檫@些對(duì)象通常是一些從二級(jí)緩存中讀出的只讀對(duì)象)。與Hibernate相比,
在TopLink中并沒(méi)有一種類似脫離事務(wù)刷出(non-transactional flush)的概念。基于這
種原因,TopLink需要被建立在特定的環(huán)境中,尤其是它需要為JTA同步做明確的創(chuàng)建,由此來(lái)
自行檢測(cè)一個(gè)JTA事務(wù)以及暴露一個(gè)相應(yīng)的活躍的 Session
和 UnitOfWork
。這一點(diǎn)對(duì)于本地事務(wù)不是必要的,由于它已經(jīng)被
Spring的 TopLinkTransactionManager
處理,但是對(duì)于
需要參與到JTA事務(wù)中的情況,是必須的(無(wú)論是由Spring的
JtaTransactionManager
、EJB CMT或者普通的JTA所驅(qū)動(dòng)的事務(wù))。
在你的基于TopLink的DAO代碼中,你可以使用 Session.getActiveUnitOfWork()
方法來(lái)訪問(wèn)當(dāng)前的 UnitOfWork
并通過(guò)它來(lái)執(zhí)行寫(xiě)操作。這將只在一個(gè)活躍的事務(wù)中有效
(在一個(gè)收到Spring管理的事務(wù)或者JTA事務(wù)中)。對(duì)于特殊的需求,你同樣可以獲取單獨(dú)的
UnitOfWork
實(shí)例,它將不參與到當(dāng)前的事務(wù)中去,不過(guò)這種情況非常少。
TopLinkTransactionManager
能夠?qū)⒁粋€(gè)TopLink事務(wù)暴露給
訪問(wèn)相同的JDBC DataSource
的JDBC訪問(wèn)代碼。
前提條件是,TopLink在底層是以JDBC方式工作的并且能夠暴露底層的JDBC Connection
。
這種情況下,用于暴露事務(wù)的 DataSource
必須被明確指定,
它是無(wú)法被自動(dòng)檢測(cè)到的。