Spring Reference 1.1第十一章-使用ORM工具進行數據訪問

11.1.簡介
Spring在資源管理,DAO實現支持以及實物策略等方面提供了與Hibernate, JDO和iBATIS SQL映射的集成。 對Hibernate,Spring使用了很多IoC的方便的特性提供了一流的支持,幫助你處理很多典型的Hibernate整合的問題。所有的這些都遵守Spring通用的事務和DAO異常體系。

當您選擇使用O/R映射來創建數據訪問應用程序的時候,Spring的增加部分就會向您提供重要的支持。首先你應該瞭解的是,一旦你使用了Spring對O/R映射的支持,你不需要親自作所有的事情。在決定花費力氣,冒着風險建造類似的內部底層結構之前,我們都建議您考慮和利用Spring的解決方案。不管你使用的是何種技術,大部分的O/R映射支持都可以以library樣式被使用,因爲所有的東西都是被設計成一組可重複利用的JavaBeans。在ApplicationContext和BeanFactory中使用更是提供了配置和部署簡單的好處,因此,這一章裏的大多數例子都是在ApplicationContext中配置。

使用Spring構建你的ORM應用的好處包括:
l 避免綁定特殊的技術,允許mix-and-match的實現策略。雖然Hibernate非常強大,靈活,開源而且免費,但它還是使用了自己的特定的API。此外有人也許會爭辯:iBatis更輕便而且在不需要複雜的O/R映射策略的應用中使用也很優秀。能夠選擇的話,使用標準或抽象的API來實現主要的應用需求,通常是更好的。尤其,當你可能會因爲功能,性能或其他方面的原因而需要切換到另一個實現的時候。舉例來說,Spring對Hibernate事務和異常的抽象,以及能夠讓你輕鬆交換mapper和DAO對象(實現數據訪問功能)的IoC機制,這兩個特性可以讓你在不犧牲Hibernate性能的情況下,在你的應用程序中隔離Hibernate的相關代碼。處理DAO的高層次的service代碼不需要知道DAO的具體實現。這個方法可以很容易使用mix-and-match方案互不干擾地實現數據訪問層(比如在一些地方用Hibernate,一些地方使用JDBC,其他地方使用iBatis),mix-and-match有利於處理遺留下來的代碼以及利用各種技術(JDBC,Hibernate,iBatis)的長處。
l 測試簡單。Spring的IoC使得很容易替換掉不同的實現,Hibernate SessionFacotory的位置,datasource, 事務管理, 映射對象的實現。這樣就很容易隔離測試持久化相關代碼的各個部分。
l 普通的資源管理。Spring的application context能夠處理諸如Hibernate 的SessionFactory, JDBC的datasource,iBatis的SQLMaps配置對象以及其他相關資源的定位和配置。這使得這些配置的值很容易被管理和修改。Spring提供了有效,簡單和安全的Hibernate Session處理。一般的使用Hibernate的代碼則需要使用同一個Hibernate Session對象以確保有效和恰當地事務處理。而Spring讓我們可以很容易透明地創建和綁定一個session到當前線程;你可以使用以下兩種辦法之一:聲明式的AOP方法攔截器,或通過使用一個外部的template包裝類在Java代碼層次實現。這樣,Spring就解決了在很多Hibernate論壇上出現的使用問題。
l 異常包裝。Spring能夠包裝Hibernate異常,把它們從專有的,checked exception變爲一組抽象的runtime exception。這樣你就可以僅僅在恰當的層處理大部分的不可恢復的異常,使你避免了很多討厭的catch/throw以及異常聲明。你還是可以在你需要的地方捕捉和處理異常。回想一下JDBC異常(包括與DB相關的方言)被轉變爲同樣的異常體系,這就意味着你可以在一致的編程模型中處理JDBC操作。
l 綜合的事務管理。Spring允許你包裝你的ORM代碼,通過使用聲明式的AOP方法攔截器或者在代碼級別使用外部的template包裝類。不管使用哪一種,事務相關的語義都會爲你處理,萬一有異常發生也會幫你做適當的事務操作(比如rollback)。就象我們下面要討論的一樣,你能夠使用和替換各種transaction managers,卻不會使你的Hibernate相關的代碼受到影響。更好的是,JDBC相關的代碼可以完全和Hibernate代碼integrate transactionaly。這對於處理那些沒有用Hibernate或iBatis實現的功能非常有用。

