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

 更新时间:2016年06月01日 15:20:25   作者:fhd001  
利用MyBatis框架的配置管理比直接使用JDBC API编写事务控制要来得更加轻松,这里我们就来详解Java的MyBatis框架中的事务处理,尤其是和Spring框架集成后更加exciting

一、MyBatis单独使用时,使用SqlSession来处理事务:

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对象:

<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的事务命名空间:

<tx:jta-transaction-manager/> 

在这种配置中,MyBatis将会和其它由容器管理事务配置的Spring事务资源一样。Spring会自动使用任意存在的容器事务,在上面附加一个SqlSession。 如果没有开始事务,或者需要基于事务配置,Spring会开启一个新的容器管理事务。
注意,如果你想使用容器管理事务,而不想使用Spring的事务管理,你就必须配置SqlSessionFactoryBean来使用基本的MyBatis的ManagedTransactionFactory而不是其它任意的Spring事务管理器: 

<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事务之外任意调用映射器方法都将会自动被提交。下面是一个编程式事务示例:

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(系列五)的基础上加入事务配置:

<!-- 事务管理器 --> 
<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" /> 

服务类:

@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; 
  } 
} 

在测试类中加入:

@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中添加:

<bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate"> 
  <constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="transactionManager" /> 
</bean> 

在UserService类加入:

@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类中加入:

@Test 
public void updateUsersWhenExceptionViaTxTemplateTest() { 
  userService.txUpdateUsersWhenExceptionViaTxTemplate(); //  
} 

注:不可catch Exception或RuntimeException而不抛出:

@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; 
} 

相关文章

  • java控制台实现学生信息管理系统(集合版)

    java控制台实现学生信息管理系统(集合版)

    这篇文章主要为大家详细介绍了java控制台实现学生信息管理系统的集合版,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • Java代码性能测试实战之ContiPerf安装使用

    Java代码性能测试实战之ContiPerf安装使用

    这篇文章主要为大家介绍了Java代码性能测试实战之ContiPerf安装使用,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • springboot将lib和jar分离的操作方法

    springboot将lib和jar分离的操作方法

    本文介绍了如何通过优化pom.xml配置来减小Spring Boot项目的jar包大小,主要通过使用spring-boot-maven-plugin和maven-dependency-plugin插件,将依赖库打包到jar中,并通过指定外部lib路径的方式运行jar,从而减小jar包体积,感兴趣的朋友一起看看吧
    2025-02-02
  • Springboot打包代码,反编译后代码混淆方式(防止还原代码)

    Springboot打包代码,反编译后代码混淆方式(防止还原代码)

    文章主要介绍了如何对Spring Boot项目进行jar包混淆,以防止反编译还原原始代码,通过在项目中添加proguard.cfg文件并配置Maven插件,可以实现代码混淆,从而增加反编译的难度
    2024-11-11
  • Java编程在方法中哪些时候需要参数

    Java编程在方法中哪些时候需要参数

    这篇文章主要介绍了Java编程在方法中哪些时候需要参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Java分页查询的几种实现方法举例

    Java分页查询的几种实现方法举例

    这篇文章主要给大家介绍了关于Java分页查询的几种实现方法,分页是系统中常用到的功能,只要涉及到查询必定伴随而来的就是分页,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • springboot使用Thymeleaf报错常见的几种解决方案

    springboot使用Thymeleaf报错常见的几种解决方案

    这篇文章主要介绍了springboot使用Thymeleaf报错常见的几种解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • redis redisTemplate数据类型基础操作

    redis redisTemplate数据类型基础操作

    这篇文章主要介绍了redis redisTemplate数据类型基础操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • 详解Java反射实现Aop代理

    详解Java反射实现Aop代理

    本篇文章主要介绍了Java 反射实现 Aop 代理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Spring Cloud 使用 Resilience4j 实现服务熔断的方法

    Spring Cloud 使用 Resilience4j 实现服务熔断的方法

    服务熔断是为了保护我们的服务,比如当某个服务出现问题的时候,控制打向它的流量,让它有时间去恢复,或者限制一段时间只能有固定数量的请求打向这个服务,这篇文章主要介绍了Spring Cloud 使用 Resilience4j 实现服务熔断,需要的朋友可以参考下
    2022-12-12

最新评论