一.MyBatis单独使用时,使用SqlSession来处理事务
使用MyBatis,你可以写代码去控制事务操作。例如,提交事务和回滚事务。
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(); // 关闭会话,释放资源 } } }
xml配置文件:
<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>
缺点:可能会在每一个方法中,都需要添加事务的提交、回滚、关闭等,过于繁琐。
二.和Spring集成后,使用Spring的事务管理
事务管理方式:spring支持编程式事务管理和声明式事务管理两种方式。
1、@ Transactional 声明式事务处理
(1)为了使用Spring的事务处理能力,我们需要配置TransactionManager在Spring的配置文件中。
<bean id="transactionManager" class="org.springframework.jdbc.
datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
(2)一旦DataSourceTransactionManager配置好了,你可以在Spring中以你通常的做法来配置事务。在事务处理期间,一个单独的SqlSession对象将会被创建和使用。当事务完成时,这个session会以合适的方式提交或回滚。这个dataSource涉及到的需要事务处理的相同的dataSource,这个将会用到SqlSessionFactory的bean中。
基于注解的事务处理特性,Spring需要先使用下面的配置:
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务注解驱动,标注@Transactional的类和方法将具有事务性 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 业务逻辑服务 --> <bean id="userService" class="com.john.hbatis.service.UserService" />
现在你可以在Spring的服务的Bean中注解@ Transactional。这个注解表明每个方法都是Spring来管理的。如果方法成功处理,那么Spring就会提交事务;如果在处理过程出现了错误,那么事务就会被回滚。当然,Spring将会关心MyBatis的转换过程是否出现Exceptons的DataAccessExceptions的异常栈。
@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) // 测试运行器,基于spring mvc模式 @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(); } }
(3)下面是配置applicationContext.xml的文件:
<beans> <context:annotation-config /> <context:component-scan base-package="com.owen.mybatis" /> <context:property-placeholder location="classpath:application.properties" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource. DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.owen.mybatis.mappers" /> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliases" value="com.owen.mybatis.domain.Student, com.owen.mybatis.domain.Tutor"/> <property name="typeAliasesPackage" value="com.owen.mybatis.domain"/> <property name="typeHandlers" value="com.owen.mybatis.typehandlers.PhoneTypeHandler"/> <property name="typeHandlersPackage" value="com.owen.mybatis.typehandlers"/> <property name="mapperLocations" value="classpath*:com/mybatis3/**/*.xml" /> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource. DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> </beans>
2、transactionTemplate 编程式事务处理
在spring配置文件中写入:
<!-- jdbc事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref local="dataSource" /> </property> </bean> <!--事务模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager"> <ref local="transactionManager" /> </property> <!--ISOLATION_DEFAULT 表示由使用的数据库决定 --> <property name="isolationLevelName" value="ISOLATION_DEFAULT"/> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> <!-- <property name="timeout" value="30"/> --> </bean> <!-- 启动使用注解实现声明式事务管理的支持 <tx:annotation-driven transaction-manager="txManager" /> -->
示例代码:
public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionTemplate transactionTemplate; ...... public boolean transfer(final Long fromId, final Long toId, final double amount) { return (Boolean) transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status) { Object result; try { result = bankDao.transfer(fromId, toId, amount); } catch (Exception e) { status.setRollbackOnly(); result = false; System.out.println("Transfer Error!"); } return result; } }); } }
扩展:Spring中的事务分为物理事务和逻辑事务;
物理事务:就是底层数据库提供的事务支持,如JDBC或JTA提供的事务;
逻辑事务:是Spring管理的事务,不同于物理事务,逻辑事务提供更丰富的控制,而且如果想得到Spring事务管理的好处,必须使用逻辑事务,因此在Spring中如果没特别强调一般就是逻辑事务;
优点:一旦事务创建之后,MyBatis-Spring将会透明的管理事务。在你的DAO或Service类中就不需要额外的代码了。