详解Java的MyBatis框架中的事务处理
一、MyBatis单独使用时,使用SqlSession来处理事务:
publicclassMyBatisTxTest{ privatestaticSqlSessionFactorysqlSessionFactory; privatestaticReaderreader; @BeforeClass publicstaticvoidsetUpBeforeClass()throwsException{ try{ reader=Resources.getResourceAsReader("Configuration.xml"); sqlSessionFactory=newSqlSessionFactoryBuilder().build(reader); }finally{ if(reader!=null){ reader.close(); } } } @Test publicvoidupdateUserTxTest(){ SqlSessionsession=sqlSessionFactory.openSession(false);//打开会话,事务开始 try{ IUserMappermapper=session.getMapper(IUserMapper.class); Useruser=newUser(9,"Testtransaction"); intaffectedCount=mapper.updateUser(user);//因后面的异常而未执行commit语句 Useruser=newUser(10,"Testtransactioncontinuously"); intaffectedCount2=mapper.updateUser(user2);//因后面的异常而未执行commit语句 inti=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对象:
<beanid="transactionManager"class="org.springframework.jdbc.datasource .DataSourceTransactionManager"> <propertyname="dataSource"ref="dataSource"/> </bean>
指定的DataSource一般可以是你使用Spring的任意JDBCDataSource。这包含了连接池和通过JNDI查找获得的DataSource。
要注意,为事务管理器指定的DataSource必须和用来创建SqlSessionFactoryBean的是同一个数据源,否则事务管理器就无法工作了。
2.容器管理事务
如果你正使用一个JEE容器而且想让Spring参与到容器管理事务中,那么Spring应该使用JtaTransactionManager或它的容器指定的子类来配置。做这件事情的最方便的方式是用Spring的事务命名空间:
<tx:jta-transaction-manager/>
在这种配置中,MyBatis将会和其它由容器管理事务配置的Spring事务资源一样。Spring会自动使用任意存在的容器事务,在上面附加一个SqlSession。如果没有开始事务,或者需要基于事务配置,Spring会开启一个新的容器管理事务。
注意,如果你想使用容器管理事务,而不想使用Spring的事务管理,你就必须配置SqlSessionFactoryBean来使用基本的MyBatis的ManagedTransactionFactory而不是其它任意的Spring事务管理器:
<beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"> <propertyname="dataSource"ref="dataSource"/> <propertyname="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事务之外任意调用映射器方法都将会自动被提交。下面是一个编程式事务示例:
DefaultTransactionDefinitiondef=newDefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatusstatus=txManager.getTransaction(def); try{ userMapper.insertUser(user); }catch(MyExceptionex){ throwex; } txManager.commit(status);
4.@Transactional方式:
在类路径下创建beans-da-tx.xml文件,在beans-da.xml(系列五)的基础上加入事务配置:
<!--事务管理器--> <beanid="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <propertyname="dataSource"ref="dataSource"/> </bean> <!--事务注解驱动,标注@Transactional的类和方法将具有事务性--> <tx:annotation-driventransaction-manager="txManager"/> <beanid="userService"class="com.john.hbatis.service.UserService"/>
服务类:
@Service("userService") publicclassUserService{ @Autowired IUserMappermapper; publicintbatchUpdateUsersWhenException(){//非事务性 Useruser=newUser(9,"Beforeexception"); intaffectedCount=mapper.updateUser(user);//执行成功 Useruser2=newUser(10,"Afterexception"); inti=1/0;//抛出运行时异常 intaffectedCount2=mapper.updateUser(user2);//未执行 if(affectedCount==1&&affectedCount2==1){ return1; } return0; } @Transactional publicinttxUpdateUsersWhenException(){//事务性 Useruser=newUser(9,"Beforeexception"); intaffectedCount=mapper.updateUser(user);//因后面的异常而回滚 Useruser2=newUser(10,"Afterexception"); inti=1/0;//抛出运行时异常,事务回滚 intaffectedCount2=mapper.updateUser(user2);//未执行 if(affectedCount==1&&affectedCount2==1){ return1; } return0; } }
在测试类中加入:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:beans-da-tx.xml"}) publicclassSpringIntegrateTxTest{ @Resource UserServiceuserService; @Test publicvoidupdateUsersExceptionTest(){ userService.batchUpdateUsersWhenException(); } @Test publicvoidtxUpdateUsersExceptionTest(){ userService.txUpdateUsersWhenException(); } }
5.TransactionTemplate方式
在beans-da-tx.xml中添加:
<beanid="txTemplate"class="org.springframework.transaction.support.TransactionTemplate"> <constructor-argtype="org.springframework.transaction.PlatformTransactionManager"ref="transactionManager"/> </bean>
在UserService类加入:
@Autowired(required=false) TransactionTemplatetxTemplate; publicinttxUpdateUsersWhenExceptionViaTxTemplate(){ intretVal=txTemplate.execute(newTransactionCallback<Integer>(){ @Override publicIntegerdoInTransaction(TransactionStatusstatus){//事务操作 Useruser=newUser(9,"Beforeexception"); intaffectedCount=mapper.updateUser(user);//因后面的异常而回滚 Useruser2=newUser(10,"Afterexception"); inti=1/0;//抛出运行时异常并回滚 intaffectedCount2=mapper.updateUser(user2);//未执行 if(affectedCount==1&&affectedCount2==1){ return1; } return0; } }); returnretVal; }
在SpringIntegrateTxTest类中加入:
@Test publicvoidupdateUsersWhenExceptionViaTxTemplateTest(){ userService.txUpdateUsersWhenExceptionViaTxTemplate();// }
注:不可catchException或RuntimeException而不抛出:
@Transactional publicinttxUpdateUsersWhenExceptionAndCatch(){//事务性操作,但是外围框架捕获不到异常,认为执行正确而提交。 try{ Useruser=newUser(9,"Beforeexception"); intaffectedCount=mapper.updateUser(user);//执行成功 Useruser2=newUser(10,"Afterexception"); inti=1/0;//抛出运行时异常 intaffectedCount2=mapper.updateUser(user2);//未执行 if(affectedCount==1&&affectedCount2==1){ return1; } }catch(Exceptione){//所有异常被捕获而未抛出 e.printStackTrace(); } return0; }