1. 程式人生 > >spring事務的開啟方式(編程式和聲明式)

spring事務的開啟方式(編程式和聲明式)

-- 事務管理器 文件 取值 efault read update checkout 初始化

1.編程式事務:編碼方式實現事務管理(代碼演示為JDBC事務管理)

Spring實現編程式事務,依賴於2大類,分別是上篇文章提到的PlatformTransactionManager,與模版類TransactionTemplate(推薦使用)。下面分別詳細介紹Spring是如何通過該類實現事務管理。 1)PlatformTransactionManager,上篇文章已經詳情解說了該類所擁有的方法,不記得可以回看上篇文章。
 1 事務管理器配置
 2 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
 3
<property name="jdbcUrl" value="${db.jdbcUrl}" /> 4 <property name="user" value="${user}" /> 5 <property name="password" value="${password}" /> 6 <property name="driverClass" value="${db.driverClass}" /> 7 <!--連接池中保留的最小連接數。 --> 8 <property name="minPoolSize"> 9
<value>5</value> 10 </property> 11 <!--連接池中保留的最大連接數。Default: 15 --> 12 <property name="maxPoolSize"> 13 <value>30</value> 14 </property> 15 <!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 --> 16
<property name="initialPoolSize"> 17 <value>10</value> 18 </property> 19 <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 --> 20 <property name="maxIdleTime"> 21 <value>60</value> 22 </property> 23 <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 --> 24 <property name="acquireIncrement"> 25 <value>5</value> 26 </property> 27 <!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements 屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。 如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default: 0 --> 28 <property name="maxStatements"> 29 <value>0</value> 30 </property> 31 <!--每60秒檢查所有連接池中的空閑連接。Default: 0 --> 32 <property name="idleConnectionTestPeriod"> 33 <value>60</value> 34 </property> 35 <!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 --> 36 <property name="acquireRetryAttempts"> 37 <value>30</value> 38 </property> 39 <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效 保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那麽在嘗試獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false --> 40 <property name="breakAfterAcquireFailure"> 41 <value>true</value> 42 </property> 43 <!--因性能消耗大請只在需要的時候使用它。如果設為true那麽在每個connection提交的 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable等方法來提升連接測試的性能。Default: false --> 44 <property name="testConnectionOnCheckout"> 45 <value>false</value> 46 </property> 47 </bean> 48 <!--DataSourceTransactionManager位於org.springframework.jdbc.datasource包下,數據源事務管理類,提供對單個javax.sql.DataSource數據源的事務管理,主要用於JDBC,Mybatis框架事務管理。 --> 49 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 50 <property name="dataSource" ref="dataSource" /> 51 </bean>
 1 業務中使用代碼(以測試類展示)
 2 import java.util.Map;
 3 import javax.annotation.Resource;
 4 import javax.sql.DataSource;
 5 import org.apache.log4j.Logger;
 6 import org.junit.Test;
 7 import org.junit.runner.RunWith;
 8 import org.springframework.jdbc.core.JdbcTemplate;
 9 import org.springframework.test.context.ContextConfiguration;
10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 import org.springframework.transaction.PlatformTransactionManager;
12 import org.springframework.transaction.TransactionDefinition;
13 import org.springframework.transaction.TransactionStatus;
14 import org.springframework.transaction.support.DefaultTransactionDefinition;
15  
16 @RunWith(SpringJUnit4ClassRunner.class)
17 @ContextConfiguration(locations = { "classpath:spring-public.xml" })
18 public class test {
19     @Resource
20     private PlatformTransactionManager txManager;
21     @Resource
22     private  DataSource dataSource;
23     private static JdbcTemplate jdbcTemplate;
24     Logger logger=Logger.getLogger(test.class);
25     private static final String INSERT_SQL = "insert into testtranstation(sd) values(?)";
26     private static final String COUNT_SQL = "select count(*) from testtranstation";
27     @Test
28     public void testdelivery(){
29         //定義事務隔離級別,傳播行為,
30         DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
31         def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
32         def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
33         //事務狀態類,通過PlatformTransactionManager的getTransaction方法根據事務定義獲取;獲取事務狀態後,Spring根據傳播行為來決定如何開啟事務
34         TransactionStatus status = txManager.getTransaction(def);  
35         jdbcTemplate = new JdbcTemplate(dataSource);
36         int i = jdbcTemplate.queryForInt(COUNT_SQL);  
37         System.out.println("表中記錄總數:"+i);
38         try {  
39             jdbcTemplate.update(INSERT_SQL, "1");  
40             txManager.commit(status);  //提交status中綁定的事務
41         } catch (RuntimeException e) {  
42             txManager.rollback(status);  //回滾
43         }  
44         i = jdbcTemplate.queryForInt(COUNT_SQL);  
45         System.out.println("表中記錄總數:"+i);
46     }
47     
48 }

2)使用TransactionTemplate,該類繼承了接口DefaultTransactionDefinition,用於簡化事務管理,事務管理由模板類定義,主要是通過TransactionCallback回調接口或TransactionCallbackWithoutResult回調接口指定,通過調用模板類的參數類型為TransactionCallback或TransactionCallbackWithoutResult的execute方法來自動享受事務管理。

