Spring的事務 之 9.3 編程式事務

9.3  編程式事務

9.3.1  編程式事務概述

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

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

 

java代碼:
Java代碼  收藏代碼
  1. Connection conn = null;  
  2. UserTransaction tx = null;  
  3. try {  
  4.     tx = getUserTransaction();                       //1.獲取事務  
  5.     tx.begin();                                    //2.開啓JTA事務  
  6.     conn = getDataSource().getConnection();           //3.獲取JDBC  
  7.     //4.聲明SQL  
  8.     String sql = "select * from INFORMATION_SCHEMA.SYSTEM_TABLES";  
  9.     PreparedStatement pstmt = conn.prepareStatement(sql);//5.預編譯SQL  
  10.     ResultSet rs = pstmt.executeQuery();               //6.執行SQL  
  11.     process(rs);                                   //7.處理結果集  
  12.     closeResultSet(rs);                             //8.釋放結果集  
  13.     tx.commit();                                  //7.提交事務  
  14. catch (Exception e) {  
  15.     tx.rollback();                                 //8.回滾事務  
  16.     throw e;  
  17. finally {  
  18.    conn.close();                                //關閉連接  
  19. }  

       此處可以看到使用UserTransaction而不是Connection連接進行控制事務,從而對於JDBC事務和JTA事務是採用不同API進行編程控制的,並且JTA和JDBC事務管理的異常也是不一樣的。

       具體如何使用JTA編程進行事務管理請參考cn.javass.spring.chapter9包下的TranditionalTransactionTest類。

       而在Spring中將採用一致的事務抽象進行控制和一致的異常控制,即面向PlatformTransactionManager接口編程來控制事務。

 

9.3.1    Spring對編程式事務的支持

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

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

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

  • 低級別解決方案:

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

 

java代碼:
Java代碼  收藏代碼
  1. //獲取具有Spring事務(邏輯事務)管理功能的連接  
  2. DataSourceUtils. getConnection(DataSource dataSource)  
  3. //釋放具有Spring事務(邏輯事務)管理功能的連接  
  4. DataSourceUtils. releaseConnection(Connection con, DataSource dataSource)  
  5.    

 

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

 

java代碼:
Java代碼  收藏代碼
  1. <!--使用該方式包裝數據源,必須在最外層,targetDataSource 知道目標數據源-->  
  2. <bean id="dataSourceProxy"  
  3. class="org.springframework.jdbc.datasource.  
  4. TransactionAwareDataSourceProxy">  
  5.     <property name="targetDataSource" ref="dataSource"/>  
  6. </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):

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
  2.     <property name="dataSource" ref="dataSource"/>  
  3. </bean>  

 

3、 準備測試環境:

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

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9;  
  2. //省略import  
  3. public class TransactionTest {  
  4.     //id自增主鍵從0開始  
  5.     private static final String CREATE_TABLE_SQL = "create table test" +  
  6.     "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " +  
  7.     "name varchar(100))";  
  8.     private static final String DROP_TABLE_SQL = "drop table test";  
  9.     private static final String INSERT_SQL = "insert into test(name) values(?)";  
  10.     private static final String COUNT_SQL = "select count(*) from test";  
  11.     ……  
  12. }  

 

3.2、初始化Spring容器

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9;  
  2. //省略import  
  3. public class TransactionTest {  
  4.     private static ApplicationContext ctx;  
  5.     private static PlatformTransactionManager txManager;  
  6.     private static DataSource dataSource;  
  7.     private static JdbcTemplate jdbcTemplate;  
  8.     ……  
  9.     @BeforeClass  
  10.     public static void setUpClass() {  
  11.         String[] configLocations = new String[] {  
  12.                 "classpath:chapter7/applicationContext-resources.xml",  
  13.                 "classpath:chapter9/applicationContext-jdbc.xml"};  
  14.         ctx = new ClassPathXmlApplicationContext(configLocations);  
  15.         txManager = ctx.getBean(PlatformTransactionManager.class);  
  16.         dataSource = ctx.getBean(DataSource.class);  
  17.         jdbcTemplate = new JdbcTemplate(dataSource);  
  18.     }   
  19.     ……  
  20. }  

 

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

 

java代碼:
Java代碼  收藏代碼
  1. @Test  
  2. public void testPlatformTransactionManager() {  
  3.     DefaultTransactionDefinition def = new DefaultTransactionDefinition();  
  4.     def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  5.     def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
  6.     TransactionStatus status = txManager.getTransaction(def);  
  7.     jdbcTemplate.execute(CREATE_TABLE_SQL);  
  8.     try {  
  9.         jdbcTemplate.update(INSERT_SQL, "test");  
  10.         txManager.commit(status);  
  11.     } catch (RuntimeException e) {  
  12.         txManager.rollback(status);  
  13.     }  
  14.     jdbcTemplate.execute(DROP_TABLE_SQL);  
  15. }  
  • DefaultTransactionDefinition:事務定義,定義如隔離級別、傳播行爲等,即在本示例中隔離級別爲ISOLATION_READ_COMMITTED(提交讀),傳播行爲爲PROPAGATION_REQUIRED(必須有事務支持,即如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,就加入到這個事務中)。
  • TransactionStatus:事務狀態類,通過PlatformTransactionManager的getTransaction方法根據事務定義獲取;獲取事務狀態後,Spring根據傳播行爲來決定如何開啓事務;
  • JdbcTemplate:通過JdbcTemplate對象執行相應的SQL操作,且自動享受到事務支持,注意事務是線程綁定的,因此事務管理器可以運行在多線程環境;
  • txManager.commit(status):提交status對象綁定的事務;
  • txManager.rollback(status):當遇到異常時回滾status對象綁定的事務。

 

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

 

java代碼:
Java代碼  收藏代碼
  1. @Test  
  2. public void testPlatformTransactionManagerForLowLevel1() {  
  3. DefaultTransactionDefinition def = new DefaultTransactionDefinition();      def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);      def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  
  4.   TransactionStatus status = txManager.getTransaction(def);  
  5.   Connection conn = DataSourceUtils.getConnection(dataSource);  
  6.   try {  
  7.       conn.prepareStatement(CREATE_TABLE_SQL).execute();  
  8.       PreparedStatement pstmt = conn.prepareStatement(INSERT_SQL);  
  9.       pstmt.setString(1"test");  
  10.       pstmt.execute();  
  11.       conn.prepareStatement(DROP_TABLE_SQL).execute();  
  12.       txManager.commit(status);  
  13.   } catch (Exception e) {  
  14.       status.setRollbackOnly();  
  15.       txManager.rollback(status);  
  16.   } finally {  
  17.       DataSourceUtils.releaseConnection(conn, dataSource);  
  18. }  
  19. }  
  20.    

 

