?
This document uses PHP Chinese website manual Release
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團隊共同開發(fā)。 非常感謝TopLink團隊,尤其是Jim Clark,替我們闡明了所有方面的細(xì)節(jié)!
TopLink本身并沒有提供SessionFactory抽象層邏輯,多線程的數(shù)據(jù)訪問是建立在中央
ServerSession
上的。對于單線程訪問,
這個中央 ServerSession
會為它一個
ClientSession
的實例供其使用。為了提供靈活便捷的創(chuàng)建選項,
Spring為TopLink定義了一個 SessionFactory
接口,
從而使你可以任意地在不同的 Session
創(chuàng)建策略之間進行切換。
作為一個一站式的商店,Spring提供了一個 LocalSessionFactoryBean
類,
允許你以bean風(fēng)格的配置方式來定義一個TopLink的 SessionFactory
。
需要進行配置的地方主要是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)的實現(xiàn)能夠有效地在任何TopLink數(shù)據(jù)訪問中使用。TopLinkTemplate
會確保當(dāng)前的 Session
對象的正確打開和關(guān)閉,并自動參與到事務(wù)管理中去。
Template實例不僅是線程安全的,同時它也是可重用的。因而他們可以作為外部對象的實例變量而被持有。對于那些簡單的諸如
executeQuery
、readAll
、readById
和
merge
操作的調(diào)用,TopLinkTemplate
(譯者注:原文誤寫成JdoTemplate
)提供可選擇的快捷函數(shù)來替換這種回調(diào)的實現(xiàn)。
不僅如此,Spring還提供了一個簡便的 TopLinkDaoSupport
基類,這個類提供了
setSessionFactory(..)
方法來接受一個 SessionFactory
對象,同時提供了 getSessionFactory()
和 getTopLinkTemplate()
方法給子類使用。綜合了這些,對于那些典型的業(yè)務(wù)需求,就有了一個非常簡單的DAO實現(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)建時以實例變量的方式被保持。
作為不使用Spring的 TopLinkTemplate
來實現(xiàn)DAO的替代解決方案,
你依然可以通過原生TopLink API對那些基于Spring的DAO進行編程,此時你必須明確地打開和關(guān)
閉一個 Session
。正如在相應(yīng)的Hibernate章節(jié)描述的
一樣,這種做法的主要優(yōu)點在于你的數(shù)據(jù)訪問代碼可以在整個過程中拋出checked exceptions。
TopLinkDaoSupport
為這種情況提供了多種函數(shù)支持,包括獲取和釋放
一個具備事務(wù)的 Session
并做相關(guān)的異常轉(zhuǎn)化。
我們可以直接操作TopLink API來實現(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實現(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
進行編碼那樣,非常適合在application context中進行配置。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 的類。從無入侵性的角度來看,這一點非常吸引人。同時,對于TopLink的開發(fā)人員來說也更自然。
然而,這樣的DAO訪問方式會拋出 TopLinkException
(這是一個無需聲明或捕獲的unchecked exception),這意味著,DAO的調(diào)用者只能以普通的
錯誤來處理這些異常,除非完全依賴TopLink自身的異常體系。因而,除非你將DAO的調(diào)用者綁定
到具體的實現(xiàn)策略上去,否則你將無法捕獲特定的異常原因(諸如樂觀鎖異常)。這種折中平衡或許
可以被接受,如果你的應(yīng)用完全基于TopLink或者無需進行特殊的異常處理。
這樣的DAO風(fēng)格有一個不利因素在于TopLink的標(biāo)準(zhǔn)的 getActiveSession()
函數(shù)僅僅在JTA事務(wù)中有效。而對于其他的事務(wù)管理策略尤其時本地的TopLink事務(wù),它將 無法 工作。
幸運的是,Spring的 TransactionAwareSessionAdapter
為TopLink的 ServerSession
暴露了一個相應(yīng)的代理類。
這個代理類能夠在任何的事務(wù)策略之上支持TopLink的 Session.getActiveSession()
和 Session.getActiveUnitOfWork()
函數(shù),返回當(dāng)前收到Spring管理
(即便由 TopLinkTransactionManager
管理)的具備事務(wù)管理功能的
Session
實例。當(dāng)然,這個函數(shù)的標(biāo)準(zhǔn)行為依然有效:
返回與當(dāng)前的JTA事務(wù)綁定的 Session
對象。
(無論這個JTA事務(wù)是由Spring的 JtaTransactionManager
、
EJB CMT或者普通的JTA所驅(qū)動的事務(wù))。
總體來說,DAO可以基于TopLink的原生API實現(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
。這一點對于本地事務(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ù)中)。對于特殊的需求,你同樣可以獲取單獨的
UnitOfWork
實例,它將不參與到當(dāng)前的事務(wù)中去,不過這種情況非常少。
TopLinkTransactionManager
能夠?qū)⒁粋€TopLink事務(wù)暴露給
訪問相同的JDBC DataSource
的JDBC訪問代碼。
前提條件是,TopLink在底層是以JDBC方式工作的并且能夠暴露底層的JDBC Connection
。
這種情況下,用于暴露事務(wù)的 DataSource
必須被明確指定,
它是無法被自動檢測到的。