11.2.Hibernate
11.2.1.資源管理
典型的應用經常會被重複的資源管理代碼搞胡亂。很多項目嘗試創造自己的方案解決這個問題,有時會爲了編程方便犧牲適當的故障處理。對於恰當的資源處理Spring提倡令人矚目的簡單的解決方案:使用templating的IoC,比如基礎的class和回調接口,或者提供AOP攔截器。基礎的類負責固定的資源處理,以及將特定的異常轉換爲unchecked異常體系。Spring引進了DAO異常體系,可適用於任何數據訪問策略。
對於直接使用JDBC的情況,前面章節提到的JdbcTemplate類負責處理connection,正確地把SQLExeption變爲DataAccessException體系(包括將與數據庫相關的SQL錯誤代碼變成有意義的異常類)。它同時支持JTA和JDBC事務,通過它們各自的Spring transaction managers。
Spring同樣也提供了對Hibernate和JDO的支持:一個HibernateTemplate/JdoTemplate類似於JdbcTemplate,HibernateInterceptor/JdoInterceptor,以及一個Hibernate/JDO transaction manager。主要的目的是:能夠清晰地劃分應用層次而不管使用何種數據訪問和事務技術;使應用對象之間的耦合鬆散。業務對象(BO)不再依賴於數據訪問和事務策略;不再有硬編碼的資源lookup;不再有難於替換的singletons;不再有自定義的服務註冊。一個簡單且堅固的方案連接了應用對象,並且使它們可重用儘可能地不依賴容器。雖然所有的數據訪問技術都能獨立使用,但是與Spring application context結合更好一些,它提供了基於xml的配置和普通的與Spring 無關的JavaBean實例。在典型的Spring app中,很多重要的對象都是JavaBeans:數據訪問template,數據訪問對象(使用template),transaction managers, 業務對象(使用數據訪問對象和transaction managers),web view resolvers, web controller(使用業務對象)等等。

11.2.2.在application context中定義資源
爲了避免將應用對象貼緊硬編碼的資源lookup,Spring允許你像定義普通bean一樣在application context中定義諸如JDBC DataSource,Hibernate SessionFactory的資源。需要訪問這些資源的應用對象只需要持有這些預定義實例的引用。下面的代碼演示如何創建一個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>
你可以將一個JNDI定位的DataSource換爲一個本地定義的如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>


當然你也可以把本地的SessionFactory換爲JNDI定位的,但是如果不是在EJB上下文中,這是不需要的。(查看“容器資源 vs 本地資源”一節)

11.2.3.反轉控制:Template和Callback
對於可以成爲定製的數據訪問對象或業務對象的方法來說,基本的模板編程模型看起來像下面所示的代碼那樣。對於外部對象沒有任何實現特定接口的要求,它只需要提供一個Hibernate的SessionFacotry。它可以從任何地方得到,比較適宜的方法是作爲從Spring 的application context中得到的bean引用:通過簡單的setSessionFactory這個bean屬性setter。下面的代碼顯示了在application context中一個DAO的定義,它引用了上面定義的SessionFactory,同時展示了一個DAO方法的具體實現。
<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);
              //對結果list做一些處理
              return result;
            }
          }
        );
    }
}