低級別方案中使用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模板類如何使用:

 

java代碼:
Java代碼  收藏代碼
  1. @Test  
  2. public void testTransactionTemplate() {//位於TransactionTest類中  
  3.   jdbcTemplate.execute(CREATE_TABLE_SQL);  
  4.   TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);  
  5.   transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  6.   transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
  7.       @Override  
  8.       protected void doInTransactionWithoutResult(TransactionStatus status) {  
  9.          jdbcTemplate.update(INSERT_SQL, "test");  
  10.   }});  
  11.   jdbcTemplate.execute(DROP_TABLE_SQL);  
  12. }  
  • TransactionTemplate :通過new TransactionTemplate(txManager)創建事務模板類,其中構造器參數爲PlatformTransactionManager實現,並通過其相應方法設置事務定義,如事務隔離級別、傳播行爲等,此處未指定傳播行爲,其默認爲PROPAGATION_REQUIRED;
  • TransactionCallbackWithoutResult:此處使用不帶返回的回調實現,其doInTransactionWithoutResult方法實現中定義了需要事務管理的操作;
  • transactionTemplate.execute():通過該方法執行需要事務管理的回調。

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

 

注:對於拋出Exception類型的異常且需要回滾時,需要捕獲異常並通過調用status對象的setRollbackOnly()方法告知事務管理器當前事務需要回滾,如下所示:

 

java代碼:
Java代碼  收藏代碼
  1. try {  
  2.     //業務操作  
  3. catch (Exception e) { //可使用具體業務異常代替  
  4.     status.setRollbackOnly();  
  5. }  

 

 

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

 

java代碼:
Java代碼  收藏代碼
  1. @Test  
  2. public void testJtaTransactionTemplate() {  
  3.     String[] configLocations = new String[] {  
  4.       "classpath:chapter9/applicationContext-jta-derby.xml"};  
  5.     ctx = new ClassPathXmlApplicationContext(configLocations);  
  6.     final PlatformTransactionManager jtaTXManager = ctx.getBean(PlatformTransactionManager.class);  
  7.     final DataSource derbyDataSource1 = ctx.getBean("dataSource1", DataSource.class);  
  8.     final DataSource derbyDataSource2 = ctx.getBean("dataSource2", DataSource.class);  
  9.     final JdbcTemplate jdbcTemplate1 = new JdbcTemplate(derbyDataSource1);  
  10.     final JdbcTemplate jdbcTemplate2 = new JdbcTemplate(derbyDataSource2);  
  11.     TransactionTemplate transactionTemplate = new TransactionTemplate(jtaTXManager);   
  12.     transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  
  13.     jdbcTemplate1.update(CREATE_TABLE_SQL);  
  14.     int originalCount = jdbcTemplate1.queryForInt(COUNT_SQL);  
  15.     try {  
  16.         transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
  17.             @Override  
  18.             protected void doInTransactionWithoutResult(TransactionStatus status) {  
  19.                 jdbcTemplate1.update(INSERT_SQL, "test");  
  20.                //因爲數據庫2沒有創建數據庫表因此會回滾事務  
  21.               jdbcTemplate2.update(INSERT_SQL, "test");  
  22.           }});  
  23.     } catch (RuntimeException e) {  
  24.         int count = jdbcTemplate1.queryForInt(COUNT_SQL);  
  25.         Assert.assertEquals(originalCount, count);  
  26.     }  
  27.     jdbcTemplate1.update(DROP_TABLE_SQL);  
  28. }  
  • 配置文件:使用此前定義的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包裏。

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.model;  
  2. public class UserModel {  
  3.     private int id;  
  4.     private String name;  
  5.     private AddressModel address;  
  6.     //省略getter和setter  
  7. }  

 

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.model;  
  2. public class AddressModel {  
  3.     private int id;  
  4.     private String province;  
  5.     private String city;  
  6.     privateString street;  
  7.     private int userId;  
  8.     //省略getter和setter  
  9. }  

 

