【Spring】Spring開啟事務的兩種方式


轉載:https://www.cnblogs.com/wangjing666/p/9655843.html

一、編程式事務:編碼方式實現事務管理

1)PlatformTransactionManager
<!-- 事務管理器配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="jdbcUrl" value="${db.jdbcUrl}" />
    <property name="user" value="${user}" />
    <property name="password" value="${password}" />
    <property name="driverClass" value="${db.driverClass}" />
     <!--連接池中保留的最小連接數。 -->
     <property name="minPoolSize">
         <value>5</value>
     </property>
     <!--連接池中保留的最大連接數。Default: 15 -->
     <property name="maxPoolSize">
         <value>30</value>
     </property>
     <!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
     <property name="initialPoolSize">
         <value>10</value>
     </property>
     <!--最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 -->
     <property name="maxIdleTime">
         <value>60</value>
     </property>
     <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
     <property name="acquireIncrement">
         <value>5</value>
     </property>
     <!--JDBC的標准參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements 屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。  如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default: 0 -->
     <property name="maxStatements">
         <value>0</value>
     </property>
     <!--每60秒檢查所有連接池中的空閑連接。Default: 0 -->
     <property name="idleConnectionTestPeriod">
         <value>60</value>
     </property>
     <!--定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default: 30 -->
     <property name="acquireRetryAttempts">
         <value>30</value>
     </property>
     <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效 保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試獲取連接失敗后該數據源將申明已斷開並永久關閉。Default: false -->
     <property name="breakAfterAcquireFailure">
         <value>true</value>
     </property>
     <!--因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable等方法來提升連接測試的性能。Default: false -->
     <property name="testConnectionOnCheckout">
         <value>false</value>
     </property>
</bean>
<!--DataSourceTransactionManager位於org.springframework.jdbc.datasource包下,數據源事務管理類,提供對單個javax.sql.DataSource數據源的事務管理,主要用於JDBC,Mybatis框架事務管理。 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
// 業務中使用代碼(以測試類展示)
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring-public.xml" })
public class test {
    @Resource
    private PlatformTransactionManager txManager;
    @Resource
    private  DataSource dataSource;
    private static JdbcTemplate jdbcTemplate;
    Logger logger=Logger.getLogger(test.class);
    private static final String INSERT_SQL = "insert into testtranstation(sd) values(?)";
    private static final String COUNT_SQL = "select count(*) from testtranstation";
    @Test
    public void testdelivery(){
        //定義事務隔離級別,傳播行為,
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //事務狀態類,通過PlatformTransactionManager的getTransaction方法根據事務定義獲取;獲取事務狀態后,Spring根據傳播行為來決定如何開啟事務
        TransactionStatus status = txManager.getTransaction(def);
        jdbcTemplate = new JdbcTemplate(dataSource);
        int i = jdbcTemplate.queryForInt(COUNT_SQL);
        System.out.println("表中記錄總數:"+i);
        try {
            jdbcTemplate.update(INSERT_SQL, "1");
            txManager.commit(status);  //提交status中綁定的事務
        } catch (RuntimeException e) {
            txManager.rollback(status);  //回滾
        }
        i = jdbcTemplate.queryForInt(COUNT_SQL);
        System.out.println("表中記錄總數:"+i);
    }

}
2)使用TransactionTemplate

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

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

  • TransactionCallback:通過實現該接口的“T doInTransaction(TransactionStatus status) ”方法來定義需要事務管理的操作代碼;
  • TransactionCallbackWithoutResult:繼承TransactionCallback接口,提供“void doInTransactionWithoutResult(TransactionStatus status)”便利接口用於方便那些不需要返回值的事務操作代碼。
@Test
public void testTransactionTemplate(){
    jdbcTemplate = new JdbcTemplate(dataSource);
    int i = jdbcTemplate.queryForInt(COUNT_SQL);
    System.out.println("表中記錄總數:"+i);
    //構造函數初始化TransactionTemplate
    TransactionTemplate template = new TransactionTemplate(txManager);
    template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
    //重寫execute方法實現事務管理
    template.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            jdbcTemplate.update(INSERT_SQL, "餓死");   //字段sd為int型,所以插入肯定失敗報異常,自動回滾,代表TransactionTemplate自動管理事務
        }}
    );
    i = jdbcTemplate.queryForInt(COUNT_SQL);
    System.out.println("表中記錄總數:"+i);
}

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

聲明式事務實現方式主要有2種,一種為通過使用Spring的<tx:advice>定義事務通知與AOP相關配置實現,另為一種通過@Transactional實現事務管理實現

1)<tx:advice>
<!--
<tx:advice>定義事務通知,用於指定事務屬性,其中“transaction-manager”屬性指定事務管理器,並通過<tx:attributes>指定具體需要攔截的方法
    <tx:method>攔截方法,其中參數有:
    name:方法名稱,將匹配的方法注入事務管理,可用通配符
    propagation:事務傳播行為,
    isolation:事務隔離級別定義;默認為“DEFAULT”
    timeout:事務超時時間設置,單位為秒,默認-1,表示事務超時將依賴於底層事務系統;
    read-only:事務只讀設置,默認為false,表示不是只讀;
    rollback-for:需要觸發回滾的異常定義,可定義多個,以“,”分割,默認任何RuntimeException都將導致事務回滾,而任何Checked Exception將不導致事務回滾;
    no-rollback-for:不被觸發進行回滾的 Exception(s);可定義多個,以“,”分割;
 -->
<tx:advice id="advice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 攔截save開頭的方法,事務傳播行為為:REQUIRED:必須要有事務, 如果沒有就在上下文創建一個 -->
        <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED" timeout="" read-only="false" no-rollback-for="" rollback-for=""/>
        <!-- 支持,如果有就有,沒有就沒有 -->
        <tx:method name="*" propagation="SUPPORTS"/>
    </tx:attributes>
</tx:advice>
<!-- 定義切入點,expression為切人點表達式,如下是指定impl包下的所有方法,具體以自身實際要求自定義  -->
<aop:config>
    <aop:pointcut expression="execution(* com.kaizhi.*.service.impl.*.*(..))" id="pointcut"/>
    <!--<aop:advisor>定義切入點,與通知,把tx與aop的配置關聯,才是完整的聲明事務配置 -->
    <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</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實現事務管理
 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
      <property name="dataSource" ref="dataSource"/>
 </bean>    
<!-- //開啟事務注解 -->
 <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注解是“不能繼承的”;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM