在基於數據庫的應用中,事務是非常重要的。為了方便使用,Spring提供了基於XML和基於注解的方式配置事務,思路都是使用AOP,在特定的切入點統一開啟事務,以方法為粒度進行事務控制。並且定義了事務的傳播屬性,規定了配置了事務的方法互相嵌套調用時的行為准則:
- PROPAGATION_REQUIRED:支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
- PROPAGATION_SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行。
- PROPAGATION_MANDATORY:支持當前事務,如果當前沒有事務,就拋出異常。
- PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務,把當前事務掛起。
- PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
- PROPAGATION_NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。
- PROPAGATION_NESTED:支持當前事務,新增Savepoint點,與當前事務同步提交或回滾。
越是統一的東西,靈活性就越差。事務是業務邏輯的一部分,有時候事務的開啟並不能以方法為粒度進行統一控制,這時候很多開發人員的做法是"將就"基於AOP的事務配置的方法,將需要開啟事務的邏輯單獨拆出方法進行控制,這其實是一種妥協,而且有時候並不見得稱心如意。那麼Spring除了基於AOP,還有別的方式管理事務嗎?答案就是org.springframework.transaction.support.TransactionTemplate。下面我們研究下它的使用。首先看下類圖:
TransactionTemplate繼承了DefaultTransactionDefinition,它是TransactionDefinition的一個實現,TransactionDefinition是Spring事務管理中很重要的一個概念,它是事務配置的入口,可以配置事務的各種屬性,如隔離級別、傳播屬性、超時時間、是否只讀,通過實現這個接口,TransactionTemplate具備了配置事務的能力。另外必須為它指定transactionManager,畢竟它只是負責觸發事務的開啟,並不具備事務管理的能力:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan"> <list> <!-- 可以加多個包 --> <value>com.cuilei01.mgr.utils</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> </props> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>${jdbc.mgr.url}</value> </property> <property name="username"> <value>${jdbc.mgr.user}</value> </property> <property name="password"> <value>${jdbc.mgr.password}</value> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置transactionTemplate --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> <!--定義事務隔離級別,-1表示使用數據庫默認級別--> <property name="readOnly" value="false"></property> <property name="isolationLevelName" value="ISOLATION_DEFAULT"></property> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property> </bean>
現在就可以在Spring管理的Bean中注入並使用:
@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public class TransactionTest { private final Logger logger = LoggerFactory.getLogger(TransactionTest.class); @Resource private TransactionTemplate transactionTemplate; @Test public void testProgrammaticTransaction() { logger.info("Begin test programmatic transaction!########################"); // 第一個事務 Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() { @Override public Integer doInTransaction(TransactionStatus status) { logger.info("Do in transaction with a return value!#####################################"); // 在事務中執行, 有返回值 return 1; } }); logger.info("result:{}", result); // 第二個事務 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { logger.info("Do in transaction without a return value!#####################################"); // 在事務中執行,沒有返回值 } }); }
看到TransactionTemplate的使用比較簡單,只需將需要在事務中之行的邏輯封裝成TransactionCallback<T>,這個是帶返回值的,不帶返回值的封裝成TransactionCallbackWithoutResult。
觀察下事務的執行情況,事務work了。
看下它的核心代碼:
@Override public <T> T execute(TransactionCallback<T> action) throws TransactionException { if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) { return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action); } else { TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { result = action.doInTransaction(status); } catch (RuntimeException ex) { // Transactional code threw application exception -> rollback rollbackOnException(status, ex); throw ex; } catch (Error err) { // Transactional code threw error -> rollback rollbackOnException(status, err); throw err; } catch (Exception ex) { // Transactional code threw unexpected exception -> rollback rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); } this.transactionManager.commit(status); return result; } }
需要執行事務的業務邏輯被封裝成action,它所做的也很簡單,在action執行前後進行事務的開啟和提交(或著rollback)。開啟事務時需要transactionManager的getTransaction方法獲取TransactionStatus,而這個方法的參數是TransactionDefinition,前面說過TransactionTemplate本身就是TransactionDefinition的實現,所以將this作為參數傳遞給這個方法就可以。
也可以看到,TransactionTemplate和基於AOP的配置一樣,也是在方法前後執行事務的開啟和提交,只是實現的方式改變了。
Spring中如何配置Hibernate事務 http://www.linuxidc.com/Linux/2013-12/93681.htm
Struts2整合Spring方法及原理 http://www.linuxidc.com/Linux/2013-12/93692.htm
基於 Spring 設計並實現 RESTful Web Services http://www.linuxidc.com/Linux/2013-10/91974.htm
Spring-3.2.4 + Quartz-2.2.0集成實例 http://www.linuxidc.com/Linux/2013-10/91524.htm
使用 Spring 進行單元測試 http://www.linuxidc.com/Linux/2013-09/89913.htm
運用Spring注解實現Netty服務器端UDP應用程序 http://www.linuxidc.com/Linux/2013-09/89780.htm
Spring 3.x 企業應用開發實戰 PDF完整高清掃描版+源代碼 http://www.linuxidc.com/Linux/2013-10/91357.htm
Spring 的詳細介紹:請點這裡
Spring 的下載地址:請點這裡