一個callback的實現能在任何的Hibernate數據訪問中有效使用。HibernateTemplate會確保Sessions正確的打開和關閉,並且會自動參與事務。Template實例是線程安全的,可重用的,因此可以作爲外部類的實例變量而被保持。對於簡單的一步操作,例如簡單的find,load, saveOrUpdate,或者delete調用,HibernateTemplate提供了可選的簡單方法可以用來替換這種一行的callback實現。此外,Spring提供了一個簡便的HibernateDaoSupport基類,這個基類提供了setSessionFactory()方法來接受一個SessionFactory,getSessionFactory()以及getHibernateTemplate()方法是子類使用的。這樣對於典型的需求就可以有很簡單的DAO實現:
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的替換,它在一個委託的try/catch塊中直接寫Hibernate代碼,以及在application context中的一個interceptor定義,從而代替callback的實現。下面演示了在application context中DAO,interceptor以及proxy定義,同時還有一個DAO方法的具體實現:
<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 ProdctDao {
    public List loadProdctsByCategory(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);
        }
    }
}


這個方法必須要有一個HibernateInterceptor才能工作。getSession方法中的”false”標誌爲了確保Session已經存在,否則SessionFactoryUtils會創建一個新的。如果線程中已經存在一個SessionHolder比如一個HibernateTransactionManager事務,那SessionFactoryUtils 就肯定會自動參與進來。HibernateaTemplate在內部使用SessionFactoryUtils,他們是一樣的底層結構。HibernateInterceptor最主要的優點是允許在數據訪問代碼中拋出checked應用異常,而HibernateTemplate在回調中嚴格限制只能拋出unchecked exception。值得注意的是我們可以把各種check以及拋出應用異常推遲到回調之後。Interceptor的主要缺點是它需要在context進行特殊的裝配。HibernateTemplate的簡便方法在很多場景下更簡單些。

11.2.5.編程式的事務劃分
在這些低層次的數據訪問服務之上,可以在應用的高層次上劃分事務,讓事務橫跨多個操作。這裏對於相關的業務對象(BO)同樣沒有實現接口的限制,它只需要一個Spring的PlatformTransactionManager。同SessionFactory一樣,這個manager可以來自任何地方,但是最好是作爲一個經由setTransactionManager方法設置的bean引用。下面演示了在Srping application context中一個transaction manager和一個業務對象的定義,以及具體的業務方法是如何實現的:
<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;
    //上面兩個的setter方法

    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 productChange = productDao.laodProductsByCategory(category);
                ……
              }
            }
        );
    }
}



11.2.6.聲明式的事務劃分
作爲可選的,我們可以使用Spring的AOP TransactionInterceptor來替換事務劃分的手工代碼,這需要在application context中定義interceptor。這個方案使得你可以把業務對象從每個業務方法中重複的事務劃分代碼中解放出來。此外,像傳播行爲和隔離級別等事務概念能夠在配置文件中改變,而不會影響業務對象的實現。
<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的應用異常在callback代碼中拋出,而TransactionTemplate在callback中嚴格要求unchecked異常。TransactionTemplate會在一個unchecked異常拋出時觸發一個rollback,或者當事務被應用程序通過TransactionStatus標記爲rollback-only。
TransactionInterceptor默認進行同樣的操作,但是它允許對每個方法配置rollback方針。一個簡便可選的創建聲明式事務的方法是:TransactionProxyFactoryBean,特別是在沒有其他AOP interceptor牽扯到的情況下。對一個特定的目標bean,TransactionProxyFactory用事務配置自己聯合proxy定義。這樣就把配置工作減少爲配置一個目標bean以及一個 proxy bean的定義(少了interceptor的定義)。此外你也不需要指定事務方法定義在哪一個接口或類中。
<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.事務管理策略
TransactionTemplate和TransactionInterceptor都是將真正的事務處理代理給一個PlatformTransactionManager實例,比如在Hibernate應用中它可以是一個HibernateTransactionManager(對於單獨一個的Hibernat SessionFactory,實質上使用一個ThreadLocal的Session)或一個JtaTransactionManager(代理給容器的JTA子系統)。你甚至可以使用自定義的PlatformTransactionManager的實現。所以呢,如果你的應用需要分佈式事務的時候,將原來的Hibernate事務管理轉變爲JTA之類的,只不過是改變配置文件的事情。簡單地,將Hibernate transaction manager替換爲Spring的JTA transaction實現。事務的劃分和數據訪問代碼則不需要改變,因爲他們使用的是通用的事務管理API。
對於橫跨多個Hibernate SessionFacotry的分佈式事務,只需簡單地將JtaTransactionManager同多個LocalSessionFactoryBean定義結合起來作爲一個事務策略。你的每一個DAO通過bean屬性得到各自的SessionFactory引用。如果所有的底層JDBC datasource都是支持事務的容器,那麼只要一個業務對象使用了JtaTransactionManager策略,它就可以橫跨多個DAO和多個session factories來劃分事務,而不需要特殊的對待:
<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都使用了與容器無關的Hibernate事務管理器的lookup或JCA連接器(只要事務不是用EJB發起的),從而考慮到在適當JVM級別上的cache處理。而且HibernateTransactionManager能夠爲普通的JDBC訪問代碼輸出JDBC Connection。這就可以使得混合的Hibernate/JDBC數據訪問可以不用JTA而在高層次上進行事務劃分,只要它們使用的是同一個數據庫。