2.1、定義Dao層接口:

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.service;  
  2. import cn.javass.spring.chapter9.model.UserModel;  
  3. public interface IUserService {  
  4.     public void save(UserModel user);  
  5.     public int countAll();  
  6. }  
  7.    

 

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.service;  
  2. import cn.javass.spring.chapter9.model.AddressModel;  
  3. public interface IAddressService {  
  4.     public void save(AddressModel address);  
  5.     public int countAll();  
  6. }  

 

2.2、定義Dao層實現:

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.dao.jdbc;  
  2. //省略import,注意model要引用chapter包裏的  
  3. public class UserJdbcDaoImpl extends NamedParameterJdbcDaoSupport implements IUserDao {  
  4.     private final String INSERT_SQL = "insert into user(name) values(:name)";  
  5.     private final String COUNT_ALL_SQL = "select count(*) from user";  
  6.     @Override  
  7.     public void save(UserModel user) {  
  8.         KeyHolder generatedKeyHolder = new GeneratedKeyHolder();  
  9.         SqlParameterSource paramSource = new BeanPropertySqlParameterSource(user);  
  10.         getNamedParameterJdbcTemplate().update(INSERT_SQL, paramSource, generatedKeyHolder);  
  11.         user.setId(generatedKeyHolder.getKey().intValue());  
  12.     }  
  13.     @Override  
  14.     public int countAll() {  
  15.        return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);  
  16.     }  
  17. }  

 

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.dao.jdbc;  
  2. //省略import,注意model要引用chapter包裏的  
  3. public class AddressJdbcDaoImpl extends NamedParameterJdbcDaoSupport implements IAddressDao {  
  4.     private final String INSERT_SQL = "insert into address(province, city, street, user_id)" + "values(:province, :city, :street, :userId)";  
  5.     private final String COUNT_ALL_SQL = "select count(*) from address";  
  6.     @Override  
  7.     public void save(AddressModel address) {  
  8.         KeyHolder generatedKeyHolder = new GeneratedKeyHolder();  
  9.         SqlParameterSource paramSource = new BeanPropertySqlParameterSource(address);  
  10.         getNamedParameterJdbcTemplate().update(INSERT_SQL, paramSource, generatedKeyHolder);  
  11.         address.setId(generatedKeyHolder.getKey().intValue());  
  12. }  
  13.     @Override  
  14.     public int countAll() {  
  15.         return getJdbcTemplate().queryForInt(COUNT_ALL_SQL);  
  16.     }  
  17. }  

 

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

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.service;  
  2. import cn.javass.spring.chapter9.model.UserModel;  
  3. public interface IUserService {  
  4.     public void save(UserModel user);  
  5.     public int countAll();  
  6. }  
  7.   
  8.   
  9. package cn.javass.spring.chapter9.service;  
  10. import cn.javass.spring.chapter9.model.AddressModel;  
  11. public interface IAddressService {  
  12.     public void save(AddressModel address);  
  13.     public int countAll();  
  14. }  

 

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

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.service.impl;  
  2. //省略import,注意model要引用chapter包裏的  
  3. public class AddressServiceImpl implements IAddressService {  
  4.     private IAddressDao addressDao;  
  5.     private PlatformTransactionManager txManager;  
  6.     public void setAddressDao(IAddressDao addressDao) {  
  7.         this.addressDao = addressDao;  
  8.     }  
  9.     public void setTxManager(PlatformTransactionManager txManager) {  
  10.         this.txManager = txManager;  
  11.     }  
  12.     @Override  
  13.     public void save(final AddressModel address) {  
  14.         TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);  
  15.         transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
  16.            @Override  
  17.            protected void doInTransactionWithoutResult(TransactionStatus status) {  
  18.                 addressDao.save(address);  
  19.            }  
  20.         });  
  21.     }  
  22.     @Override  
  23.     public int countAll() {  
  24.         return addressDao.countAll();  
  25.     }  
  26. }  
  27.    

 

 

