1. 程式人生 > >【第九章】 Spring的事務 之 9.3 程式設計式事務

【第九章】 Spring的事務 之 9.3 程式設計式事務

9.3.1  程式設計式事務概述

        所謂程式設計式事務指的是通過編碼方式實現事務,即類似於JDBC程式設計實現事務管理。

       Spring框架提供一致的事務抽象,因此對於JDBC還是JTA事務都是採用相同的API進行程式設計。

Connection conn = null;  
UserTransaction tx = null;  
try {  
    tx = getUserTransaction();                       //1.獲取事務  
    tx.begin();                                    //2.開啟JTA事務  
    conn = getDataSource().getConnection();           //3.獲取JDBC  
    //4.宣告SQL  
    String sql = "select * from INFORMATION_SCHEMA.SYSTEM_TABLES";  
    PreparedStatement pstmt = conn.prepareStatement(sql);//5.預編譯SQL  
    ResultSet rs = pstmt.executeQuery();               //6.執行SQL  
    process(rs);                                   //7.處理結果集  
    closeResultSet(rs);                             //8.釋放結果集  
    tx.commit();                                  //7.提交事務  
} catch (Exception e) {  
    tx.rollback();                                 //8.回滾事務  
    throw e;  
} finally {  
   conn.close();                                //關閉連線  
} 

9.3.2   Spring對程式設計式事務的支援

Spring中的事務分為物理事務和邏輯事務;

  • 物理事務:就是底層資料庫提供的事務支援,如JDBC或JTA提供的事務;
  • 邏輯事務:是Spring管理的事務,不同於物理事務,邏輯事務提供更豐富的控制,而且如果想得到Spring事務管理的好處,必須使用邏輯事務,因此在Spring中如果沒特別強調一般就是邏輯事務

    邏輯事務即支援非常低級別的控制,也有高級別解決方案:

  • 低級別解決方案:

  工具類:使用工具類獲取連線(會話)和釋放連線(會話),如使用org.springframework.jdbc.datasource包中的 DataSourceUtils 類來獲取和釋放具有邏輯事務功能的連線。當然對整合第三方ORM框架也提供了類似的工具類,如對Hibernate提供了SessionFactoryUtils工具類,JPA的EntityManagerFactoryUtils等,其他工具類都是使用類似***Utils命名;

//獲取具有Spring事務(邏輯事務)管理功能的連線  
DataSourceUtils. getConnection(DataSource dataSource)  
//釋放具有Spring事務(邏輯事務)管理功能的連線  
DataSourceUtils. releaseConnection(Connection con, DataSource dataSource)

         TransactionAwareDataSourceProxy使用該資料來源代理類包裝需要Spring事務管理支援的資料來源,該包裝類必須位於最外層,主要用於遺留專案中可能直接使用資料來源獲取連線和釋放連線支援或希望在Spring中進行混合使用各種持久化框架時使用,其內部實際使用 DataSourceUtils 工具類獲取和釋放真正連線;

<!--使用該方式包裝資料來源,必須在最外層,targetDataSource 知道目標資料來源-->  
<bean id="dataSourceProxy"  
class="org.springframework.jdbc.datasource.  
TransactionAwareDataSourceProxy">  
    <property name="targetDataSource" ref="dataSource"/>  
</bean>  

        通過如上方式包裝資料來源後,可以在專案中使用物理事務編碼的方式來獲得邏輯事務的支援,即支援直接從DataSource獲取連線和釋放連線,且這些連線自動支援Spring邏輯事務;

  • 高級別解決方案:

         模板類:使用Spring提供的模板類,如JdbcTemplate、HibernateTemplate和JpaTemplate模板類等,而這些模板類內部其實是使用了低級別解決方案中的工具類來管理連線或會話;

     Spring提供兩種程式設計式事務支援:直接使用PlatformTransactionManager實現和使用TransactionTemplate模板類,用於支援邏輯事務管理。

如果採用程式設計式事務推薦使用TransactionTemplate模板類和高級別解決方案。

9.3.3  使用PlatformTransactionManager

首先讓我們看下如何使用PlatformTransactionManager實現來進行事務管理:

1、資料來源定義,此處使用第7章的配置檔案,即“chapter7/ applicationContext-resources.xml”檔案。

2、事務管理器定義(chapter9/applicationContext-jdbc.xml):

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
    <property name="dataSource" ref="dataSource"/>  
</bean>  

3、 準備測試環境:

3.1、首先準備測試時使用的SQL

