spring事務的開啟方式(編程式和聲明式)
阿新 • • 發佈:2018-09-16
-- 事務管理器 文件 取值 efault read update checkout 初始化
使用@Transactional註意點:
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 註意點:
- 事務回滾異常只能為RuntimeException異常,而Checked Exception異常不回滾,捕獲異常不拋出也不會回滾,但可以強制事務回滾:TransactionAspectSupport.currentTransactionStatus().isRollbackOnly();
- 解決“自我調用”而導致的不能設置正確的事務屬性問題,可參考http://www.iteye.com/topic/1122740
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註意點:
- 如果在接口、實現類或方法上都指定了@Transactional 註解,則優先級順序為方法>實現類>接口;
- 建議只在實現類或實現類的方法上使用@Transactional,而不要在接口上使用,這是因為如果使用JDK代理機制(基於接口的代理)是沒問題;而使用使用CGLIB代理(繼承)機制時就會遇到問題,因為其使用基於類的代理而不是接口,這是因為接口上的@Transactional註解是“不能繼承的”;
spring事務的開啟方式(編程式和聲明式)