Spring - Java/J2EE Application Framework 应用框架 第 11 章 使用ORM工具进行数据访问
第?11?章?使用ORM工具進(jìn)行數(shù)據(jù)訪問
11.1.?簡介
Spring在資源管理,DAO實(shí)現(xiàn)支持以及實(shí)物策略等方面提供了與Hibernate, JDO和iBATIS SQL映射的集成。 對(duì)Hibernate,Spring使用了很多IoC的方便的特性提供了一流的支持,幫助你處理很多典型的Hibernate整合的問題。所有的這些都遵守Spring通用的事務(wù)和DAO異常體系.
當(dāng)您選擇使用O/R映射來創(chuàng)建數(shù)據(jù)訪問應(yīng)用程序的時(shí)候,Spring的增加部分就會(huì)向您提供重要的支持。首先你應(yīng)該了解的是,一旦你使用了Spring對(duì)O/R映射的支持,你不需要親自作所有的事情。在決定花費(fèi)力氣,冒著風(fēng)險(xiǎn)建造類似的內(nèi)部底層結(jié)構(gòu)之前,我們都建議您考慮和利用Spring的解決方案。不管你使用的是何種技術(shù),大部分的O/R映射支持都可以以library樣式被使用,因?yàn)樗械臇|西都是被設(shè)計(jì)成一組可重復(fù)利用的JavaBeans。在ApplicationContext和BeanFactory中使用更是提供了配置和部署簡單的好處,因此,這一章里的大多數(shù)例子都是在ApplicationContext中配置。
使用Spring構(gòu)建你的ORM應(yīng)用的好處包括:
-
避免綁定特殊的技術(shù),允許mix-and-match的實(shí)現(xiàn)策略。?雖然Hibernate非常強(qiáng)大,靈活,開源而且免費(fèi),但它還是使用了自己的特定的API。此外有人也許會(huì)爭(zhēng)辯:iBatis更輕便而且在不需要復(fù)雜的O/R映射策略的應(yīng)用中使用也很優(yōu)秀。能夠選擇的話,使用標(biāo)準(zhǔn)或抽象的API來實(shí)現(xiàn)主要的應(yīng)用需求,通常是更好的。尤其,當(dāng)你可能會(huì)因?yàn)楣δ?#xff0c;性能或其他方面的原因而需要切換到另一個(gè)實(shí)現(xiàn)的時(shí)候。舉例來說,Spring對(duì)Hibernate事務(wù)和異常的抽象,以及能夠讓你輕松交換mapper和DAO對(duì)象(實(shí)現(xiàn)數(shù)據(jù)訪問功能)的IoC機(jī)制,這兩個(gè)特性可以讓你在不犧牲Hibernate性能的情況下,在你的應(yīng)用程序中隔離Hibernate的相關(guān)代碼。處理DAO的高層次的service代碼不需要知道DAO的具體實(shí)現(xiàn)。這個(gè)方法可以很容易使用mix-and-match方案互不干擾地實(shí)現(xiàn)數(shù)據(jù)訪問層(比如在一些地方用Hibernate,一些地方使用JDBC,其他地方使用iBatis),mix-and-match有利于處理遺留下來的代碼以及利用各種技術(shù)(JDBC,Hibernate,iBatis)的長處.
-
測(cè)試簡單. Spring的IoC使得很容易替換掉不同的實(shí)現(xiàn),Hibernate SessionFacotory的位置,datasource, 事務(wù)管理, 映射對(duì)象的實(shí)現(xiàn)。這樣就很容易隔離測(cè)試持久化相關(guān)代碼的各個(gè)部分。
-
通用的資源管理。 Spring的application context能夠處理諸如Hibernate 的SessionFactory, JDBC的datasource,iBatis的SQLMaps配置對(duì)象以及其他相關(guān)資源的定位和配置。這使得這些配置的值很容易被管理和修改。Spring提供了有效,簡單和安全的Hibernate Session處理。一般的使用Hibernate的代碼則需要使用同一個(gè)Hibernate Session對(duì)象以確保有效和恰當(dāng)?shù)厥聞?wù)處理。而Spring讓我們可以很容易透明地創(chuàng)建和綁定一個(gè)session到當(dāng)前線程;你可以使用以下兩種辦法之一:聲明式的AOP方法攔截器,或通過使用一個(gè)外部的template包裝類在Java代碼層次實(shí)現(xiàn)。這樣,Spring就解決了在很多Hibernate論壇上出現(xiàn)的使用問題。
-
異常包裝。 Spring能夠包裝Hibernate異常,把它們從專有的,checked exception變?yōu)橐唤M抽象的runtime exception。這樣你就可以僅僅在恰當(dāng)?shù)膶犹幚泶蟛糠值牟豢苫謴?fù)的異常,使你避免了很多討厭的catch/throw以及異常聲明。你還是可以在你需要的地方捕捉和處理異常。回想一下JDBC異常(包括與DB相關(guān)的方言)被轉(zhuǎn)變?yōu)橥瑯拥漠惓sw系,這就意味著你可以在一致的編程模型中處理JDBC操作。
-
綜合的事務(wù)管理?。 Spring允許你包裝你的ORM代碼,通過使用聲明式的AOP方法攔截器或者在代碼級(jí)別使用外部的template包裝類。不管使用哪一種,事務(wù)相關(guān)的語義都會(huì)為你處理,萬一有異常發(fā)生也會(huì)幫你做適當(dāng)?shù)氖聞?wù)操作(比如rollback)。就象我們下面要討論的一樣,你能夠使用和替換各種transaction managers,卻不會(huì)使你的Hibernate相關(guān)的代碼受到影響。更好的是,JDBC相關(guān)的代碼可以完全和Hibernate代碼integrate transactionaly。這對(duì)于處理那些沒有用Hibernate或iBatis實(shí)現(xiàn)的功能非常有用。
11.2.?Hibernate
11.2.1.?資源管理
典型的應(yīng)用經(jīng)常會(huì)被重復(fù)的資源管理代碼搞胡亂。 很多項(xiàng)目嘗試創(chuàng)造自己的方案解決這個(gè)問題,有時(shí)會(huì)為了編程方便犧牲適當(dāng)?shù)墓收咸幚怼?對(duì)于恰當(dāng)?shù)馁Y源處理Spring提倡令人矚目的簡單的解決方案:使用templating的IoC, 比如基礎(chǔ)的class和回調(diào)接口,或者提供AOP攔截器。基礎(chǔ)的類負(fù)責(zé)固定的資源處理, 以及將特定的異常轉(zhuǎn)換為unchecked異常體系。Spring引進(jìn)了DAO異常體系,可適用于任何數(shù)據(jù)訪問策略。 對(duì)于直接使用JDBC的情況,前面章節(jié)提到的JdbcTemplate類負(fù)責(zé)處理connection, 正確地把SQLException?變?yōu)镈ataAccessException?體系(包括將與數(shù)據(jù)庫相關(guān)的SQL錯(cuò)誤代碼變成有意義的異常類)。 它同時(shí)支持JTA和JDBC事務(wù),通過它們各自的Spring transaction managers。 Spring同樣也提供了對(duì)Hibernate和JDO的支持:一個(gè)HibernateTemplate/JdoTemplate類似于JdbcTemplate,HibernateInterceptor/JdoInterceptor,以及一個(gè)Hibernate/JDO transaction manager。 主要的目的是:能夠清晰地劃分應(yīng)用層次而不管使用何種數(shù)據(jù)訪問和事務(wù)技術(shù);使應(yīng)用對(duì)象之間的耦合松散。 業(yè)務(wù)對(duì)象(BO)不再依賴于數(shù)據(jù)訪問和事務(wù)策略;不再有硬編碼的資源lookup;不再有難于替換的singletons ;不再有自定義的服務(wù)注冊(cè)。一個(gè)簡單且堅(jiān)固的方案連接了應(yīng)用對(duì)象,并且使它們可重用盡可能地不依賴容器 。雖然所有的數(shù)據(jù)訪問技術(shù)都能獨(dú)立使用,但是與Spring application context結(jié)合更好一些, 它提供了基于xml的配置和普通的與Spring 無關(guān)的JavaBean實(shí)例。在典型的Spring app中, 很多重要的對(duì)象都是JavaBeans:數(shù)據(jù)訪問template,數(shù)據(jù)訪問對(duì)象(使用template),transaction managers, 業(yè)務(wù)對(duì)象(使用數(shù)據(jù)訪問對(duì)象和transaction managers),web view resolvers, web controller(使用業(yè)務(wù)對(duì)象)等等。
11.2.2.?在 Application Context中的Bean 聲明
為了避免將應(yīng)用對(duì)象貼緊硬編碼的資源lookup, Spring允許你像定義普通bean一樣在application context中定義諸如 JDBC?DataSource,Hibernate?SessionFactory?的資源。 需要訪問這些資源的應(yīng)用對(duì)象只需要持有這些預(yù)定義實(shí)例的引用。 下面的代碼演示如何創(chuàng)建一個(gè)JDBC?DataSource和Hibernate?SessionFactory?: :
<beans><bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName"><value>java:comp/env/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>你可以將一個(gè)JNDI定位的DataSource換為一個(gè)本地定義的如DBCP的BasicDataSource,如下面的代碼: :
<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>當(dāng)然你也可以把本地的SessionFactory換為JNDI定位的, 但是如果不是在EJB上下文中,這是不需要的。(查看“容器資源 vs 本地資源”一節(jié))
11.2.3.?反向控制: 模板和回調(diào)的使用
對(duì)于可以成為定制的數(shù)據(jù)訪問對(duì)象或業(yè)務(wù)對(duì)象的方法來說, 基本的模板編程模型看起來像下面所示的代碼那樣。 對(duì)于外部對(duì)象沒有任何實(shí)現(xiàn)特定接口的要求,它只需要提供一個(gè)Hibernate的SessionFactory。 它可以從任何地方得到,比較適宜的方法是作為從Spring 的application context中得到的bean引用: 通過簡單的setSessionFactory這個(gè)bean屬性setter。下面的代碼顯示了在application context中一個(gè)DAO的定義, 它引用了上面定義的SessionFactory,同時(shí)展示了一個(gè)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 listreturn result;}});} }一個(gè)callback的實(shí)現(xiàn)能在任何的Hibernate數(shù)據(jù)訪問中有效使用。?HibernateTemplate會(huì)確保Session正確的打開和關(guān)閉,并且會(huì)自動(dòng)參與事務(wù)。 Template實(shí)例是線程安全的,可重用的,因此可以作為外部類的實(shí)例變量而被保持。 對(duì)于簡單的一步操作,例如簡單的find,load, saveOrUpdate,或者delete調(diào)用,?HibernateTemplate提供了可選的簡單方法可以用來替換這種一行的callback實(shí)現(xiàn)。 此外,Spring提供了一個(gè)簡便的HibernateDaoSupport基類, 這個(gè)基類提供了setSessionFactory方法來接受一個(gè)SessionFactory,getSessionFactory?以及getHibernateTemplate方法是子類使用的。這樣對(duì)于典型的需求就可以有很簡單的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);} }11.2.4.?利用AOP攔截器(Interceptor)取代Template
Spring的AOP?HibernateInterceptor是作為HibernateTemplate的替換, 它在一個(gè)委托的try/catch塊中直接寫Hibernate代碼, 以及在application context中的一個(gè)interceptor定義,從而代替callback的實(shí)現(xiàn)。 下面演示了在application context中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才能工作。 getSession方法中的”false”標(biāo)志為了確保Session已經(jīng)存在, 否則SessionFactoryUtils會(huì)創(chuàng)建一個(gè)新的。 如果線程中已經(jīng)存在一個(gè)?SessionHolder比如一個(gè)HibernateTransactionManager事務(wù) ,那SessionFactoryUtils?就肯定會(huì)自動(dòng)參與進(jìn)來。HibernateTemplate?在內(nèi)部使用SessionFactoryUtils, 他們是一樣的底層結(jié)構(gòu)。?HibernateInterceptor最主要的優(yōu)點(diǎn)是允許在數(shù)據(jù)訪問代碼中拋出checked應(yīng)用異常, 而HibernateTemplate在回調(diào)中嚴(yán)格限制只能拋出未檢查異常。 值得注意的是我們可以把各種check以及拋出應(yīng)用異常推遲到回調(diào)之后。 Interceptor的主要缺點(diǎn)是它需要在context進(jìn)行特殊的裝配。HibernateTemplate的簡便方法在很多場(chǎng)景下更簡單些
11.2.5.?編程式的事務(wù)劃分
在這些低層次的數(shù)據(jù)訪問服務(wù)之上,可以在應(yīng)用的高層次上劃分事務(wù), 讓事務(wù)橫跨多個(gè)操作。這里對(duì)于相關(guān)的業(yè)務(wù)對(duì)象(BO)同樣沒有實(shí)現(xiàn)接口的限制, 它只需要一個(gè)Spring的PlatformTransactionManager。同SessionFactory一樣, 這個(gè)manager可以來自任何地方, 但是最好是作為一個(gè)經(jīng)由setTransactionManager?方法設(shè)置的bean引用, 例如,用setProductDao方法來設(shè)置productDAO. 下面演示了在Srping application context中一個(gè)transaction manager和一個(gè)業(yè)務(wù)對(duì)象的定義, 以及具體的業(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);...}});} }11.2.6.?聲明式的事務(wù)劃分
作為可選的,我們可以使用Spring的AOP TransactionInterceptor來替換事務(wù)劃分的手工代碼, 這需要在application context中定義interceptor。 這個(gè)方案使得你可以把業(yè)務(wù)對(duì)象從每個(gè)業(yè)務(wù)方法中重復(fù)的事務(wù)劃分代碼中解放出來。 此外,像傳播行為和隔離級(jí)別等事務(wù)概念能夠在配置文件中改變,而不會(huì)影響業(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_REQUIREDproduct.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的應(yīng)用異常在callback代碼中拋出, 而TransactionTemplate?在callback中嚴(yán)格要求unchecked異常。?TransactionTemplate?會(huì)在一個(gè)unchecked異常拋出時(shí)觸發(fā)一個(gè)rollback, 或者當(dāng)事務(wù)被應(yīng)用程序通過TransactionStatus標(biāo)記為rollback-only。?TransactionInterceptor默認(rèn)進(jìn)行同樣的操作,但是它允許對(duì)每個(gè)方法配置rollback方針。 一個(gè)簡便可選的創(chuàng)建聲明式事務(wù)的方法是:TransactionProxyFactoryBean, 特別是在沒有其他AOP interceptor牽扯到的情況下。對(duì)一個(gè)特定的目標(biāo)bean,?TransactionProxyFactoryBean用事務(wù)配置自己聯(lián)合proxy定義。 這樣就把配置工作減少為配置一個(gè)目標(biāo)bean以及一個(gè) proxy bean的定義(少了interceptor的定義)。 此外你也不需要指定事務(wù)方法定義在哪一個(gè)接口或類中。
<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>11.2.7.?事務(wù)管理策略
TransactionTemplate?和TransactionInterceptor?都是將真正的事務(wù)處理代理給一個(gè)PlatformTransactionManager實(shí)例, 比如在Hibernate應(yīng)用中它可以是一個(gè)HibernateTransactionManager?(對(duì)于單獨(dú)一個(gè)的Hibernat SessionFactory, 實(shí)質(zhì)上使用一個(gè)ThreadLocal的Session)或一個(gè)JtaTransactionManager?(代理給容器的JTA子系統(tǒng))。 你甚至可以使用自定義的PlatformTransactionManager的實(shí)現(xiàn)。 所以呢,如果你的應(yīng)用需要分布式事務(wù)的時(shí)候, 將原來的Hibernate事務(wù)管理轉(zhuǎn)變?yōu)镴TA之類的,只不過是改變配置文件的事情。 簡單地,將Hibernate transaction manager替換為Spring的JTA transaction實(shí)現(xiàn)。 事務(wù)的劃分和數(shù)據(jù)訪問代碼則不需要改變,因?yàn)樗麄兪褂玫氖峭ㄓ玫氖聞?wù)管理API。 對(duì)于橫跨多個(gè)Hibernate SessionFacotry的分布式事務(wù), 只需簡單地將JtaTransactionManager?同多個(gè)LocalSessionFactoryBean定義結(jié)合起來作為一個(gè)事務(wù)策略。 你的每一個(gè)DAO通過bean屬性得到各自的SessionFactory引用。 如果所有的底層JDBC datasource都是支持事務(wù)的容器, 那么只要一個(gè)業(yè)務(wù)對(duì)象使用了?JtaTransactionManager策略, 它就可以橫跨多個(gè)DAO和多個(gè)session factories來劃分事務(wù),而不需要特殊的對(duì)待.
<beans><bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName"><value>java:comp/env/jdbc/myds1</value></property></bean><bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiName"><value>java:comp/env/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>HibernateTransactionManager?和JtaTransactionManager?都使用了與容器無關(guān)的Hibernate事務(wù)管理器的lookup或JCA連接器(只要事務(wù)不是用EJB發(fā)起的), 從而考慮到在適當(dāng)JVM級(jí)別上的cache處理。 而且HibernateTransactionManager?能夠?yàn)槠胀ǖ腏DBC訪問代碼輸出JDBC Connection。 這就可以使得混合的Hibernate/JDBC數(shù)據(jù)訪問可以不用JTA而在高層次上進(jìn)行事務(wù)劃分, 只要它們使用的是同一個(gè)數(shù)據(jù)庫!
注釋, 對(duì)于使用TransactionProxyFactoryBean?聲明性劃分事務(wù)屬于可選方式,請(qǐng)參考for an alternative approach to using?第?7.4.1?節(jié) “BeanNameAutoProxyCreator,另一種聲明方式”.
11.2.8.?使用Spring管理的應(yīng)用Bean
一個(gè)Spring application context的定義能夠被很多種不同的上下文實(shí)現(xiàn)所讀取, 比如FileSystemXmlApplicationContext?和?ClassPathXmlApplicationContext以及XmlWebApplicationContext。 默認(rèn)的情況下,一個(gè)web應(yīng)用程序會(huì)從”WEB-INF/applicationContext.xml”中得到root context。 在所有的Spring應(yīng)用中,XML文件中定義的application context會(huì)把所有相關(guān)的application beans連接起來, 包括Hibernate session factory以及數(shù)據(jù)訪問和業(yè)務(wù)對(duì)象(就像上面定義的那些bean)。 它們中的大部分都不會(huì)意識(shí)到被Spring容器所管理,甚至在同其他bean合作的時(shí)候, 因?yàn)樗鼈儍H僅遵循JavaBeans的規(guī)范。一個(gè)bean屬性及可以表示值參數(shù),也可以是其他的合作bean。 下面的bean定義能夠作為Spring web MVC context的一部分,在最基礎(chǔ)的 application context中訪問business beans。
<bean id="myProductList" class="product.ProductListController"><property name="productService"><ref bean="myProductService"/></property> </bean>Spring web controller所需要的所有業(yè)務(wù)或數(shù)據(jù)訪問對(duì)象都是通過bean引用提供的, 所以在應(yīng)用得上下文中就不再需要手動(dòng)的bean lookup了。但是在與Struts一起使用, 或在一個(gè)EJB實(shí)現(xiàn)中甚至一個(gè)applet中使用,你仍然可以自己手動(dòng)地look up一個(gè)bean (意思大概是Spring不會(huì)影響你原來的使用方式)。因此,Spring beans事實(shí)上能夠在任何地方支持。 你只需要一個(gè)application context的引用,你可以在一個(gè)web 應(yīng)用中通過一個(gè)servlet context的屬性得到, 或者手動(dòng)地從一個(gè)文件或class path resource創(chuàng)建實(shí)例。
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");11.2.9.?容器資源 vs 本地資源
Spring的資源管理考慮到了在JNDI SessionFactory和local的SessionFactory之間的簡單切換, 對(duì)于JNDI DataSource也是這樣的,不需要修改一行代碼。 把資源定義放在容器中還是放在應(yīng)用程序本地中主要是由使用的事務(wù)策略決定的。 同Spring定義的本地SessionFactory相比,一個(gè)手動(dòng)注冊(cè)的JNDI SessionFactory并不會(huì)提供任何多余的好處。 如果通過Hibernate的JCA連接器進(jìn)行注冊(cè),對(duì)于參與JTA事務(wù)有明顯的好處, 尤其在EJB中。而Spring的transaction支持的一個(gè)重要好處就是不依賴于任何一個(gè)容器。 使用非JTA的策略配置,程序?qū)?huì)在獨(dú)立的或測(cè)試的環(huán)境下同樣正常工作。尤其在典型的單數(shù)據(jù)庫事務(wù)情況下, 這將是JTA的輕便和強(qiáng)大的替換方案。當(dāng)使用一個(gè)本地的EJB SLSB來驅(qū)動(dòng)事務(wù)時(shí), 盡管你可能只訪問一個(gè)數(shù)據(jù)庫而且僅僅通過CMT使用SLSBs的聲明性事務(wù),你仍然要依賴于EJB容器和JTA。 編程式使用JTA的替換方案依然需要J2EE環(huán)境。JTA不僅僅在自己這一方面而且在JNDI DataSource方面, 引入了容器依賴。對(duì)于非Spring的,JTA驅(qū)動(dòng)的Hibernate事務(wù),為了在適當(dāng)?shù)腏VM層次上做caching, 你必須使用Hibernate JCA連接器或者額外的Hibernate事務(wù)代碼及配置好的JTA事務(wù)。 Spring驅(qū)動(dòng)的事務(wù)能夠很好地使用本地定義的Hibernate SessionFacotry工作,就像使用本地的JDBC DataSource工作( 當(dāng)然訪問的必須是一個(gè)單獨(dú)的數(shù)據(jù)庫)。因此你只需要在面對(duì)分布式事務(wù)需求的時(shí)候退回到Spring的JTA事務(wù)策略。 必須要注意,一個(gè)JCA連接器需要特定容器的部署步驟,而且首先要支持JCA。 這要比使用本地資源定義和Spring驅(qū)動(dòng)事務(wù)來部署一個(gè)簡單的Web應(yīng)用麻煩多了。 而且你通常需要企業(yè)版本的容器,比如WebLogic的Express版本并不提供JCA。 一個(gè)僅使用本地資源并且事務(wù)僅跨越一個(gè)數(shù)據(jù)庫的Spring應(yīng)用將會(huì)在任何一種J2EE Web容器中工作 (不需要JTA,JCA或者EJB),比如Tomcat,Resin, 甚至普通的Jetty。 更多的是,這樣的中間層可以在桌面應(yīng)用或測(cè)試用例中簡單地重用。綜上所述:如果你不使用EJB, 堅(jiān)持使用本地SessionFactory的創(chuàng)建和Spring的HibernateTransactionManager?或JtaTransactionManager。 你可得到包括適當(dāng)?shù)腏VM層次上的caching以及分布式事務(wù)在內(nèi)的所有好處,卻不會(huì)有任何容器部署的麻煩。 通過JCA連接器的Hibernate SessionFactory 的JNDI注冊(cè)僅僅在EJB中使用會(huì)帶來好處。
11.2.10.?舉例
Spring發(fā)行包中的Petclinic這個(gè)例子提供了可選的DAO實(shí)現(xiàn)和application context配置文件 (分別針對(duì)Hibernaet, JDBC, 和Apache OJB)。因此Petclinic可以作為可工作的示例應(yīng)用, 闡明了在Spring web應(yīng)用中如何使用Hibernate。它也利用了不同事務(wù)策略下的聲明式事務(wù)劃分。
11.3.?JDO
ToDo
11.4.?iBATIS
Spring通過org.springframework.orm.ibatis包來支持iBATIS SqlMaps 1.3.x和2.0。 iBATIS的支持非常類似于Hibernate的支持,它支持同樣的template樣式的編程, 而且同Hibernate一樣,iBatis的支持也是和Spring的異常體系一起工作,他會(huì)讓你喜歡上Spring所有的IoC特性。
11.4.1.?1.3.x和2.0的概覽和區(qū)別
Spring對(duì)iBATIS SqlMaps1.3和2.0都提供了支持。首先讓我們來看一看兩個(gè)之間的區(qū)別。
表?11.1.?iBATIS SqlMaps supporting classes for 1.3 and 2.0
| Creation of SqlMap | SqlMapFactoryBean | SqlMapClientFactoryBean |
| Template-style helper class | SqlMapTemplate | SqlMapClientTemplate |
| Callback to use MappedStatement | SqlMapCallback | SqlMapClientCallback |
| Super class for DAOs | SqlMapDaoSupport | SqlMapClientDaoSupport |
11.4.2.?創(chuàng)建SqlMap
使用iBATIS SqlMaps涉及創(chuàng)建包含語句和結(jié)果對(duì)應(yīng)關(guān)系的配置文件。 Spring會(huì)負(fù)責(zé)使用SqlMapFactoryBean或SqlMapClientFactoryBean來讀取這些配置文件, 其中后一個(gè)類是同SqlMaps2.0聯(lián)合使用的。
public class Account {private String name;private String email;public String getName() {return this.name;}public void setName(String name) {this.name = name;}public String getEmail() {return this.email;}public void setEmail(String email) {this.email = email;} }假設(shè)我們要映射這個(gè)類,我們就要?jiǎng)?chuàng)建如下的SqlMap。通過使用查詢,我們稍后可以用email addersses來取得users.?Account.xml:
<sql-map name="Account"><result-map name="result" class="examples.Account"><property name="name" column="NAME" columnIndex="1"/><property name="email" column="EMAIL" columnIndex="2"/></result-map><mapped-statement name="getAccountByEmail" result-map="result">selectACCOUNT.NAME,ACCOUNT.EMAILfrom ACCOUNTwhere ACCOUNT.EMAIL = #value#</mapped-statement><mapped-statement name="insertAccount">insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#)</mapped-statement> </sql-map>在定義完SqlMap之后,我們必須為iBATIS創(chuàng)建一個(gè)配置文件 (sqlmap-config.xml):
<sql-map-config><sql-map resource="example/Account.xml"/></sql-map-config>iBATIS會(huì)從classpath讀取資源,所以要確保Account.xml文件在classpath上面。
用Spring,我們現(xiàn)在能夠很容易地通過?SqlMapFactoryBean創(chuàng)建SqlMap:
<bean id="sqlMap" class="org.springframework.orm.ibatis.SqlMapFactoryBean"><property name="configLocation"><value>WEB-INF/sqlmap-config.xml</value></property> </bean>11.4.3.?使用?SqlMapDaoSupport
SqlMapDaoSupport類是類似于HibernateDaoSupport?和JdbcDaoSupport的支持類。讓我們實(shí)現(xiàn)一個(gè)DAO:
public class SqlMapAccountDao extends SqlMapDaoSupport implements AccountDao {public Account getAccount(String email) throws DataAccessException {Account acc = new Account();acc.setEmail();return (Account)getSqlMapTemplate().executeQueryForObject("getAccountByEmail", email);}public void insertAccount(Account account) throws DataAccessException {getSqlMapTemplate().executeUpdate("insertAccount", account);} }正如你所看到的,我們使用SqlMapTemplate來執(zhí)行查詢。Spring會(huì)在創(chuàng)建如下所示的SqlMapAccountDao的時(shí)候已經(jīng)使用SqlMapFactoryBean為我們初始化了SqlMap。一切都準(zhǔn)備就緒了:
<!-- for more information about using datasource, have a look at the JDBC chapter --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"><property name="driverClassName"><value>${jdbc.driverClassName}</value></property><property name="url"><value>${jdbc.url}</value></property><property name="username"><value>${jdbc.username}</value></property><property name="password"><value>${jdbc.password}</value></property> </bean><bean id="accountDao" class="example.SqlMapAccountDao"><property name="dataSource"><ref local="dataSource"/></property><property name="sqlMap"><ref local="sqlMap"/></property> </bean>11.4.4.?事務(wù)管理
在使用iBATIS的應(yīng)用程序中增加聲明式事務(wù)管理是相當(dāng)?shù)暮唵巍?基本上你所需要做的唯一事情是: 在你的application context中增加一個(gè)transaction manager并且使用諸如?TransactionProxyFactoryBean聲明式地設(shè)定你的事務(wù)界限。 關(guān)于這方面的更多情況可以在第?7?章?事務(wù)管理中找到。
TODO elaborate!
from:?http://docs.huihoo.com/spring/zh-cn/orm.html
總結(jié)
以上是生活随笔為你收集整理的Spring - Java/J2EE Application Framework 应用框架 第 11 章 使用ORM工具进行数据访问的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring - Java/J2EE A
- 下一篇: Spring - Java/J2EE A