package cn.javass.spring.chapter9;  
//省略import  
public class TransactionTest {  
    //id自增主鍵從0開始  
    private static final String CREATE_TABLE_SQL = "create table test" +  
    "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " +  
    "name varchar(100))";  
    private static final String DROP_TABLE_SQL = "drop table test";  
    private static final String INSERT_SQL = "insert into test(name) values(?)";  
    private static final String COUNT_SQL = "select count(*) from test";  
    ……  
}  

3.2、初始化Spring容器

package cn.javass.spring.chapter9;  
//省略import  
public class TransactionTest {  
    private static ApplicationContext ctx;  
    private static PlatformTransactionManager txManager;  
    private static DataSource dataSource;  
    private static JdbcTemplate jdbcTemplate;  
    ……  
    @BeforeClass  
    public static void setUpClass() {  
        String[] configLocations = new String[] {  
                "classpath:chapter7/applicationContext-resources.xml",  
                "classpath:chapter9/applicationContext-jdbc.xml"};  
        ctx = new ClassPathXmlApplicationContext(configLocations);  
        txManager = ctx.getBean(PlatformTransactionManager.class);  
        dataSource = ctx.getBean(DataSource.class);  
        jdbcTemplate = new JdbcTemplate(dataSource);  
    }   
    ……  
}  

3.3、使用高級別方案JdbcTemplate來進行事務管理器測試:

@Test  
public void testPlatformTransactionManager() {  
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
    TransactionStatus status = txManager.getTransaction(def);  
    jdbcTemplate.execute(CREATE_TABLE_SQL);  
    try {  
        jdbcTemplate.update(INSERT_SQL, "test");  
        txManager.commit(status);  
    } catch (RuntimeException e) {  
        txManager.rollback(status);  
    }  
    jdbcTemplate.execute(DROP_TABLE_SQL);  
} 
  • DefaultTransactionDefinition事務定義,定義如隔離級別、傳播行為等,即在本示例中隔離級別為ISOLATION_READ_COMMITTED(提交讀),傳播行為為PROPAGATION_REQUIRED(必須有事務支援,即如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,就加入到這個事務中)。
  • TransactionStatus事務狀態類,通過PlatformTransactionManager的getTransaction方法根據事務定義獲取;獲取事務狀態後,Spring根據傳播行為來決定如何開啟事務;
  • JdbcTemplate:通過JdbcTemplate物件執行相應的SQL操作,且自動享受到事務支援,注意事務是執行緒繫結的,因此事務管理器可以執行在多執行緒環境;
  • txManager.commit(status):提交status物件繫結的事務;
  • txManager.rollback(status):當遇到異常時回滾status物件繫結的事務。

3.4、使用低級別解決方案來進行事務管理器測試:

@Test  
public void testPlatformTransactionManagerForLowLevel1() {  
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
  TransactionStatus status = txManager.getTransaction(def);  
  Connection conn = DataSourceUtils.getConnection(dataSource);  
  try {  
      conn.prepareStatement(CREATE_TABLE_SQL).execute();  
      PreparedStatement pstmt = conn.prepareStatement(INSERT_SQL);  
      pstmt.setString(1, "test");  
      pstmt.execute();  
      conn.prepareStatement(DROP_TABLE_SQL).execute();  
      txManager.commit(status);  
  } catch (Exception e) {  
      status.setRollbackOnly();  
      txManager.rollback(status);  
  } finally {  
      DataSourceUtils.releaseConnection(conn, dataSource);  
}  
}  

        低級別方案中使用DataSourceUtils獲取和釋放連線,使用txManager開管理事務,而且面向JDBC程式設計,比起模板類方式來繁瑣和複雜的多,因此不推薦使用該方式。在此就不介紹資料來源代理類使用了,需要請參考platformTransactionManagerForLowLevelTest2測試方法。

       到此事務管理是不是還很繁瑣?必須手工提交或回滾事務,有沒有更好的解決方案呢?Spring提供了TransactionTemplate模板類來簡化事務管理。

9.3.4  使用TransactionTemplate

         TransactionTemplate模板類用於簡化事務管理,事務管理由模板類定義,而具體操作需要通過TransactionCallback回撥介面或TransactionCallbackWithoutResult回撥介面指定,通過呼叫模板類的引數型別為TransactionCallback或TransactionCallbackWithoutResult的execute方法來自動享受事務管理。

TransactionTemplate模板類使用的回撥介面:

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

1、接下來演示一下TransactionTemplate模板類如何使用:

@Test  
public void testTransactionTemplate() {//位於TransactionTest類中  
  jdbcTemplate.execute(CREATE_TABLE_SQL);  
  TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);  
  transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
      @Override  
      protected void doInTransactionWithoutResult(TransactionStatus status) {  
         jdbcTemplate.update(INSERT_SQL, "test");  
  }});  
  jdbcTemplate.execute(DROP_TABLE_SQL);  
}  
  • TransactionTemplate :通過new TransactionTemplate(txManager)建立事務模板類,其中構造器引數為PlatformTransactionManager實現,並通過其相應方法設定事務定義,如事務隔離級別、傳播行為等,此處未指定傳播行為,其預設為PROPAGATION_REQUIRED;
  • TransactionCallbackWithoutResult:此處使用不帶返回的回撥實現,其doInTransactionWithoutResult方法實現中定義了需要事務管理的操作;
  • transactionTemplate.execute():通過該方法執行需要事務管理的回撥。

這樣是不是簡單多了,沒有事務管理程式碼,而是由模板類來完成事務管理。

注:對於丟擲Exception型別的異常且需要回滾時,需要捕獲異常並通過呼叫status物件的setRollbackOnly()方法告知事務管理器當前事務需要回滾,如下所示:

try {  
    //業務操作  
} catch (Exception e) { //可使用具體業務異常代替  
    status.setRollbackOnly();  
}  

2、前邊已經演示了JDBC事務管理,接下來演示一下JTA分散式事務管理:

@Test  
public void testJtaTransactionTemplate() {  
    String[] configLocations = new String[] {  
      "classpath:chapter9/applicationContext-jta-derby.xml"};  
    ctx = new ClassPathXmlApplicationContext(configLocations);  
    final PlatformTransactionManager jtaTXManager = ctx.getBean(PlatformTransactionManager.class);  
    final DataSource derbyDataSource1 = ctx.getBean("dataSource1", DataSource.class);  
    final DataSource derbyDataSource2 = ctx.getBean("dataSource2", DataSource.class);  
    final JdbcTemplate jdbcTemplate1 = new JdbcTemplate(derbyDataSource1);  
    final JdbcTemplate jdbcTemplate2 = new JdbcTemplate(derbyDataSource2);  
    TransactionTemplate transactionTemplate = new TransactionTemplate(jtaTXManager);   
    transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
    jdbcTemplate1.update(CREATE_TABLE_SQL);  
    int originalCount = jdbcTemplate1.queryForInt(COUNT_SQL);  
    try {  
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
            @Override  
            protected void doInTransactionWithoutResult(TransactionStatus status) {  
                jdbcTemplate1.update(INSERT_SQL, "test");  
               //因為資料庫2沒有建立資料庫表因此會回滾事務  
              jdbcTemplate2.update(INSERT_SQL, "test");  
          }});  
    } catch (RuntimeException e) {  
        int count = jdbcTemplate1.queryForInt(COUNT_SQL);  
        Assert.assertEquals(originalCount, count);  
    }  
    jdbcTemplate1.update(DROP_TABLE_SQL);  
}  
  • 配置檔案:使用此前定義的chapter9/applicationContext-jta-derby.xml;
  • jtaTXManager: JTA事務管理器;
  • derbyDataSource1和derbyDataSource2:derby資料來源1和derby資料來源2;
  • jdbcTemplate1和jdbcTemplate2:分別使用derbyDataSource1和derbyDataSource2構造的JDBC模板類;
  • transactionTemplate:使用jtaTXManager事務管理器的事務管理模板類,其隔離級別為提交讀,傳播行為預設為PROPAGATION_REQUIRED(必須有事務支援,即如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,就加入到這個事務中);
  • jdbcTemplate1.update(CREATE_TABLE_SQL):此處只有derbyDataSource1所代表的資料庫建立了“test”表,而derbyDataSource2所代表的資料庫沒有此表;
  • TransactionCallbackWithoutResult:在此介面實現中定義了需要事務支援的操作:

         jdbcTemplate1.update(INSERT_SQL, "test"):表示向資料庫1中的test表中插入資料;

         jdbcTemplate2.update(INSERT_SQL, "test"):表示向資料庫2中的test表中插入資料,但資料庫2沒有此表將丟擲異常,且JTA分散式事務將回滾;

  • Assert.assertEquals(originalCount, count):用來驗證事務是否回滾,驗證結果返回為true,說明分散式事務回滾了。

        到此我們已經會使用PlatformTransactionManager和TransactionTemplate進行簡單事務處理了,那如何應用到實際專案中去呢?接下來讓我們看下如何在實際專案中應用Spring管理事務。

        接下來看一下如何將Spring管理事務應用到實際專案中,為簡化演示,此處只定義最簡單的模型物件和不完整的Dao層介面和Service層介面:

