本文研究的主要是Spring的事務機制的相關內容,具體如下。
通常有兩種事務策略:全局事務和局部事務。全局事務可以跨多個事務性資源(即數據源,典型的是數據庫和消息隊列),通常都需要J2EE應用服務器的管理,其底層需要服務器的JTA支持。而局部事務則與底層採用的持久化技術有關,如果底層直接使用JDBC,需要用Connection對象來操事務。如果採用Hibernate持久化技術,則需要使用session對象來操作事務。
通常的,使用JTA事務,JDBC事務及Hibernate事務的編程流程大致如下,
上圖也可以看出,採用傳統事務編程,程序代碼必須和具體的事務策略的API耦合,如果應用需要切換一種策略,意味著需要大幅修改代碼。但是如果使用Spring事務的話,就不會有這個問題了。
Sring沒有提供任何事務支持,它只是負責包裝底層的事務,而在Spring層面,對外提供統一的編程API。 Spring事務的核心是PlatformTransactionManager接口,
PlatformTransactionManager代表與具體類型無關的事務接口,可以代表任何事務,包括JDBC事務,Hibernate事務,甚至是JTA事務。
Springa事務機制是一種典型的策略模式,PlatformTransactionManager代表事務管理接口,但它並不知道到底如何管理事務,它只要求事務管理提供開始事務getTransaction(),提交事務commit()和回滾事務rollback()這三個方法,但具體如何實現則交給其實現類完成。編程人員只需要在配置文件中根據具體需要使用的事務類型做配置,Spring底層就自動會使用具體的事務實現類進行事務操作,而對於程序員來說,完全不需要關心底層過程,只需要面向PlatformTransactionManager接口進行編程即可。 PlatformTransactionManager接口中提供瞭如下方法: getTransaction(..), commit(); rollback();這些都是與平台無關的事務操作。
getTransaction()的完整寫法為TransactionStatus getTransaction(TransactionDefinition definiton)
這個方法用來返回一個事務對象,其中的參數TransactionDefinition 則可以為事務對象指定各種屬性,通常可以指定事務的隔離屬性, 傳播屬性, 超時,只讀這幾個屬性。
Spring具體的事務管理需要在配置文件中配置好PlatformTransactionManager,下面是不同類型的事務對應的Spring配置。
JDBC數據源的局部事務管理器的配置如下,
<!-- 定義數據源Bean,使用C3P0數據源實現,並註入數據源的必要信息--> <bean id="dataSource" destroy-method="close" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://localhost/test" p:user="root" p:password="" p:maxPoolSize="40" p:minPoolSize="2" p:initialPoolSize="2" p:maxIdleTime="30" /> <!-- 配置JDBC數據源的局部數據管理器,使用DataSourceTransactionManager類--> <bean id="transactionManager" p:dataSource-ref="dataSource" />
容器管理的JTA全局事務管理器的配置如下,
<bean id="dataSource" p:jndiName="jdbc/jpetstore" /><!-- 使用JtaTransactionManager類, 該類實現了PlatformTransactionManager接口--><!-- 使用JTA全局事務,Spring容器可以自行從Java EE服務器中獲取事務性資源,無需依賴注入--><bean id="transactionManager" />
對於JTA全局事務,只需要指定事務管理器的實現類JtaTransactionManager即可,Spring容器會自行從J2EE服務器獲取數據源,無需顯式註入進事務管理器。
基於Hibernate持久化技術的Spring局部事務配置如下,
<!-- 定義數據源Bean,使用C3P0數據源實現,並註入數據源的必要信息--> <bean id="dataSource" destroy-method="close" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://localhost/test" p:user="root" p:password="" p:maxPoolSize="40" p:minPoolSize="2" p:initialPoolSize="2" p:maxIdleTime="30" /> <!-- 定義Hibernate的SessionFactory, SessionFactory需要依賴數據源,注入dataSource --> <bean id="sessionFactory" p:dataSource-ref="dataSource"> <!-- annotatedClasses用來列出全部持久化類--> <property name="annotatedClasses"> <list> <!-- 以下用來列出所有PO類--> <value>com.entity.User</value> </list> </property> <!-- 定義Hibernate的sessionFactory屬性--> <property name="hibernateProperties"> <props> <!-- 指定Hibernate的連接方言--> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <!-- 是否根據Hibernate映射表創建數據表--> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- 配置Hibernate的局部數據管理器,使用HibernateTransactionManager類--> <!-- 該類是PlatformTransactionManager接口針對Hibernate的特定實現--> <!-- 配置HibernateTransactionManager需要注入sessionFactory --> <bean id="transactionManager" p:sessionFactory-ref="sessionFactory" />
Spring事務如果採用Hibernate策略,一般需要配置三點:數據源, sessionFactory, 事務管理器。
如果底層採用Hibernate持久層技術,而事務採用JTA全局事務時,配置如下,
<!-- 配置JTA數據源--> <bean id="dataSource" p:jndiName="jdbc/jpetstore" /> <!-- 定義Hibernate的SessionFactory, SessionFactory需要依賴數據源,注入dataSource --> <bean id="sessionFactory" p:dataSource-ref="dataSource"> <!-- annotatedClasses用來列出全部持久化類--> <property name="annotatedClasses"> <list> <!-- 以下用來列出所有PO類--> <value>com.entity.User</value> </list> </property> <!-- 定義Hibernate的sessionFactory屬性--> <property name="hibernateProperties"> <props> <!-- 指定Hibernate的連接方言--> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> <!-- 是否根據Hibernate映射表創建數據表--> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- 使用JtaTransactionManager類,該類是PlatformTransactionManager接口的實現類--> <!-- 針對全局事務管理的特定實現--> <bean id="transactionManager" />
這與前面的基於Hibernate的Spring事務比起來,就是將數據源換成了JNDI數據源, 將事務管理器換成了JtaTransactionManager.
對於JTA全局事務,因為需要底層應用服務器的支持,而不同應用服務器所提供的JTA全局事務可能存在細節上的差異,因此實際配置全局事務管理器時可能需要使用JtaTransactionManager的子類,例如Oracle的JavaEE應用服務器提供的OC4JJtaTransactionManager,Oracle為WebLogic提供的WebLogicJtaTransactionManager, IBM為WebSphere提供的WebSphereUowTransactionManager等。
從上面各種事務類型的Spring配置可以看出,當應用程序採用Spring事務管理時,應用程序無需與具體的事務API耦合,應用程序只需要面向PlatormTransactionManager接口編程即可,ApplicationContext會根據配置文件選擇合適的事務策略實現類(即PlatormTransactionManager的實現類)。
那麼在具體在Spring中如何進行事務控制編程呢,通常有兩種方式,
編程式事務管理:就是直接在代碼中使用PlatormTransactionManager提供的三個抽象方法進行事務流程控制。也可以在Spring容器中獲取PlatormTransactionManager類型的Bean,該Bean總是PlatormTransactionManager的具體實現類的實例,具體的實現類則由ApplicationContext按照策略模式進行選擇,編程人員無需關心,只需要面向接口編程即可。
聲明式事務管理:這種方式不需要講事務控制流程寫入代碼中,而是通過AOP的方式,完全由配置文件完成事務的織入。即XML配置文件可以為業務組件配置事務代理,事務代理為業務組件提供事務控制。現在這種方式是最好的,源碼侵入性最低。
當使用聲明式事務時,只需要寫好配置文件,配置需要事務控制的組件種類,業務組件就會在AOP機制下被織入事務控制,而編程人員不需要寫任何事務管理代碼,可以專注於業務組件的開發。因此通常都推薦使用聲明式事務管理。
Spring的XML Schema方式提供了簡潔的事務配置策略,通過命名空間<tx:advice>來配置一個事務增強處理,其中可以指定事務的各種屬性(例如隔離屬性, 傳播屬性, 超時,只讀屬性等等),然後通過<aop:config>標籤可以將事務的增強與AOP的切入點(即Bean的執行方法)進行綁定,從而實現對Bean的方法織入事務操作。下面是一個簡單的例子,配置一個NewsDaoImpl bean進行數據操作,使用c3p0數據源,Spring的JDBC事務管理器,在<tx:advice對事務設置屬性。
完整的Spring配置如下,
<?xml version="1.0" encoding="UTF-8"?><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" 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-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <!-- 定義數據源Bean,使用C3P0數據源實現,並註入數據源的必要信息--> <bean id="dataSource" destroy-method="close" p:driverClass="com.mysql.jdbc.Driver" p:jdbcUrl="jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=UTF-8" p:user="root" p:password="" p:maxPoolSize="40" p:minPoolSize="2" p:initialPoolSize="2" p:maxIdleTime="30" /> <!-- 配置JDBC數據源的局部數據管理器,使用DataSourceTransactionManager類--> <bean id="transactionManager" p:dataSource-ref="dataSource" /> <!-- 配置一個業務邏輯Bean --> <bean id="newsDao" p:ds-ref="dataSource" /> <!-- 配置事務增強處理, 指定事務管理器--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 用於配置詳細的事務定義--> <tx:attributes> <!-- 所有以get開頭的方法都是只讀的--> <tx:method name="get*" read-only="true" /> <!-- 其他方法默認都適用事務,指定超時5秒--> <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="5" /> </tx:attributes> </tx:advice> <aop:config> <!-- 配置一個切入點,匹配impl包下所有以impl結尾的類裡的所有方法的執行--> <aop:pointcut expression="execution(* com.dao.impl.*Impl.*(..))" id="myPointcut" /> <!-- 將切入點myPointcut和增強txAdvice綁定--> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" /> <!-- 再配置一個切入點,匹配impl包下所有以abc開頭類裡的所有方法的執行--> </aop:config></beans>
NewsDaoImpl代碼中,則是插入重複數據到表中,
package com.dao.impl;import javax.sql.DataSource;import org.springframework.jdbc.core.JdbcTemplate;import com.dao.NewsDao;public class NewsDaoImpl implements NewsDao {private DataSource ds;public void setDs(DataSource ds) {this.ds = ds;}@Override public void insert(String title, String content) {//c3p0數據池的用法JdbcTemplate jt = new JdbcTemplate(ds);jt.update("insert into news_inf" + " values(100,?,?)", title, content);jt.update("insert into news_inf" + " values(100,?,?)", title, content);//如果沒有事務控制,則第一條記錄可以被插入//如果增加事務控制,將發現第一條記錄也插不進去}}下面是測試方法,
public static void test3() { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans4JDBC.xml"); //獲取事務代理Bean NewsDao dao = (NewsDao)ctx.getBean("newsDao", NewsDao.class); dao.insert("java編程核心思想", "輕量級Java EE開發"); System.out.println("執行完畢"); }執行測試方法會發現拋出異常(因為有重複數據),而又因為事務控制,數據庫中講不會有數據插入。
可以看到上面例子中,通常對於XML Schema的配置中,其實就是對一個普通的Bean做了AOP配置,織入一個advice增強,而advice增強中則配置一個事務管理器,事務管理器又依賴數據源。
對於<aop:advisor>中,將advice和切入點的綁定,而在Spring底層是由Bean後處理器完成(例如BeanNameAutoProxyCreator, DefaultAdvisorAutoProxyCreator),其本質就是動態代理。
另外,在<tx:advice>配置增強中,還可以為事務指定再遇到特定異常時,進行強制rollback和強制不rollback,即rollback-for="xxxException", no-rollback-for="xxxException"
除了使用XML Schema的方法之外,也可以直接在方法上添加@Transaction註解,使這個方法具有事務屬性。 在@Transaction中可以為事務配置各種屬性(例如隔離屬性, 傳播屬性, 超時,只讀屬性等等),此外,還需要在在XML配置中加入<tx:annotation-triven配置表明Spring會根據註解來配置事務代理,這樣,事務的屬性配置和AOP切入配置就可以只通過一步(直接通過註解配置在方法名上)完成了。
<tx:annotation-driven transaction-manager="transactionManager" />
NewsDaoImpl.
@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT, timeout=5)@Overridepublic void insert(String title, String content) {以上就是本文關於Spring的事務機制實例代碼的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!