详解Java的MyBatis框架中的事务处理

转载来源:http://www.jb51.net/article/85622.htm

一、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 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值