11.2.8.使用Spring管理的應用Bean
一個Spring application context的定義能夠被很多種不同的上下文實現所讀取,比如FileSystemXmlApplicationContext和ClassPathXmlApplicationContext以及XmlWebApplicationContext。默認的情況下,一個web應用程序會從”WEB-INF/applicationContext.xml”中得到root context。在所有的Spring應用中,XML文件中定義的application context會把所有相關的application beans連接起來,包括Hibernate session factory以及數據訪問和業務對象(就像上面定義的那些bean)。它們中的大部分都不會意識到被Spring容器所管理,甚至在同其他bean合作的時候,因爲它們僅僅遵循JavaBeans的規範。一個bean屬性及可以表示值參數,也可以是其他的合作bean。下面的bean定義能夠作爲Spring web MVC context的一部分,在root application context中訪問business beans。
<bean id="myProductList" class="product.ProductListController">
<property name="productService">
<ref bean="myProductService"/>
</property>
</bean>


Spring web controller所需要的所有業務或數據訪問對象都是通過bean引用提供的,所以在應用得上下文中就不再需要手動的bean lookup了。但是在與Struts一起使用,或在一個EJB實現中甚至一個applet中使用,你仍然可以自己手動地look up一個bean(意思大概是Spring不會影響你原來的使用方式)。因此,Spring beans事實上能夠在任何地方支持。你只需要一個application context的引用,你可以在一個web 應用中通過一個servlet context的屬性得到,或者手動地從一個文件或class path resource創建實例。
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之間的簡單切換,對於JNDI DataSource也是這樣的,不需要修改一行代碼。把資源定義放在容器中還是放在應用程序本地中主要是由使用的事務策略決定的。同Spring定義的本地SessionFactory相比,一個手動註冊的JNDI SessionFactory並不會提供任何多餘的好處。如果通過Hibernate的JCA連接器進行註冊,對於參與JTA事務有明顯的好處,尤其在EJB中。而Spring的transaction支持的一個重要好處就是不依賴於任何一個容器。使用非JTA的策略配置,程序將會在獨立的或測試的環境下同樣正常工作。尤其在典型的單數據庫事務情況下,這將是JTA的輕便和強大的替換方案。當使用一個本地的EJB SLSB來驅動事務時,儘管你可能只訪問一個數據庫而且僅僅通過CMT使用SLSBs的聲明性事務,你仍然要依賴於EJB容器和JTA。編程式使用JTA的替換方案依然需要J2EE環境。JTA不僅僅在自己這一方面而且在JNDI DataSource方面,引入了容器依賴。對於非Spring的,JTA驅動的Hibernate事務,爲了在適當的JVM層次上做caching,你必須使用Hibernate JCA連接器或者額外的Hibernate事務代碼及配置好的JTA事務。Spring驅動的事務能夠很好地使用本地定義的Hibernate SessionFacotry工作,就像使用本地的JDBC DataSource工作(當然訪問的必須是一個單獨的數據庫)。因此你只需要在面對分佈式事務需求的時候退回到Spring的JTA事務策略。必須要注意,一個JCA連接器需要特定容器的部署步驟,而且首先要支持JCA。這要比使用本地資源定義和Spring驅動事務來部署一個簡單的Web應用麻煩多了。而且你通常需要企業版本的容器,比如WebLogic的Express版本並不提供JCA。一個僅使用本地資源並且事務僅跨越一個數據庫的Spring應用將會在任何一種J2EE Web容器中工作(不需要JTA,JCA或者EJB),比如Tomcat,Resin, 甚至普通的Jetty。更多的是,這樣的中間層可以在桌面應用或測試用例中簡單地重用。綜上所述:如果你不使用EJB,堅持使用本地SessionFactory的創建和Spring的HibernateTransactionManager或JtaTransactionManager。你可得到包括適當的JVM層次上的caching以及分佈式事務在內的所有好處,卻不會有任何容器部署的麻煩。通過JCA連接器的Hibernate SessionFactory 的JNDI註冊僅僅在EJB中使用會帶來好處。