java代碼:
Java代碼  收藏代碼
  1.       
  2. package cn.javass.spring.chapter9.service.impl;  
  3. //省略import,注意model要引用chapter包裏的  
  4. public class UserServiceImpl implements IUserService {  
  5.     private IUserDao userDao;  
  6.     private IAddressService addressService;  
  7.     private PlatformTransactionManager txManager;  
  8.     public void setUserDao(IUserDao userDao) {  
  9.         this.userDao = userDao;  
  10.     }  
  11.     public void setTxManager(PlatformTransactionManager txManager) {  
  12.         this.txManager = txManager;  
  13.     }  
  14.     public void setAddressService(IAddressService addressService) {  
  15.         this.addressService = addressService;  
  16.     }  
  17.     @Override  
  18.     public void save(final UserModel user) {  
  19.         TransactionTemplate transactionTemplate =  
  20.             TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);  
  21.         transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
  22.         @Override  
  23.            protected void doInTransactionWithoutResult(TransactionStatus status) {  
  24.                 userDao.save(user);  
  25.                 user.getAddress().setUserId(user.getId());  
  26.                 addressService.save(user.getAddress());  
  27.            }  
  28.         });  
  29.     }  
  30.     @Override  
  31.     public int countAll() {  
  32.         return userDao.countAll();  
  33.     }  
  34. }  
  35.    
  36.    

 

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

 

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

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.util;  
  2. //省略import  
  3. public class TransactionTemplateUtils {  
  4.     public static TransactionTemplate getTransactionTemplate(  
  5.             PlatformTransactionManager txManager,  
  6.             int propagationBehavior,  
  7.             int isolationLevel) {  
  8.         
  9.         TransactionTemplate transactionTemplate = new TransactionTemplate(txManager);  
  10.         transactionTemplate.setPropagationBehavior(propagationBehavior);  
  11.         transactionTemplate.setIsolationLevel(isolationLevel);  
  12.         return transactionTemplate;  
  13.     }  
  14.      
  15.     public static TransactionTemplate getDefaultTransactionTemplate(PlatformTransactionManager txManager) {  
  16.         return getTransactionTemplate(  
  17.                 txManager,  
  18.                 TransactionDefinition.PROPAGATION_REQUIRED,  
  19.                 TransactionDefinition.ISOLATION_READ_COMMITTED);  
  20.     }  
  21. }  
  22.    

 

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

 

 

5、數據源配置定義,此處使用第7章的配置文件,即“chapter7/ applicationContext-resources.xml”文件。

 

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

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">    
  2.     <property name="dataSource" ref="dataSource"/>  
  3. </bean>  
  4. <bean id="abstractDao" abstract="true">  
  5.     <property name="dataSource" ref="dataSource"/>  
  6. </bean>    

 

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="userDao" class="cn.javass.spring.chapter9.dao.jdbc.UserJdbcDaoImpl" parent="abstractDao"/>  
  2. <bean id="addressDao" class="cn.javass.spring.chapter9.dao.jdbc.AddressJdbcDaoImpl" parent="abstractDao"/>  

 

 

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

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="userService" class="cn.javass.spring.chapter9.service.impl.UserServiceImpl">  
  2.     <property name="userDao" ref="userDao"/>  
  3.     <property name="txManager" ref="txManager"/>  
  4.     <property name="addressService" ref="addressService"/>  
  5. </bean>  
  6. <bean id="addressService" class="cn.javass.spring.chapter9.service.impl.AddressServiceImpl">  
  7.     <property name="addressDao" ref="addressDao"/>  
  8.     <property name="txManager" ref="txManager"/>  
  9. </bean>  

 

8、準備測試需要的表創建語句,在TransactionTest測試類中添加如下靜態變量:

 

java代碼:
Java代碼  收藏代碼
  1. private static final String CREATE_USER_TABLE_SQL =  
  2.     "create table user" +  
  3.     "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " +  
  4.     "name varchar(100))";  
  5. private static final String DROP_USER_TABLE_SQL = "drop table user";  
  6.    
  7. private static final String CREATE_ADDRESS_TABLE_SQL =  
  8.     "create table address" +  
  9.     "(id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, " +  
  10.     "province varchar(100), city varchar(100), street varchar(100), user_id int)";  
  11. private static final String DROP_ADDRESS_TABLE_SQL = "drop table address";  

 

9、 測試一下吧:

 

