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