1、 首先定義專案中的模型物件,本示例使用使用者模型和使用者地址模型:模型物件一般放在專案中的model包裡。

package cn.javass.spring.chapter9.model;  
public class UserModel {  
    private int id;  
    private String name;  
    private AddressModel address;  
    //省略getter和setter  
} 
package cn.javass.spring.chapter9.model;  
public class AddressModel {  
    private int id;  
    private String province;  
    private String city;  
    private String street;  
    private int userId;  
    //省略getter和setter  
}  

2.1、定義Dao層介面:

package cn.javass.spring.chapter9.service;  
import cn.javass.spring.chapter9.model.UserModel;  
public interface IUserService {  
    public void save(UserModel user);  
    public int countAll();  
}  
package cn.javass.spring.chapter9.service;  
import cn.javass.spring.chapter9.model.AddressModel;  
public interface IAddressService {  
    public void save(AddressModel address);  
    public int countAll();  
}  

2.2、定義Dao層實現:

package cn.javass.spring.chapter9.dao.jdbc;  
//省略import,注意model要引用chapter包裡的  
public class UserJdbcDaoImpl extends NamedParameterJdbcDaoSupport implements IUserDao {  
    private final String INSERT_SQL = "insert into user(name) values(:name)";  
    private final String COUNT_ALL_SQL = "select count(*) from user";  
    @Override  
    public void save(UserModel user) {  
        KeyHolder generatedKeyHolder = new GeneratedKeyHolder();  
        SqlParameterSource paramSource = new BeanPropertySqlParameterSource(user);  
        getNamedParameterJdbcTemplate().update(INSERT_SQL, paramSource, generatedKeyHolder);  
        user.setId(generatedKeyHolder.getKey().intValue());  
    }  
    @Override  
    public int countAll() {  
       return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);  
    }  

3.1、定義Service層介面,一般使用“I×××Service”命名:

package cn.javass.spring.chapter9.service;  
import cn.javass.spring.chapter9.model.UserModel;  
public interface IUserService {  
    public void save(UserModel user);  
    public int countAll();  
}  
  
  
package cn.javass.spring.chapter9.service;  
import cn.javass.spring.chapter9.model.AddressModel;  
public interface IAddressService {  
    public void save(AddressModel address);  
    public int countAll();  
} 

3.2、定義Service層實現,一般使用“×××ServiceImpl”或“×××Service”命名:

package cn.javass.spring.chapter9.service.impl;  
//省略import,注意model要引用chapter包裡的  
public class AddressServiceImpl implements IAddressService {  
    private IAddressDao addressDao;  
    private PlatformTransactionManager txManager;  
    public void setAddressDao(IAddressDao addressDao) {  
        this.addressDao = addressDao;  
    }  
    public void setTxManager(PlatformTransactionManager txManager) {  
        this.txManager = txManager;  
    }  
    @Override  
    public void save(final AddressModel address) {  
        TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);  
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
           @Override  
           protected void doInTransactionWithoutResult(TransactionStatus status) {  
                addressDao.save(address);  
           }  
        });  
    }  
    @Override  
    public int countAll() {  
        return addressDao.countAll();  
    }  
}  
package cn.javass.spring.chapter9.service.impl;  
//省略import,注意model要引用chapter包裡的  
public class UserServiceImpl implements IUserService {  
    private IUserDao userDao;  
    private IAddressService addressService;  
    private PlatformTransactionManager txManager;  
    public void setUserDao(IUserDao userDao) {  
        this.userDao = userDao;  
    }  
    public void setTxManager(PlatformTransactionManager txManager) {  
        this.txManager = txManager;  
    }  
    public void setAddressService(IAddressService addressService) {  
        this.addressService = addressService;  
    }  
    @Override  
    public void save(final UserModel user) {  
        TransactionTemplate transactionTemplate =  
            TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);  
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
        @Override  
           protected void doInTransactionWithoutResult(TransactionStatus status) {  
                userDao.save(user);  
                user.getAddress().setUserId(user.getId());  
                addressService.save(user.getAddress());  
           }  
        });  
    }  
    @Override  
    public int countAll() {  
        return userDao.countAll();  
    }  
}  

