Spring的輕量級(jí)的bean容器為業(yè)務(wù)對(duì)象(business objects)、DAO對(duì)象和資源(如:JDBC數(shù)據(jù)源或者Hibernate SessionFactorie等)對(duì)象提供了IoC類(lèi)型的裝配能力。Spring使用一個(gè)xml格式的應(yīng)用配置文件為開(kāi)發(fā)者提供了一種通過(guò)解析定制的屬性文件來(lái)手動(dòng)管理單實(shí)例對(duì)象或者工廠(chǎng)對(duì)象的選擇性。由于Spring將非入侵性做為一個(gè)重要的目標(biāo),因此可以由Spring配置管理的bean對(duì)象均不需要依賴(lài)Spring自有的接口和類(lèi)就可以通過(guò)它們的bean屬性完成配置。這個(gè)概念可以被應(yīng)用到任何環(huán)境中,無(wú)論你開(kāi)發(fā)的是一個(gè)J2EE的web應(yīng)用還是一個(gè)桌面應(yīng)用甚至只是一個(gè)applet都可以。
??? 在使用Hibernate的應(yīng)用中, Spring的對(duì)DAO對(duì)象通常的事務(wù)管理特別應(yīng)該引起關(guān)注。它的目的就是分離數(shù)據(jù)訪(fǎng)問(wèn)和事務(wù)處理,使事務(wù)性業(yè)務(wù)對(duì)象不與任何特殊的數(shù)據(jù)訪(fǎng)問(wèn)或者事務(wù)策略綁在一起,從而不影響業(yè)務(wù)對(duì)象的可復(fù)用性。這種劃分既可以經(jīng)由事務(wù)模板(TransactionTemplate)用編程的方式實(shí)現(xiàn),也可以經(jīng)由面向方面(AOP)事務(wù)攔截器(TransactionTemplate)用聲明的方式實(shí)現(xiàn)。無(wú)論是本地的Hibernate / JDBC事務(wù),還是JTA事務(wù)都支持對(duì)象外的事務(wù)策略,這對(duì)于本地的無(wú)狀態(tài)會(huì)話(huà)Bean(Stateless Session Beans)是一個(gè)非常有用的選擇。
??? Spring的HibernateTemplate類(lèi)提供了一個(gè)簡(jiǎn)單的方式實(shí)現(xiàn)了Hibernate-based DAO對(duì)象而不必關(guān)心如何獲得Hibernate的Session實(shí)例,也不必關(guān)心多方參與的事務(wù)處理。無(wú)需使用try-catch塊,也無(wú)需進(jìn)行事務(wù)檢查。一個(gè)簡(jiǎn)單的Hibernate訪(fǎng)問(wèn)方法就完全解決了些麻煩! 無(wú)論是在多個(gè)DAO接口還是在多方事務(wù)的情況下,Spring使得多種DAO對(duì)象無(wú)縫地協(xié)同工作。例如:某些DAO對(duì)象可能是基于plain JDBC的實(shí)現(xiàn),更適合于經(jīng)由Spring的JdbcTemplate來(lái)避免手動(dòng)的異常處理。
????? 你可以單獨(dú)地使用許多Spring特性,因?yàn)镾pring的所有對(duì)象都是設(shè)計(jì)成可復(fù)用的JavaBean對(duì)象的集合。也不要因?yàn)镾pring可以提供一個(gè)完整的應(yīng)該框架而氣餒!使用其他的Spring特性時(shí),應(yīng)用配置概念是一個(gè)附加的特性,并不是一個(gè)必須的特性。無(wú)論如何,當(dāng)你要決定去構(gòu)建一個(gè)象Spring這樣的內(nèi)在的基礎(chǔ)架構(gòu)的時(shí)候,在使用Spring的路途上沒(méi)有什么范圍上的限制。
1. 介紹: 資源管理
?????? 典型的業(yè)務(wù)應(yīng)用系統(tǒng)常常由于重復(fù)的資源管理代碼而導(dǎo)致混亂。許多項(xiàng)目試著用自己的方法來(lái)解決這個(gè)問(wèn)題,有時(shí)要為此付出失敗的代價(jià),Spring針對(duì)適當(dāng)?shù)馁Y源管理提倡了一種引人注目的簡(jiǎn)單方法:即經(jīng)由模板來(lái)倒置控制(Inversion of control),例如:基礎(chǔ)類(lèi)使用回調(diào)接口,或者應(yīng)用AOP攔截器。其基礎(chǔ)核心是適當(dāng)?shù)馁Y源處理和將特殊的API異常轉(zhuǎn)換為一個(gè)unchecked的基礎(chǔ)異常。
?????? Spring引入了一個(gè)DAO異常層適用于任何數(shù)據(jù)訪(fǎng)問(wèn)策略。對(duì)于直接的JDBC,JdbcTemplate類(lèi)關(guān)注于連接處理,并且關(guān)注于對(duì)SQLException轉(zhuǎn)換為適當(dāng)?shù)腄ataAccessException,包括對(duì)特殊的數(shù)據(jù)庫(kù)SQL錯(cuò)誤轉(zhuǎn)換為有意義的異常。 經(jīng)由不同的事務(wù)管理對(duì)象,Spring支持JTA和JDBC事務(wù)。Spring 也提供對(duì)Hibernate和JDO的支持,它的這種支持由與JdbcTemplate類(lèi)的作用相類(lèi)似的HibernateTemplate類(lèi)和JdoTemplate類(lèi), 以及HibernateInterceptor類(lèi)、JdoInterceptor類(lèi),還有Hibernate、JDO 事務(wù)管理類(lèi)組成。
?????? 最主要的目的是要使應(yīng)用的層次分明,為此將數(shù)據(jù)訪(fǎng)問(wèn)和事務(wù)處理同應(yīng)用對(duì)象分離開(kāi)來(lái)。所有的業(yè)務(wù)對(duì)象都不再依賴(lài)數(shù)據(jù)訪(fǎng)問(wèn)或者事務(wù)策略。不再有硬編碼的資源查找代碼,不再有難以替換的單例對(duì)象,也不再需要定制服務(wù)注冊(cè)。
????? 所有的單獨(dú)的數(shù)據(jù)訪(fǎng)問(wèn)特性均無(wú)需依賴(lài)于Spring,可以單獨(dú)使用,無(wú)需讓Spring知道,同時(shí)也可以通過(guò)Spring的應(yīng)用配置(提供基于XML的配置和對(duì)普通JavaBean實(shí)例的交叉引用)來(lái)進(jìn)行裝配。在一個(gè)典型的Spring應(yīng)用中,大部分重要的對(duì)象都是普通的JavaBean:數(shù)據(jù)訪(fǎng)問(wèn)模板對(duì)象(data access templates)、數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象(使用數(shù)據(jù)訪(fǎng)問(wèn)模板對(duì)象的對(duì)象)、事務(wù)管理對(duì)象及業(yè)務(wù)對(duì)象(使用數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象和事務(wù)對(duì)象的對(duì)象),web表示分解對(duì)象、web控制對(duì)象(使用業(yè)務(wù)對(duì)象的對(duì)象)等等。
2. 應(yīng)用配置中的資源定義
??? 為了避免應(yīng)用對(duì)象將資源查找的代碼硬編碼,Spring允許在應(yīng)用配置中將一個(gè)如JDBC DataSource或者Hibernate SessionFactory定義為一個(gè)Bean。應(yīng)用對(duì)象如果需要訪(fǎng)問(wèn)資源只需要通過(guò)Bean引用(DAO定義在下一部分說(shuō)明)接受先前定義的實(shí)例的引用。以下的內(nèi)容引用自一個(gè)應(yīng)用配置定義,顯示了如何建立一個(gè)JDBC DataSource和一個(gè)Hibernate的SessionFactory:
?<beans>
<bean id="myDataSource" class="org.springframework.jndi .JndiObjectFactoryBean"> <property name="jndiName"> <value>jdbc/myds</value> </property> </bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate .LocalSessionFactoryBean"> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect .MySQLDialect</prop> </props> </property> <property name="dataSource"> <ref bean="myDataSource"/> </property> </bean>
...
</beans> |
??? ? 注意選擇是用JNDI來(lái)定位數(shù)據(jù)源還是從一個(gè)象Jakarta Commons DBCP BasicDataSource這樣的本地定義取得一個(gè)數(shù)據(jù)源,只是一個(gè)改變配置的事:
<bean id="myDataSource" ????? class="org.apache.commons .dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"> <value>org.hsqldb.jdbcDriver</value> </property> <property name="url"> <value>jdbc:hsqldb:hsql://localhost:9001</value> </property> <property name="username"> <value>sa</value> </property> <property name="password"> <value></value> </property> </bean> |
???? ? 你也可以使用一個(gè)JNDI查找SessionFactory,但是通常對(duì)于EJB環(huán)境之外的應(yīng)用來(lái)說(shuō)并不是需要的(參考"container resources vs local resources"部分的討論)。
3. 倒置控制(Inversion of Control): 模板和回調(diào)
??? 模板的基本編程模式就象你將在下面看到那樣,至于方法就如同任何定制的數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象或者業(yè)務(wù)的對(duì)象的方法一樣。除了需要向其提供一個(gè)Hibernate的SessionFactory之外,再?zèng)]有對(duì)周?chē)鷪?zhí)行對(duì)象的信賴(lài)的限制。雖然最好是從一個(gè)Spring的應(yīng)用配置中經(jīng)由一個(gè)簡(jiǎn)單setSessionFactory bean的屬性設(shè)置使用Bean引用來(lái)獲得它,但隨后你可以從任何地方獲得它。隨后的引用片段包括一段在Spring應(yīng)用配置中對(duì)DAO定義的配置,其中引用了在其前面定義的SessionFactory,和一段DAO方法的實(shí)現(xiàn)的例子。
<beans>
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
...
</beans> |
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public List loadProductsByCategory(final String category) {
HibernateTemplate hibernateTemplate =
new HibernateTemplate(this.sessionFactory);
return (List) hibernateTemplate.execute(
new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
// do some further stuff with the result list
return result;
}
}
);
}
}
|
?????? 一個(gè)回調(diào)的實(shí)現(xiàn)可以被有效地用在任何Hibernate數(shù)據(jù)訪(fǎng)問(wèn)中。在任何情況下都由HibernateTemplate來(lái)管理Session的開(kāi)閉和自動(dòng)的多方事務(wù)。模板實(shí)例是線(xiàn)程安全和可重用的,因此它們可以做為其他類(lèi)的變量。
?????? 對(duì)于簡(jiǎn)單的單步的動(dòng)作,象find, load, saveOrUpdate或者delete的調(diào)用,HibernateTemplate提供更為便利的選擇以代替象一行的回調(diào)的執(zhí)行。此外,Spring提供了一個(gè)方便的基本類(lèi),就是HibernateDaoSupport類(lèi),它提供了setSessionFactory方法來(lái)接受一個(gè)SessionFactory,同時(shí)提供了getSessionFactory和getHibernateTemplate方法供其繼承類(lèi)使用。將這些結(jié)合起來(lái),允許對(duì)于典型的需求給出了非常簡(jiǎn)單的DAO實(shí)現(xiàn):
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
public List loadProductsByCategory(String category) {
return getHibernateTemplate().find(
"from test.Product product where product.category=?", category,
Hibernate.STRING);
}
}
|
4. 應(yīng)用一個(gè)AOP攔截器代替一個(gè)模板
?? 除使用HibernateTemplate之外的另一個(gè)選擇就是使用Spring的AOP HibernateInterceptor。用直接在一個(gè)委托的try/catch塊中編寫(xiě)Hibernate代碼,配合相應(yīng)的在應(yīng)用配置中分別的攔截器配置來(lái)代替執(zhí)行回調(diào)。下面的片段顯示了一個(gè)Spring應(yīng)用配置中的DAO, interceptor和proxy的各自的定義,同時(shí)給出了一個(gè)DAO方法實(shí)現(xiàn)的例子:
<beans>
...
<bean id="myHibernateInterceptor"
class="org.springframework.orm.hibernate .HibernateInterceptor">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
<bean id="myProductDaoTarget" class="product.ProductDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
<bean id="myProductDao" class="org.springframework.aop .framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>product.ProductDao</value>
</property>
<property name="interceptorNames">
<list>
<value>myHibernateInterceptor</value>
<value>myProductDaoTarget</value>
</list>
</property>
</bean>
...
</beans>
|
?public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
public List loadProductsByCategory(final String category) throws MyException {
Session session = SessionFactoryUtils .getSession(getSessionFactory(), false);
try {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
if (result == null) {
throw new MyException("invalid search result");
}
return result;
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
}
|
??????? 這個(gè)方法將只在有一個(gè)與它配合的HibernateInterceptor時(shí)才能正常工作,HibernateInterceptor為它負(fù)責(zé)在方法調(diào)用前線(xiàn)程綁定Session的開(kāi)啟和方法調(diào)用后的關(guān)閉。getSession方法調(diào)用中的"false"標(biāo)志是要確認(rèn)Session必須是已經(jīng)存在的,如果沒(méi)有發(fā)現(xiàn)任何一個(gè)Session,SessionFactoryUtils將會(huì)為其創(chuàng)建一個(gè)。如果已經(jīng)有一個(gè)Session句柄綁定在本線(xiàn)程上,比如是由一個(gè)HibernateTransactionManager事務(wù)綁定的,在任何情況下SessionFactoryUtils會(huì)自動(dòng)接入這個(gè)Session。HibernateTemplate在底層也使用SessionFactoryUtils,與以上說(shuō)的方式基本是一樣的。
?????? HibernateInterceptor的主要益處是它允許在數(shù)據(jù)訪(fǎng)問(wèn)代碼中拋出checked application exception,而HibernateTemplate由于受限于回調(diào)只能在其中拋出unchecked exceptions。注意到這點(diǎn)我們可以推遲各自的檢驗(yàn),同時(shí)在回調(diào)后拋出應(yīng)用異常。攔截方式的主要缺點(diǎn)是它需要在配置中進(jìn)行特殊的配置。HibernateTemplate在大多數(shù)情況下都是一種簡(jiǎn)單好用的方法。
5. 程序事務(wù)劃分
?? 在這種底層的數(shù)據(jù)訪(fǎng)問(wèn)服務(wù)之上,事務(wù)處理可以在更高的應(yīng)用層被劃分 ,形成一些操作。這里除了需要一個(gè)Spring的PlatformTransactionManager對(duì)象外,對(duì)于周?chē)\(yùn)行的業(yè)務(wù)對(duì)象也沒(méi)有任何限制。同樣的,其后你可以從任何地方獲得它們,但是經(jīng)由Bean引用的方式通過(guò)setTransactionManage方法獲得更為適合,象productDAO要經(jīng)由一個(gè)setProductDao方法獲得一樣。下面的引用片段顯示了在一個(gè)Spring應(yīng)用配置中的事務(wù)管理對(duì)象和業(yè)務(wù)對(duì)象的定義,并且還提供了一個(gè)業(yè)務(wù)方法實(shí)現(xiàn)的例子:
<beans>
...
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate .HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
<bean id="myProductService" class="product.ProductServiceImpl">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="productDao">
<ref bean="myProductDao"/>
</property>
</bean>
</beans>
|
?public class ProductServiceImpl implements ProductService {
private PlatformTransactionManager transactionManager;
private ProductDao productDao;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void increasePriceOfAllProductsInCategory(final String category) {
TransactionTemplate transactionTemplate =
new TransactionTemplate(this.transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition .PROPAGATION_REQUIRED);
transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
List productsToChange = productDAO.loadProductsByCategory(category);
...
}
}
);
}
}
|
6. 聲明性事務(wù)劃分
?????? 我們還可以選擇使用Spring的AOP TransactionInterceptor通過(guò)在應(yīng)用配置中定義攔截器配置來(lái)代替事務(wù)劃分代碼的事務(wù)處理方式。這允許我們保持業(yè)務(wù)對(duì)象獨(dú)立于每個(gè)業(yè)務(wù)對(duì)象中重復(fù)的事務(wù)劃分代碼。此外,事務(wù)行為和隔離層次的變化可以通過(guò)一個(gè)配置文件來(lái)改變而不需要對(duì)業(yè)務(wù)對(duì)象的實(shí)現(xiàn)造成影響。
<beans>
...
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate .HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
<bean id="myTransactionInterceptor"
class="org.springframework.transaction .interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
product.ProductService.increasePrice*=PROPAGATION_REQUIRED
product.ProductService.someOtherBusinessMethod= PROPAGATION_MANDATORY
</value>
</property>
</bean>
<bean id="myProductServiceTarget" class="product .ProductServiceImpl">
<property name="productDao">
<ref bean="myProductDao"/>
</property>
</bean>
<bean id="myProductService" class="org.springframework.aop .framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>product.ProductService</value>
</property>
<property name="interceptorNames">
<list>
<value>myTransactionInterceptor</value>
<value>myProductServiceTarget</value>
</list>
</property>
</bean>
</beans>
|
?public class ProductServiceImpl implements ProductService {
private ProductDao productDao;
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void increasePriceOfAllProductsInCategory(final String category) {
List productsToChange = this.productDAO .loadProductsByCategory(category);
...
}
} |
???? ?如同使用HibernateInterceptor一樣,TransactionInterceptor允許任何checked application exception從回調(diào)代碼中拋出,而TransactionTemplate受回調(diào)限制在其內(nèi)部拋出unchecked exceptions,在出現(xiàn)一個(gè)unchecked application exception的情況時(shí),TransactionTemplate將引發(fā)一個(gè)回滾或者這個(gè)事務(wù)由應(yīng)用(通過(guò)事務(wù)狀態(tài))標(biāo)記為回滾。TransactionInterceptor默認(rèn)情況也是同樣的行為,但是允許為每一個(gè)方法制定回滾策略。
????? ?建立聲明性事務(wù)的一個(gè)便利的方式是使用TransactionProxyFactoryBean,特別是如果沒(méi)有其他AOP攔截器的話(huà),TransactionProxyFactoryBean將聯(lián)合定義為代理的自身與一個(gè)特殊的目標(biāo)Bean的事務(wù)配置。這將減少一個(gè)代理Bean對(duì)應(yīng)一個(gè)目標(biāo)Bean的配置情況。此外,你不必指定哪個(gè)接口或者哪個(gè)類(lèi)必須定義事務(wù)方法。
<beans>
...
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate .HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
<bean id="myProductServiceTarget" class="product .ProductServiceImpl">
<property name="productDao">
<ref bean="myProductDao"/>
</property>
</bean>
<bean id="myProductService"
class="org.springframework.transaction.interceptor .TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="target">
<ref bean="myProductServiceTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
<prop key="someOtherBusinessMethod">PROPAGATION_MANDATORY</prop>
</props>
</property>
</bean>
</beans>
|
7. 事務(wù)管理策略
????????? 對(duì)于Hibernate應(yīng)用來(lái)說(shuō),無(wú)論是TransactionTemplate還是TransactionInterceptor都是委托驗(yàn)實(shí)際的事務(wù)處理給PlatformTransactionManager實(shí)例,可以是一個(gè)HibernateTransactionManager(由一個(gè)單一的Hibernate的SessionFactory,使用一個(gè)ThreadLocal Session)或者可以是一個(gè)JtaTransactionManager(代理容器的JTA子系統(tǒng))。甚至你可以使用一個(gè)自定義的PlatformTransactionManager實(shí)現(xiàn)。
????? ?如果選擇從本地Hibernate事務(wù)管理轉(zhuǎn)為由JTA來(lái)進(jìn)行事務(wù)管理,例如:當(dāng)你的應(yīng)用的部署面對(duì)分布的事務(wù)需求時(shí),也僅僅是改變一下配置的事。只要簡(jiǎn)單地將Hibernate的事務(wù)管理?yè)Q為JTA事務(wù)實(shí)現(xiàn)即可。所有的事務(wù)劃分和數(shù)據(jù)訪(fǎng)問(wèn)無(wú)需做任何變動(dòng)仍可以繼續(xù)工作,因?yàn)樗麄兪褂玫亩际瞧胀ǖ氖聞?wù)管理API。
?????? 對(duì)于分布式的事務(wù)會(huì)跨越多個(gè)Hibernate的session factories,僅僅是聯(lián)合JtaTransactionManager與多個(gè)LocalSessionFactoryBean定義作為事務(wù)策略。你的每一個(gè)DAO將通過(guò)它們各自的Bean屬性得到一個(gè)特殊的SessionFactory的引用。如果這一切都是在下面的JDBC數(shù)據(jù)源是事務(wù)容器,一個(gè)業(yè)務(wù)對(duì)象可以劃分事務(wù)跨越很多DAO和很多session factories而無(wú)需做特別的處理,對(duì)于使用JtaTransactionManager做為事務(wù)策略也是一樣的。
?<beans>
<bean id="myDataSource1" class="org.springframework.jndi .JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/myds1</value>
</property>
</bean>
<bean id="myDataSource2" class="org.springframework.jndi. JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/myds2</value>
</property>
</bean>
<bean id="mySessionFactory1"
class="org.springframework.orm.hibernate. LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect. MySQLDialect</prop>
</props>
</property>
<property name="dataSource">
<ref bean="myDataSource1"/>
</property>
</bean>
<bean id="mySessionFactory2"
class="org.springframework.orm.hibernate. LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>inventory.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate. dialect.OracleDialect</prop>
</props>
</property>
<property name="dataSource">
<ref bean="myDataSource2"/>
</property>
</bean>
<bean id="myTransactionManager"
class="org.springframework.transaction.jta. JtaTransactionManager"/>
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory1"/>
</property>
</bean>
<bean id="myInventoryDao" class="product.InventoryDaoImpl">
<property name="sessionFactory">
<ref bean="mySessionFactory2"/>
</property>
</bean>
<bean id="myProductServiceTarget" class="product. ProductServiceImpl">
<property name="productDao">
<ref bean="myProductDao"/>
</property>
<property name="inventoryDao">
<ref bean="myInventoryDao"/>
</property>
</bean>
<bean id="myProductService"
class="org.springframework.transaction.interceptor. TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="myTransactionManager"/>
</property>
<property name="target">
<ref bean="myProductServiceTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
<prop key="someOtherBusinessMethod">PROPAGATION_MANDATORY</prop>
</props>
</property>
</bean>
</beans>
|
????? ?無(wú)論是HibernateTransactionManager還是JtaTransactionManager允許適當(dāng)?shù)膶?duì)Hibernate的在JVM層次的緩存處理-不需要容器-提供特殊的事務(wù)查找或者JCA連接器(只要不使用EJB發(fā)起事務(wù))。另外,HibernateTransactionManager能輸出JDBC連接供通常的JDBC訪(fǎng)問(wèn)代碼使用。這樣就允許在高層次上的事務(wù)劃分是混合了Hibernate與JDBC而不要JTA的,只要只是訪(fǎng)問(wèn)一個(gè)數(shù)據(jù)庫(kù)就可以!
8. 使用Spring管理應(yīng)用的Bean
??????? 一個(gè)Spring應(yīng)用配置定義可以被多種配置實(shí)現(xiàn)所加載,從FileSystemXmlApplicationContext和ClassPathXmlApplicationContext到XmlWebApplicationContext。這就允許在各種環(huán)境下重用Spring管理的數(shù)據(jù)訪(fǎng)問(wèn)和業(yè)務(wù)對(duì)象。默認(rèn)情況下,一個(gè)Web應(yīng)用將有它自己的定義在“WEB-INF/applicationContext.xml”中的根配置。
?????? 在任何一個(gè)Spring應(yīng)用中,一個(gè)應(yīng)用配置定義在一個(gè)XML格式的文件中用來(lái)對(duì)應(yīng)用的所有有關(guān)的Bean進(jìn)行裝配,從Hibernate的session factory到自定義的數(shù)據(jù)訪(fǎng)問(wèn)和業(yè)務(wù)對(duì)象(象上面所有的Bean那樣)。他們中的大多數(shù)不需要Spring容器知道他們,甚至即使是與其他Bean合作時(shí)也一樣,因?yàn)樗麄冎皇呛?jiǎn)單的JavaBean之間的協(xié)作。下面的Bean定義可能是一個(gè)Spring Web 的MVC配置中用來(lái)訪(fǎng)問(wèn)業(yè)務(wù)對(duì)象的配置的一部分。
?<bean id="myProductList" class="product.ProductListController">
<property name="productService">
<ref bean="myProductService"/>
</property>
</bean>
|
????? Spring的Web控制器經(jīng)由Bean引用擁有它們需要的所有的業(yè)務(wù)和數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象,因此它們無(wú)需在應(yīng)用配置中做任何手工的Bean查找。但是當(dāng)使用Spring管理的Beans用于Struts或者是在EJB實(shí)現(xiàn),或者一個(gè)applet中時(shí)常常是需要必須手工查找一個(gè)Bean的。因此Spring的Bean可以被用在任何地方。也許只是需要是一應(yīng)用配置的引用,或者經(jīng)由一個(gè)web容器的Servlet配置屬性,或者從一個(gè)文件中或者類(lèi)路徑的資源中創(chuàng)建它。
ApplicationContext context =WebApplicationContextUtils. getWebApplicationContext(servletContext);
ProductService productService =
(ProductService) context.getBean("myProductService");
|
?ApplicationContext context =
new FileSystemXmlApplicationContext("C:/myContext.xml");
ProductService productService =
(ProductService) context.getBean("myProductService");
|
?ApplicationContext context =
new ClassPathXmlApplicationContext("myContext.xml");
ProductService productService =
(ProductService) context.getBean("myProductService"); |
9. 容器資源VS本地資源
?????? Spring的資源管理允許簡(jiǎn)單地在一個(gè)JNDI SessionFactory和一個(gè)本地SessionFactory間做選擇,同樣允許在一個(gè)JNDI DataSource與本地DataSource間做選擇,而無(wú)需改變應(yīng)用的一行代碼。在容器中保存資源定義還是在應(yīng)用本地保存,主要是一個(gè)事務(wù)策略方面的事。比較一個(gè)Spring定義的本地SessionFactory與一個(gè)手工注冊(cè)的JNDI SessionFactory沒(méi)有任何益處。如果經(jīng)由Hibernate的JCA連接器注冊(cè),才會(huì)有加入JTA事務(wù)的明顯益處,特別是對(duì)EJB。
?????? 一個(gè)重要的Spring事務(wù)提供的好處是它不與任何容器綁定。定義包括JTA在內(nèi)的策略,它都可以獨(dú)立工作或者在一個(gè)試驗(yàn)環(huán)境中工作。特別是對(duì)典型的一個(gè)數(shù)據(jù)庫(kù)的事務(wù)來(lái)說(shuō),對(duì)于JTA這是一個(gè)非常輕量的和強(qiáng)大的選擇。當(dāng)使用本地EJB SLSB的事務(wù)時(shí),你將同時(shí)依賴(lài)EJB容器和JTA-即使你只是訪(fǎng)問(wèn)一個(gè)數(shù)據(jù)庫(kù),即使只是使用SLSBs經(jīng)由CMT來(lái)聲明事務(wù)。選擇使用 JTA編程也需要一個(gè)J2EE環(huán)境。
?????? 就JTA自身和JNDI數(shù)據(jù)源來(lái)說(shuō)JTA不只是包括容器依賴(lài)。對(duì)于不使用Spring的JTA驅(qū)動(dòng)的Hibernate事務(wù),你必須使用HibernateJCA連接器或者在合適的JVM緩沖層專(zhuān)門(mén)寫(xiě)Hibernate的事務(wù)代碼配置JTA事務(wù)。在只訪(fǎng)問(wèn)一個(gè)數(shù)據(jù)庫(kù)的情況下,Spring驅(qū)動(dòng)的事務(wù)可以與一個(gè)本地定義的Hibernate的SessionFactory配合良好,就如同與一個(gè)本地JDBC數(shù)據(jù)源相配合一樣。因此當(dāng)面對(duì)分布的事務(wù)需求時(shí),你只需要轉(zhuǎn)換為Spring的JTA事務(wù)策略即可。
????? ?要注意一個(gè)JCA連接器需要特別的容器的部署步驟,并且顯然首先得支持JCA。這比使用本地資源定義和Spring驅(qū)動(dòng)事務(wù)來(lái)部署一個(gè)簡(jiǎn)單的Web應(yīng)用有更多的爭(zhēng)議。而且你常常需要企業(yè)版本的容器支持,象WebLogic Express就不提供JCA。一個(gè)只用一個(gè)數(shù)據(jù)庫(kù)的使用本地資源和事務(wù)的Spring應(yīng)用可以在任何J2EE的Web容器中工作,Web容器不必支持JTA, JCA和EJB,如:Tomcat, Resin甚至最小的Jetty。另外,這樣一個(gè)中間層就可以很容易地在桌面應(yīng)用或者在測(cè)試套件中被重用。
?????? 所有考慮過(guò)的事情包括:如果你不使用EJB,堅(jiān)持使用本地SessionFactory,使用SpringHibernateTransactionManager或者JtaTransactionManager,你將獲得包括適當(dāng)處理的JVM層的緩存和分布事務(wù)的所有益處,而無(wú)需引起任何關(guān)于容器部署的爭(zhēng)論。經(jīng)由JCA連接器的一個(gè)Hibernate的SessionFactory的JNDI注冊(cè)只是在使用EJB的情況中才會(huì)有明顯的附加值。
10. Skeletons和例子
????? 配置使用Spring和HIbernate的一個(gè)J2EE的Web應(yīng)用的注釋和細(xì)節(jié)最好去看看在Spring Framework的例子中的“典型的Web應(yīng)用”Skeletons,它給出了適合于JDBC 和 Hibernate應(yīng)用的多種數(shù)據(jù)源及事務(wù)管理的配置項(xiàng),仔細(xì)看一下事務(wù)攔截器的配置,它也同樣向你展示了如何配置AOP攔截器。
????? 在Spring的1.0 M2版中,例子Petclinic提供了JDBC和Hibernate的DAO實(shí)現(xiàn)和應(yīng)用配置的選擇。Petclinic
可以作為一個(gè)可工作的簡(jiǎn)單應(yīng)用說(shuō)明如何在一個(gè)Spring web 應(yīng)用中使用Hibernate,同樣也包括根據(jù)不同的事務(wù)策略來(lái)聲明事務(wù)劃分。
Links
Spring Framework website
Spring Framework documentation