一、MyBatis單獨使用時,使用SqlSession來處理事務:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class MyBatisTxTest { private static SqlSessionFactory sqlSessionFactory; private static Reader reader; @BeforeClass public static void setUpBeforeClass() throws Exception { try { reader = Resources.getResourceAsReader( "Configuration.xml" ); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } finally { if (reader != null ) { reader.close(); } } } @Test public void updateUserTxTest() { SqlSession session = sqlSessionFactory.openSession( false ); // 打開會話,事務開始 try { IUserMapper mapper = session.getMapper(IUserMapper. class ); User user = new User( 9 , "Test transaction" ); int affectedCount = mapper.updateUser(user); // 因后面的異常而未執行commit語句 User user = new User( 10 , "Test transaction continuously" ); int affectedCount2 = mapper.updateUser(user2); // 因后面的異常而未執行commit語句 int i = 2 / 0 ; // 觸發運行時異常 session.commit(); // 提交會話,即事務提交 } finally { session.close(); // 關閉會話,釋放資源 } } } |
二、和Spring集成后,使用Spring的事務管理:
一個使用MyBatis-Spring的主要原因是它允許MyBatis參與到Spring的事務管理中。而不是給MyBatis創建一個新的特定的事務管理器,MyBatis-Spring利用了存在于Spring中的DataSourceTransactionManager。
一旦DataSourceTransactionManager配置好了,你可以在Spring中以你通常的做法來配置事務。@Transactional注解和AOP樣式的配置都是支持的。在事務處理期間,一個單獨的SqlSession對象將會被創建和使用。當事務完成時,這個session會以合適的方式提交或回滾。
一旦事務創建之后,MyBatis-Spring將會透明的管理事務。在你的DAO或Service類中就不需要額外的代碼了。
1.標準配置
要開啟Spring的事務處理,在Spring的XML配置文件中簡單創建一個DataSourceTransactionManager對象:
1
2
3
4
|
< bean id = "transactionManager" class="org.springframework.jdbc.datasource .DataSourceTransactionManager"> < property name = "dataSource" ref = "dataSource" /> </ bean > |
指定的DataSource一般可以是你使用Spring的任意JDBC DataSource。這包含了連接池和通過JNDI查找獲得的DataSource。
要注意,為事務管理器指定的DataSource必須和用來創建SqlSessionFactoryBean的是同一個數據源,否則事務管理器就無法工作了。
2.容器管理事務
如果你正使用一個JEE容器而且想讓Spring參與到容器管理事務中,那么Spring應該使用JtaTransactionManager或它的容器指定的子類來配置。做這件事情的最方便的方式是用Spring的事務命名空間:
1
|
< tx:jta-transaction-manager /> |
在這種配置中,MyBatis將會和其它由容器管理事務配置的Spring事務資源一樣。Spring會自動使用任意存在的容器事務,在上面附加一個SqlSession。 如果沒有開始事務,或者需要基于事務配置,Spring會開啟一個新的容器管理事務。
注意,如果你想使用容器管理事務,而不想使用Spring的事務管理,你就必須配置SqlSessionFactoryBean來使用基本的MyBatis的ManagedTransactionFactory而不是其它任意的Spring事務管理器:
1
2
3
4
5
6
|
< bean id = "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > < property name = "dataSource" ref = "dataSource" /> < property name = "transactionFactoryClass" > < value >org.apache.ibatis.transaction.managed.ManagedTransactionFactory"/> </ property > </ bean > |
3.編程式事務管理
MyBatis的SqlSession提供指定的方法來處理編程式的事務。但是當使用MyBatis-Spring時,bean將會使用Spring管理的SqlSession或映射器來注入。那就是說Spring通常是處理事務的。你不能在Spring管理的SqlSession上調用SqlSession.commit(),SqlSession.rollback()或SqlSession.close()方法。如果這樣做了,就會拋出UnsupportedOperationException異常。注意在使用注入的映射器時不能訪問那些方法。無論連接是否設置為自動提交,SqlSession數據方法的執行或在Spring事務之外任意調用映射器方法都將會自動被提交。下面是一個編程式事務示例:
1
2
3
4
5
6
7
8
9
|
DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = txManager.getTransaction(def); try { userMapper.insertUser(user); } catch (MyException ex){ throw ex; } txManager.commit(status); |
4.@Transactional方式:
在類路徑下創建beans-da-tx.xml文件,在beans-da.xml(系列五)的基礎上加入事務配置:
1
2
3
4
5
6
7
8
9
10
|
<!-- 事務管理器 --> < bean id = "txManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > < property name = "dataSource" ref = "dataSource" /> </ bean > <!-- 事務注解驅動,標注@Transactional的類和方法將具有事務性 --> < tx:annotation-driven transaction-manager = "txManager" /> < bean id = "userService" class = "com.john.hbatis.service.UserService" /> |
服務類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@Service ( "userService" ) public class UserService { @Autowired IUserMapper mapper; public int batchUpdateUsersWhenException() { // 非事務性 User user = new User( 9 , "Before exception" ); int affectedCount = mapper.updateUser(user); // 執行成功 User user2 = new User( 10 , "After exception" ); int i = 1 / 0 ; // 拋出運行時異常 int affectedCount2 = mapper.updateUser(user2); // 未執行 if (affectedCount == 1 && affectedCount2 == 1 ) { return 1 ; } return 0 ; } @Transactional public int txUpdateUsersWhenException() { // 事務性 User user = new User( 9 , "Before exception" ); int affectedCount = mapper.updateUser(user); // 因后面的異常而回滾 User user2 = new User( 10 , "After exception" ); int i = 1 / 0 ; // 拋出運行時異常,事務回滾 int affectedCount2 = mapper.updateUser(user2); // 未執行 if (affectedCount == 1 && affectedCount2 == 1 ) { return 1 ; } return 0 ; } } |
在測試類中加入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations = { "classpath:beans-da-tx.xml" }) public class SpringIntegrateTxTest { @Resource UserService userService; @Test public void updateUsersExceptionTest() { userService.batchUpdateUsersWhenException(); } @Test public void txUpdateUsersExceptionTest() { userService.txUpdateUsersWhenException(); } } |
5.TransactionTemplate方式
在beans-da-tx.xml中添加:
1
2
3
|
< bean id = "txTemplate" class = "org.springframework.transaction.support.TransactionTemplate" > < constructor-arg type = "org.springframework.transaction.PlatformTransactionManager" ref = "transactionManager" /> </ bean > |
在UserService類加入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Autowired (required = false ) TransactionTemplate txTemplate; public int txUpdateUsersWhenExceptionViaTxTemplate() { int retVal = txTemplate.execute( new TransactionCallback<Integer>() { @Override public Integer doInTransaction(TransactionStatus status) { // 事務操作 User user = new User( 9 , "Before exception" ); int affectedCount = mapper.updateUser(user); // 因后面的異常而回滾 User user2 = new User( 10 , "After exception" ); int i = 1 / 0 ; // 拋出運行時異常并回滾 int affectedCount2 = mapper.updateUser(user2); // 未執行 if (affectedCount == 1 && affectedCount2 == 1 ) { return 1 ; } return 0 ; } }); return retVal; } |
在SpringIntegrateTxTest類中加入:
1
2
3
4
|
@Test public void updateUsersWhenExceptionViaTxTemplateTest() { userService.txUpdateUsersWhenExceptionViaTxTemplate(); // } |
注:不可catch Exception或RuntimeException而不拋出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Transactional public int txUpdateUsersWhenExceptionAndCatch() { // 事務性操作,但是外圍框架捕獲不到異常,認為執行正確而提交。 try { User user = new User( 9 , "Before exception" ); int affectedCount = mapper.updateUser(user); // 執行成功 User user2 = new User( 10 , "After exception" ); int i = 1 / 0 ; // 拋出運行時異常 int affectedCount2 = mapper.updateUser(user2); // 未執行 if (affectedCount == 1 && affectedCount2 == 1 ) { return 1 ; } } catch (Exception e) { // 所有異常被捕獲而未拋出 e.printStackTrace(); } return 0 ; } |