java代碼:
Java代碼  收藏代碼
  1. @Test  
  2.     public void testServiceTransaction() {  
  3.         String[] configLocations = new String[] {  
  4.         "classpath:chapter7/applicationContext-resources.xml",  
  5.         "classpath:chapter9/dao/applicationContext-jdbc.xml",  
  6.         "classpath:chapter9/service/applicationContext-service.xml"};  
  7.         ApplicationContext ctx2 = new ClassPathXmlApplicationContext(configLocations);  
  8.          
  9.         DataSource dataSource2 = ctx2.getBean(DataSource.class);  
  10.         JdbcTemplate jdbcTemplate2 = new JdbcTemplate(dataSource2);  
  11.         jdbcTemplate2.update(CREATE_USER_TABLE_SQL);  
  12.         jdbcTemplate2.update(CREATE_ADDRESS_TABLE_SQL);  
  13.          
  14.         IUserService userService = ctx2.getBean("userService", IUserService.class);  
  15.         IAddressService addressService = ctx2.getBean("addressService", IAddressService.class);  
  16.         UserModel user = createDefaultUserModel();  
  17.         userService.save(user);  
  18.         Assert.assertEquals(1, userService.countAll());  
  19.         Assert.assertEquals(1, addressService.countAll());  
  20.         jdbcTemplate2.update(DROP_USER_TABLE_SQL);  
  21.         jdbcTemplate2.update(DROP_ADDRESS_TABLE_SQL);  
  22. }  
  23. private UserModel createDefaultUserModel() {  
  24.     UserModel user = new UserModel();  
  25.     user.setName("test");  
  26.     AddressModel address = new AddressModel();  
  27.     address.setProvince("beijing");  
  28.     address.setCity("beijing");  
  29.     address.setStreet("haidian");  
  30.     user.setAddress(address);  
  31.     return user;  
  32. }  
  33.    

 

從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指定,表示如果當前存在一個邏輯事務,則加入該邏輯事務,否則將新建一個邏輯事務,如圖9-2和9-3所示;

 

圖9-2 Required傳播行爲

 

圖9-3 Required傳播行爲拋出異常情況

              在前邊示例中就是使用的Required傳播行爲:

一、在調用userService對象的save方法時,此方法用的是Required傳播行爲且此時Spring事務管理器發現還沒開啓邏輯事務,因此Spring管理器覺得開啓邏輯事務,

二、在此邏輯事務中調用了addressService對象的save方法,而在save方法中發現同樣用的是Required傳播行爲,因此使用該已經存在的邏輯事務;

三、在返回到addressService對象的save方法,當事務模板類執行完畢,此時提交併關閉事務。

       因此userService對象的save方法和addressService的save方法屬於同一個物理事務,如果發生回滾,則兩者都回滾。

 

 

接下來測試一下該傳播行爲如何執行吧:

一、正確提交測試,如上一節的測試,在此不再演示;

二、回滾測試,修改AddressServiceImpl的save方法片段:

 

java代碼:
Java代碼  收藏代碼
  1. addressDao.save(address);  

 

 

java代碼:
Java代碼  收藏代碼
  1. addressDao.save(address);  
  2. //拋出異常,將標識當前事務需要回滾  
  3. throw new RuntimeException();  

 

二、修改UserServiceImpl的save方法片段:

 

java代碼:
Java代碼  收藏代碼
  1. addressService.save(user.getAddress());  

 

 