TransactionTemplate模板類使用的回調接口:

  • TransactionCallback:通過實現該接口的“T doInTransaction(TransactionStatus status) ”方法來定義需要事務管理的操作代碼;
  • TransactionCallbackWithoutResult:繼承TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用於方便那些不需要返回值的事務操作代碼。

還是以測試類方式展示如何實現

 1 @Test
 2 public void testTransactionTemplate(){
 3     jdbcTemplate = new JdbcTemplate(dataSource);
 4     int i = jdbcTemplate.queryForInt(COUNT_SQL);  
 5     System.out.println("表中記錄總數:"+i);
 6     //構造函數初始化TransactionTemplate
 7     TransactionTemplate template = new TransactionTemplate(txManager);
 8     template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
 9     //重寫execute方法實現事務管理
10     template.execute(new TransactionCallbackWithoutResult() {
11         @Override
12         protected void doInTransactionWithoutResult(TransactionStatus status) {
13             jdbcTemplate.update(INSERT_SQL, "餓死");   //字段sd為int型,所以插入肯定失敗報異常,自動回滾,代表TransactionTemplate自動管理事務
14         }}
15     );
16     i = jdbcTemplate.queryForInt(COUNT_SQL);  
17     System.out.println("表中記錄總數:"+i);
18 }

2.聲明式事務:可知編程式事務每次實現都要單獨實現,但業務量大功能復雜時,使用編程式事務無疑是痛苦的,而聲明式事務不同,聲明式事務屬於無侵入式,不會影響業務邏輯的實現。

聲明式事務實現方式主要有2種,一種為通過使用Spring的<tx:advice>定義事務通知與AOP相關配置實現,另為一種通過@Transactional實現事務管理實現,下面詳細說明2種方法如何配置,已經相關註意點 1)方式一,配置文件如下
 1 <!-- 
 2 <tx:advice>定義事務通知,用於指定事務屬性,其中“transaction-manager”屬性指定事務管理器,並通過<tx:attributes>指定具體需要攔截的方法
 3     <tx:method>攔截方法,其中參數有:
 4     name:方法名稱,將匹配的方法註入事務管理,可用通配符
 5     propagation:事務傳播行為,
 6     isolation:事務隔離級別定義;默認為“DEFAULT”
 7     timeout:事務超時時間設置,單位為秒,默認-1,表示事務超時將依賴於底層事務系統;
 8     read-only:事務只讀設置,默認為false,表示不是只讀;
 9     rollback-for:需要觸發回滾的異常定義,可定義多個,以“,”分割,默認任何RuntimeException都將導致事務回滾,而任何Checked Exception將不導致事務回滾;
10     no-rollback-for:不被觸發進行回滾的 Exception(s);可定義多個,以“,”分割;
11  -->
12 <tx:advice id="advice" transaction-manager="transactionManager">
13     <tx:attributes>
14         <!-- 攔截save開頭的方法,事務傳播行為為:REQUIRED:必須要有事務, 如果沒有就在上下文創建一個 -->
15         <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>
16         <!-- 支持,如果有就有,沒有就沒有 -->
17         <tx:method name="*" propagation="SUPPORTS"/>
18     </tx:attributes>
19 </tx:advice>
20 <!-- 定義切入點,expression為切人點表達式,如下是指定impl包下的所有方法,具體以自身實際要求自定義  -->
21 <aop:config>
22     <aop:pointcut expression="execution(* com.kaizhi.*.service.impl.*.*(..))" id="pointcut"/>
23     <!--<aop:advisor>定義切入點,與通知,把tx與aop的配置關聯,才是完整的聲明事務配置 -->
24     <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
25 </aop:config>
關於事務傳播行為與隔離級別,可參考http://blog.csdn.net/liaohaojian/article/details/68488150 註意點:
  1. 事務回滾異常只能為RuntimeException異常,而Checked Exception異常不回滾,捕獲異常不拋出也不會回滾,但可以強制事務回滾:TransactionAspectSupport.currentTransactionStatus().isRollbackOnly();
  2. 解決“自我調用”而導致的不能設置正確的事務屬性問題,可參考http://www.iteye.com/topic/1122740
2)方式二通過@Transactional實現事務管理
1 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
2      <property name="dataSource" ref="dataSource"/>
3 </bean>    
4 <tx:annotation-driven transaction-manager="txManager"/> //開啟事務註解
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED),具體參數跟上面<tx:method>中一樣 Spring提供的@Transaction註解事務管理,內部同樣是利用環繞通知TransactionInterceptor實現事務的開啟及關閉。
使用@Transactional註意點:
    1. 如果在接口、實現類或方法上都指定了@Transactional 註解,則優先級順序為方法>實現類>接口;
    2. 建議只在實現類或實現類的方法上使用@Transactional,而不要在接口上使用,這是因為如果使用JDK代理機制(基於接口的代理)是沒問題;而使用使用CGLIB代理(繼承)機制時就會遇到問題,因為其使用基於類的代理而不是接口,這是因為接口上的@Transactional註解是“不能繼承的”;

spring事務的開啟方式(編程式和聲明式)