Service實現中需要Spring事務管理的部分應該使用TransactionTemplate模板類來包裝執行。

4、定義TransactionTemplateUtils,用於簡化獲取TransactionTemplate模板類,工具類一般放在util包中:

package cn.javass.spring.chapter9.util;  
//省略import  
public class TransactionTemplateUtils {  
    public static TransactionTemplate getTransactionTemplate(  
            PlatformTransactionManager txManager,  
            int propagationBehavior,  
            int isolationLevel) {  
        
        TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);  
        transactionTemplate.setPropagationBehavior(propagationBehavior);  
        transactionTemplate.setIsolationLevel(isolationLevel);  
        return transactionTemplate;  
    }  
     
    public static TransactionTemplate getDefaultTransactionTemplate(PlatformTransactionManager txManager) {  
        return getTransactionTemplate(  
                txManager,  
                TransactionDefinition.PROPAGATION_REQUIRED,  
                TransactionDefinition.ISOLATION_READ_COMMITTED);  
    }  
}  

getDefaultTransactionTemplate用於獲取傳播行為為PROPAGATION_REQUIRED,隔離級別為ISOLATION_READ_COMMITTED的模板類。

5、資料來源配置定義,此處使用第7章的配置檔案,即“chapter7/ applicationContext-resources.xml”檔案。

6、Dao層配置定義(chapter9/dao/applicationContext-jdbc.xml):

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
    <property name="dataSource" ref="dataSource"/>  
</bean>  
<bean id="abstractDao" abstract="true">  
    <property name="dataSource" ref="dataSource"/>  
</bean>  
<bean id="userDao" class="cn.javass.spring.chapter9.dao.jdbc.UserJdbcDaoImpl" parent="abstractDao"/>  
<bean id="addressDao" class="cn.javass.spring.chapter9.dao.jdbc.AddressJdbcDaoImpl" parent="abstractDao"/> 

7、Service層配置定義(chapter9/service/applicationContext-service.xml):

<bean id="userService" class="cn.javass.spring.chapter9.service.impl.UserServiceImpl">  
    <property name="userDao" ref="userDao"/>  
    <property name="txManager" ref="txManager"/>  
    <property name="addressService" ref="addressService"/>  
</bean>  
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.AddressServiceImpl">  
    <property name="addressDao" ref="addressDao"/>  
    <property name="txManager" ref="txManager"/>  
</bean>  

8、準備測試需要的表建立語句,在TransactionTest測試類中新增如下靜態變數:

private static final String CREATE_USER_TABLE_SQL =  
    "create table user" +  
    "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " +  
    "name varchar(100))";  
private static final String DROP_USER_TABLE_SQL = "drop table user";  
   
private static final String CREATE_ADDRESS_TABLE_SQL =  
    "create table address" +  
    "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " +  
    "province varchar(100), city varchar(100), street varchar(100), user_id int)";  
private static final String DROP_ADDRESS_TABLE_SQL = "drop table address";

9、 測試一下吧:

@Test  
    public void testServiceTransaction() {  
        String[] configLocations = new String[] {  
        "classpath:chapter7/applicationContext-resources.xml",  
        "classpath:chapter9/dao/applicationContext-jdbc.xml",  
        "classpath:chapter9/service/applicationContext-service.xml"};  
        ApplicationContext ctx2 = new ClassPathXmlApplicationContext(configLocations);  
         
        DataSource dataSource2 = ctx2.getBean(DataSource.class);  
        JdbcTemplate jdbcTemplate2 = new JdbcTemplate(dataSource2);  
        jdbcTemplate2.update(CREATE_USER_TABLE_SQL);  
        jdbcTemplate2.update(CREATE_ADDRESS_TABLE_SQL);  
         
        IUserService userService = ctx2.getBean("userService", IUserService.class);  
        IAddressService addressService = ctx2.getBean("addressService", IAddressService.class);  
        UserModel user = createDefaultUserModel();  
        userService.save(user);  
        Assert.assertEquals(1, userService.countAll());  
        Assert.assertEquals(1, addressService.countAll());  
        jdbcTemplate2.update(DROP_USER_TABLE_SQL);  
        jdbcTemplate2.update(DROP_ADDRESS_TABLE_SQL);  
}  
private UserModel createDefaultUserModel() {  
    UserModel user = new UserModel();  
    user.setName("test");  
    AddressModel address = new AddressModel();  
    address.setProvince("beijing");  
    address.setCity("beijing");  
    address.setStreet("haidian");  
    user.setAddress(address);  
    return user;  
} 

     從Spring容器中獲取Service層物件,呼叫Service層物件持久化物件,大家有沒有注意到Spring事務全部在Service層定義,為什麼會在Service層定義,而不是Dao層定義呢?這是因為在服務層可能牽扯到業務邏輯,而每個業務邏輯可能呼叫多個Dao層方法,為保證這些操作的原子性,必須在Service層定義事務。

    還有大家有沒有注意到如果Service層的事務管理相當令人頭疼,而且是侵入式的,有沒有辦法消除這些冗長的事務管理程式碼呢?這就需要Spring宣告式事務支援,下一節將介紹無侵入式的宣告式事務。