11.2.10.舉例
Spring發行包中的Petclinic這個例子提供了可選的DAO實現和application context配置文件(分別針對Hibernaet, JDBC, 和Apache OJB)。因此Petclinic可以作爲可工作的示例應用,闡明瞭在Spring web應用中如何使用Hibernate。它也利用了不同事務策略下的聲明式事務劃分。

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的異常體系一起工作,他會讓你喜歡上Spring所有的IoC特性。

11.4.1.1.3.x和2.0的概覽和區別
Spring對iBATIS SqlMaps1.3和2.0都提供了支持。首先讓我們來看一看兩個之間的區別。
 

11.4.2.創建SqlMap
使用iBATIS SqlMaps涉及創建包含語句和結果對應關係的配置文件。Spring會負責使用SqlMapFactoryBean或SqlMapClientFactoryBean來讀取這些配置文件,其中後一個類是同SqlMaps2.0聯合使用的。
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;
}
}


假設我們要映射這個類,我們就要創建如下的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">
select
ACCOUNT.NAME,
ACCOUNT.EMAIL
from ACCOUNT
where ACCOUNT.EMAIL = #value#
</mapped-statement>

<mapped-statement name="insertAccount">
insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#)
</mapped-statement>
</sql-map>


在定義完SqlMap之後,我們必須爲iBATIS創建一個配置文件(sqlmap-config.xml):
<sql-map-config>
<sql-map resource="example/Account.xml"/>
</sql-map-config>


iBATIS會從classpath讀取資源,所以要確保Account.xml文件在classpath上面。

用Spring,我們現在能夠很容易地通過SqlMapFactoryBean創建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的支持類。讓我們實現一個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來執行查詢。Spring會在創建如下所示的SqlMapAccountDao的時候已經使用SqlMapFactoryBean爲我們初始化了SqlMap。一切都準備就緒了:
<!-- 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.事務管理
在使用iBATIS的應用程序中增加聲明式事務管理是相當的簡單。基本上你所需要做的唯一事情是:在你的application context中增加一個transaction manager並且使用諸如TransactionProxyFactoryBean聲明式地設定你的事務界限。關於這方面的更多情況可以在第七章:事務管理中找到。

TODO elaborate!
                                                                  ----rongsantang
參考資源:
參與論壇討論:http://www.matrix.org.cn/forum.asp
更多技術文章:http://www.matrix.org.cn/article.asp
Matrix java門戶:http://www.matrix.org.cn
原文地址:http://www.matrix.org.cn/article/1051.html
ezerg 編程小語
發佈了108 篇原創文章 · 獲贊 1 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章