java代碼:
Java代碼  收藏代碼
  1. try {  
  2.     addressService.save(user.getAddress());//將在同一個事務內執行  
  3. catch (RuntimeException e) {  
  4. }  
  5.    

 

如果該業務方法執行時事務被標記爲回滾,則不管在此是否捕獲該異常都將發生回滾,因爲處於同一邏輯事務。

 

三、修改測試方法片段:

 

java代碼:
Java代碼  收藏代碼
  1. userService.save(user);  
  2. Assert.assertEquals(1, userService.countAll());  
  3. Assert.assertEquals(1, addressService.countAll());  

 

爲如下形式:

 

 

java代碼:
Java代碼  收藏代碼
  1. try {  
  2.     userService.save(user);  
  3.     Assert.fail();  
  4. catch (RuntimeException e) {  
  5. }  
  6. Assert.assertEquals(0, userService.countAll());  
  7. Assert.assertEquals(0, addressService.countAll());  

 

Assert斷言中countAll方法都返回0,說明事務回滾了,即說明兩個業務方法屬於同一個物理事務,即使在userService對象的save方法中將異常捕獲,由於addressService對象的save方法拋出異常,即事務管理器將自動標識當前事務爲需要回滾。

 

 

 

 

RequiresNew:創建新的邏輯事務,使用PROPAGATION_REQUIRES_NEW指定,表示每次都創建新的邏輯事務(物理事務也是不同的)如圖9-4和9-5所示:

 

圖9-4 RequiresNew傳播行爲

 

圖9-5 RequiresNew傳播行爲並拋出異常

接下來測試一個該傳播行爲如何執行吧:

1、將如下獲取事務模板方式

 

java代碼:
Java代碼  收藏代碼
  1. TransactionTemplate transactionTemplate = TransactionTemplateUtils.getDefaultTransactionTemplate(txManager);  

 

       替換爲如下形式,表示傳播行爲爲RequiresNew:

 

java代碼:
Java代碼  收藏代碼
  1. TransactionTemplate transactionTemplate = TransactionTemplateUtils.getTransactionTemplate(  
  2.         txManager,   
  3.         TransactionDefinition.PROPAGATION_REQUIRES_NEW,   
  4.         TransactionDefinition.ISOLATION_READ_COMMITTED);  

 

2、執行如下測試,發現執行結果是正確的:

 

java代碼:
Java代碼  收藏代碼
  1. userService.save(user);  
  2. Assert.assertEquals(1, userService.countAll());  
  3. Assert.assertEquals(1, addressService.countAll());  

 

3、修改UserServiceImpl的save方法片段

 

java代碼:
Java代碼  收藏代碼
  1. userDao.save(user);         
  2. user.getAddress().setUserId(user.getId());  
  3. addressService.save(user.getAddress());  

 

爲如下形式,表示userServiceImpl類的save方法將發生回滾,而AddressServiceImpl類的方法由於在拋出異常前執行,將成功提交事務到數據庫:

 

 

java代碼:
Java代碼  收藏代碼
  1. userDao.save(user);         
  2. user.getAddress().setUserId(user.getId());  
  3. addressService.save(user.getAddress());  
  4. throw new RuntimeException();  

 

4、修改測試方法片段:

 

java代碼:
Java代碼  收藏代碼
  1. userService.save(user);  
  2. Assert.assertEquals(1, userService.countAll());  
  3. Assert.assertEquals(1, addressService.countAll());  

 

爲如下形式:

 

 

java代碼:
Java代碼  收藏代碼
  1. try {  
  2.     userService.save(user);  
  3.     Assert.fail();  
  4. catch (RuntimeException e) {  
  5. }  
  6. Assert.assertEquals(0, userService.countAll());  
  7. Assert.assertEquals(1, addressService.countAll());  

 

Assert斷言中調用userService對象countAll方法返回0,說明該邏輯事務作用域回滾,而調用addressService對象的countAll方法返回1,說明該邏輯事務作用域正確提交。因此這是不正確的行爲,因爲用戶和地址應該是一一對應的,不應該發生這種情況,因此此處正確的傳播行爲應該是Required。

 

該傳播行爲執行流程(正確提交情況):

一、當執行userService對象的save方法時,由於傳播行爲是RequiresNew,因此創建一個新的邏輯事務(物理事務也是不同的);

二、當執行到addressService對象的save方法時,由於傳播行爲是RequiresNew,因此首先暫停上一個邏輯事務並創建一個新的邏輯事務(物理事務也是不同的);

三、addressService對象的save方法執行完畢後,提交邏輯事務(並提交物理事務)並重新恢復上一個邏輯事務,繼續執行userService對象的save方法內的操作;

四、最後userService對象的save方法執行完畢,提交邏輯事務(並提交物理事務);

五、userService對象的save方法和addressService對象的save方法不屬於同一個邏輯事務且也不屬於同一個物理事務。

 

 

 

 

Supports:支持當前事務,使用PROPAGATION_SUPPORTS指定,指如果當前存在邏輯事務,就加入到該邏輯事務,如果當前沒有邏輯事務,就以非事務方式執行,如圖9-6和9-7所示:

 

圖9-6 Required+Supports傳播行爲

 

       圖9-7 Supports+Supports傳播行爲

 

 

 

 

NotSupported:不支持事務,如果當前存在事務則暫停該事務,使用PROPAGATION_NOT_SUPPORTED指定,即以非事務方式執行,如果當前存在邏輯事務,就把當前事務暫停,以非事務方式執行,如圖9-8和9-9所示:

 

       圖9-8 Required+NotSupported傳播行爲

 

       圖9-9 Supports+NotSupported傳播行爲

 

 

 

 

Mandatory:必須有事務,否則拋出異常,使用PROPAGATION_MANDATORY指定,使用當前事務執行,如果當前沒有事務,則拋出異常(IllegalTransactionStateException),如圖9-10和9-11所示:

 

 

 

       圖9-10 Required+Mandatory傳播行爲

 

       圖9-11 Supports+Mandatory傳播行爲

 

 

 

 

Never:不支持事務,如果當前存在是事務則拋出異常,使用PROPAGATION_NEVER指定,即以非事務方式執行,如果當前存在事務,則拋出異常(IllegalTransactionStateException),如圖9-12和9-13所示:

 

       圖9-12 Required+Never傳播行爲

 

       圖9-13 Supports+Never傳播行爲

 

 

 

 

 

Nested:嵌套事務支持,使用PROPAGATION_NESTED指定,如果當前存在事務,則在嵌套事務內執行,如果當前不存在事務,則創建一個新的事務,嵌套事務使用數據庫中的保存點來實現,即嵌套事務回滾不影響外部事務,但外部事務回滾將導致嵌套事務回滾,如圖9-14和9-15所示:

 

       圖9-14 Required+Nested傳播行爲

 

圖9-15 Nested+Nested傳播行爲

 

 

 

Nested和RequiresNew的區別:

1、  RequiresNew每次都創建新的獨立的物理事務,而Nested只有一個物理事務;

2、  Nested嵌套事務回滾或提交不會導致外部事務回滾或提交,但外部事務回滾將導致嵌套事務回滾,而 RequiresNew由於都是全新的事務,所以之間是無關聯的;

3、  Nested使用JDBC 3的保存點實現,即如果使用低版本驅動將導致不支持嵌套事務。

使用嵌套事務,必須確保具體事務管理器實現的nestedTransactionAllowed屬性爲true,否則不支持嵌套事務,如DataSourceTransactionManager默認支持,而HibernateTransactionManager默認不支持,需要我們來開啓。

對於事務傳播行爲我們只演示了Required和RequiresNew,其他傳播行爲類似,如果對這些事務傳播行爲不太會使用,請參考chapter9包下的TransactionTest測試類中的testPropagation方法,方法內有詳細示例。

 

  • 事務超時:設置事務的超時時間,單位爲秒,默認爲-1表示使用底層事務的超時時間;

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

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

 

  • 事務只讀:將事務標識爲只讀,只讀事務不修改任何數據;

         對於JDBC只是簡單的將連接設置爲只讀模式,對於更新將拋出異常;

         而對於一些其他ORM框架有一些優化作用,如在Hibernate中,Spring事務管理器將執行“session.setFlushMode(FlushMode.MANUAL)”即指定Hibernate會話在只讀事務模式下不用嘗試檢測和同步持久對象的狀態的更新。

         如果使用設置具體事務管理的validateExistingTransaction屬性爲true(默認false),將確保整個事務傳播鏈都是隻讀或都不是隻讀,如圖9-16是正確的事務只讀設置,而圖9-17是錯誤的事務只讀設置:

 

圖9-16 正確的事務只讀設置

 

 

圖9-17 錯誤的事務只讀設置

如圖10-17,對於錯誤的事務只讀設置將拋出IllegalTransactionStateException異常,並伴隨“Participating transaction with definition [……] is not marked as read-only……”信息,表示參與的事務只讀屬性設置錯誤。

 

 

大家有沒有感覺到編程式實現事務管理是不是很繁瑣冗長,重複,而且是侵入式的,因此發展到這Spring決定使用配置方式實現事務管理。

 

 

9.3.6  配置方式實現事務管理

在Spring2.x之前爲了解決編程式事務管理的各種不好問題,Spring提出使用配置方式實現事務管理,配置方式利用代理機制實現,即使有TransactionProxyFactoryBean類來爲目標類代理事務管理。

 

接下來演示一下具體使用吧:

1、重新定義業務類實現,在業務類中無需顯示的事務管理代碼:

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.service.impl;  
  2. //省略import  
  3. public class ConfigAddressServiceImpl implements IAddressService {  
  4.     private IAddressDao addressDao;  
  5.     public void setAddressDao(IAddressDao addressDao) {  
  6.         this.addressDao = addressDao;  
  7.     }  
  8.     @Override  
  9.     public void save(final AddressModel address) {  
  10.         addressDao.save(address);  
  11.     }  
  12.     //countAll方法實現不變  
  13. }  

 

 

java代碼:
Java代碼  收藏代碼
  1. package cn.javass.spring.chapter9.service.impl;  
  2. //省略import  
  3. public class ConfigUserServiceImpl implements IUserService {  
  4.     private IUserDao userDao;  
  5.     private IAddressService addressService;  
  6.     public void setUserDao(IUserDao userDao) {  
  7.         this.userDao = userDao;  
  8.     }  
  9.     public void setAddressService(IAddressService addressService) {  
  10.         this.addressService = addressService;  
  11.     }  
  12.     @Override  
  13.     public void save(final UserModel user) {  
  14.         userDao.save(user);  
  15.         user.getAddress().setUserId(user.getId());  
  16.         addressService.save(user.getAddress());  
  17.     }  
  18.     //countAll方法實現不變  
  19. }  

 

 

從以上業務類中可以看出,沒有事務管理的代碼,即沒有侵入式的代碼。

 

2、在chapter9/service/applicationContext-service.xml配置文件中添加如下配置:

2.1、首先添加目標類定義:

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="targetUserService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">  
  2.     <property name="userDao" ref="userDao"/>  
  3.     <property name="addressService" ref="targetAddressService"/>  
  4. </bean>  
  5. <bean id="targetAddressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">  
  6.     <property name="addressDao" ref="addressDao"/>  
  7. </bean>  

 

2.2、配置TransactionProxyFactoryBean類:

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="transactionProxyParent" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  abstract="true">  
  2.     <property name="transactionManager" ref="txManager"/>  
  3.     <property name="transactionAttributes">  
  4.     <props>  
  5.             <prop key="save*">  
  6.                       PROPAGATION_REQUIRED,  
  7.                       ISOLATION_READ_COMMITTED,  
  8.                       timeout_10,  
  9.                       -Exception,  
  10.                       +NoRollBackException  
  11.            </prop>  
  12.            <prop key="*">  
  13.                       PROPAGATION_REQUIRED,  
  14.                       ISOLATION_READ_COMMITTED,  
  15.                       readOnly  
  16.            </prop>  
  17.         </props>  
  18. </property>  
  19. </bean>  
  20.    
  • TransactionProxyFactoryBean:用於爲目標業務類創建代理的Bean;
  • abstract="true":表示該Bean是抽象的,用於去除重複配置;
  • transactionManager:事務管理器定義;
  • transactionAttributes:表示事務屬性定義:
  • PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,timeout_10,-Exception,+NoRollBackException:事務屬性定義,Required傳播行爲,提交讀隔離級別,事務超時時間爲10秒,將對所有Exception異常回滾,而對於拋出NoRollBackException異常將不發生回滾而是提交;
  • PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,readOnly:事務屬性定義,Required傳播行爲,提交讀隔離級別,事務是隻讀的,且只對默認的RuntimeException異常回滾;
  • <prop key="save*">:表示將代理以save開頭的方法,即當執行到該方法時會爲該方法根據事務屬性配置來開啓/關閉事務;
  • <prop key="*">:表示將代理其他所有方法,但需要注意代理方式,默認是JDK代理,只有public方法能代理;

 

注:事務屬性的傳播行爲和隔離級別使用TransactionDefinition靜態變量名指定;事務超時使用“timeout_超時時間”指定,事務只讀使用“readOnly”指定,需要回滾的異常使用“-異常”指定,不需要回滾的異常使用“+異常”指定,默認只對RuntimeException異常回滾。

 

需要特別注意“-異常”和“+異常”中“異常”只是真實異常的部分名,內部使用如下方式判斷:

 

java代碼:
Java代碼  收藏代碼
  1. //真實拋出的異常.name.indexOf(配置中指定的需要回滾/不回滾的異常名)  
  2. exceptionClass.getName().indexOf(this.exceptionName)  
  3.    

       因此異常定義時需要特別注意,配置中定義的異常只是真實異常的部分名。

 

 

2.3、定義代理Bean:

 

java代碼:
Java代碼  收藏代碼
  1. <bean id="proxyUserService" parent="transactionProxyParent">  
  2.     <property name="target" ref="targetUserService"/>  
  3. </bean>  
  4. <bean id="proxyAddressService" parent="transactionProxyParent">  
  5.     <property name="target" ref="targetAddressService"/>  
  6. </bean>  

 

代理Bean通過集成抽象Bean“transactionProxyParent”,並通過target屬性設置目標Bean,在實際使用中應該使用該代理Bean。

 

 

3、修改測試方法並測試該配置方式是否好用:

將TransactionTest 類的testServiceTransaction測試方法拷貝一份命名爲testConfigTransaction:

並在testConfigTransaction測試方法內將:

 

java代碼:
Java代碼  收藏代碼
  1. IUserService userService =  
  2. ctx2.getBean("userService", IUserService.class);  
  3. IAddressService addressService =  
  4. ctx2.getBean("addressService", IAddressService.class);  

 

替換爲:

 

java代碼:
Java代碼  收藏代碼
  1. IUserService userService =  
  2. ctx2.getBean("proxyUserService ", IUserService.class);  
  3. IAddressService addressService =  
  4. ctx2.getBean("proxyAddressService ", IAddressService.class);  

 

4、執行測試,測試正常通過,說明該方式能正常工作,當調用save方法時將匹配到“<prop key="save*">”定義,而countAll將匹配到“<prop key="save*">”定義,底層代理會應用相應定義中的事務屬性來創建或關閉事務。

 

 

圖9-18 代理方式實現事務管理

       如圖9-18,代理方式實現事務管理只是將硬編碼的事務管理代碼轉移到代理中去由代理實現,在代理中實現事務管理。

 

       注:在代理模式下,默認只有通過代理對象調用的方法才能應用相應的事務屬性,而在目標方法內的“自我調用”是不會應用相應的事務屬性的,即被調用方法不會應用相應的事務屬性,而是使用調用方法的事務屬性。

 

如圖9-19所示,在目標對象targetUserService的save方法內調用事務方法“this.otherTransactionMethod()”將不會應用配置的傳播行爲RequriesNew,開啓新事務,而是使用save方法的已開啓事務,如果非要這樣使用如下方式實現:

 

1、  修改TransactionProxyFactoryBean配置定義,添加exposeProxy屬性爲true;

2、  在業務方法內通過代理對象調用相應的事務方放,如 “((IUserService)AopContext.currentProxy()).otherTransactionMethod()”即可應用配置的事務屬性。

3、  使用這種方式屬於侵入式,不推薦使用,除非必要。

 

圖9-19 代理方式下的自我調用

 

       配置方式也好麻煩啊,每個業務實現都需要配置一個事務代理,發展到這,Spring想出更好的解決方案,Spring2.0及之後版本提出使用新的“<tx:tags/>”方式配置事務,從而無需爲每個業務實現配置一個代理。

 

 

原創內容,轉載請註明出處【http://sishuok.com/forum/blogPost/list/2506.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章