可能大家對事務定義中的各種屬性有點困惑,如傳播行為到底幹什麼用的?接下來將詳細講解一下事務屬性。

9.3.5  事務屬性

事務屬性通過TransactionDefinition介面實現定義,主要有事務隔離級別、事務傳播行為、事務超時時間、事務是否只讀

       Spring提供TransactionDefinition介面預設實現DefaultTransactionDefinition,可以通過該實現類指定這些事務屬性。

  • 事務隔離級別:用來解決併發事務時出現的問題,其使用TransactionDefinition中的靜態變數來指定:

         ISOLATION_DEFAULT:預設隔離級別,即使用底層資料庫預設的隔離級別;

         ISOLATION_READ_UNCOMMITTED:未提交讀;

         ISOLATION_READ_COMMITTED:提交讀,一般情況下我們使用這個;

         ISOLATION_REPEATABLE_READ:可重複讀;

         ISOLATION_SERIALIZABLE:序列化。

可以使用DefaultTransactionDefinition類的setIsolationLevel(TransactionDefinition. ISOLATION_READ_COMMITTED)來指定隔離級別,其中此處表示隔離級別為提交讀,也可以使用或setIsolationLevelName(“ISOLATION_READ_COMMITTED”)方式指定,其中引數就是隔離級別靜態變數的名字,但不推薦這種方式。

  • 事務傳播行為:Spring管理的事務是邏輯事務,而且物理事務和邏輯事務最大差別就在於事務傳播行為,事務傳播行為用於指定在多個事務方法間呼叫時,事務是如何在這些方法間傳播的,Spring共支援7種傳播行為:

Required:必須有邏輯事務,否則新建一個事務,使用PROPAGATION_REQUIRED指定,表示如果當前存在一個邏輯事務,則加入該邏輯事務,否則將新建一個邏輯事務。

RequiresNew:建立新的邏輯事務,使用PROPAGATION_REQUIRES_NEW指定,表示每次都建立新的邏輯事務(物理事務也是不同的)

Supports:支援當前事務,使用PROPAGATION_SUPPORTS指定,指如果當前存在邏輯事務,就加入到該邏輯事務,如果當前沒有邏輯事務,就以非事務方式執行。

NotSupported:不支援事務,如果當前存在事務則暫停該事務,使用PROPAGATION_NOT_SUPPORTED指定,即以非事務方式執行。

Mandatory:必須有事務,否則丟擲異常,使用PROPAGATION_MANDATORY指定,使用當前事務執行,如果當前沒有事務,則丟擲異常(IllegalTransactionStateException)。

Never:不支援事務,如果當前存在是事務則丟擲異常,使用PROPAGATION_NEVER指定,即以非事務方式執行,如果當前存在事務,則丟擲異常(IllegalTransactionStateException)

Nested:巢狀事務支援,使用PROPAGATION_NESTED指定,如果當前存在事務,則在巢狀事務內執行,如果當前不存在事務,則建立一個新的事務,巢狀事務使用資料庫中的儲存點來實現,即巢狀事務回滾不影響外部事務,但外部事務回滾將導致巢狀事務回滾

  • 事務超時:設定事務的超時時間,單位為秒,預設為-1表示使用底層事務的超時時間;

          使用如setTimeout(100)來設定超時時間,如果事務超時將丟擲org.springframework.transaction.TransactionTimedOutException異常並將當前事務標記為應該回滾,即超時後事務被自動回滾;

         可以使用具體事務管理器實現的defaultTimeout屬性設定預設的事務超時時間,如DataSourceTransactionManager. setDefaultTimeout(10)。

  • 事務只讀:將事務標識為只讀,只讀事務不修改任何資料;