myBatis系列之七:事务管理

一.myBatis单独使用时,使用SqlSession来处理事务

        在前面的MyBatisStudy02工程中,新增MyBatisTxTest.java的测试类。

package com.bijian.study.test;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bijian.study.dao.IUserMapper;
import com.bijian.study.model.User;

public class MyBatisTxTest {

	private static final Logger log = LoggerFactory.getLogger(MyBatisTxTest.class);
	
	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(1, "Test transaction");
			user.setName("bijian2");
			user.setAge(18);
			int affectedCount = mapper.updateUser(user); // 因后面的异常而未执行commit语句
			log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId());
			User user2 = new User(1, "Test transaction continuously");
			user2.setName("bijian3");
			user2.setAge(28);
			int affectedCount2 = mapper.updateUser(user2); // 因后面的异常而未执行commit语句
			log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user2.getId());
			int i = 2 / 0; // 触发运行时异常
			session.commit(); // 提交会话,即事务提交
		} finally {
			session.close(); // 关闭会话,释放资源
		}
	}
}

        测试前数据库表user表的数据如下:

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.00 sec)

        运行上面的单元测试,抛出java.lang.ArithmeticException:/ by zero异常。控制台输出如下内容:

21:29:59.797 [main] INFO  com.bijian.study.test.MyBatisTxTest - 1 new record was inserted successfully whose id: 1
21:29:59.799 [main] INFO  com.bijian.study.test.MyBatisTxTest - 1 new record was inserted successfully whose id: 1

        此时我们再查看数据库表user表的数据如下:

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.01 sec)

 

二.和Spring集成后,使用Spring的事务管理

1.@Transactional方式

        将工程MyBatisStudy03工程/WEB-INF/下的applicationContext.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.bijian.study.service.UserService" />

        服务类:

package com.bijian.study.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import com.bijian.study.dao.IUserMapper;
import com.bijian.study.model.User;

@Service("userService")
public class UserService {

	private static final Logger log = LoggerFactory.getLogger(UserService.class);
	
	@Autowired
	private IUserMapper mapper;

	public int batchUpdateUsersWhenException() { // 非事务性
		User user = new User(1, "Before exception");
		int affectedCount = mapper.updateUser(user); // 执行成功
		log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId());
		User user2 = new User(1, "After exception");
		int i = 1 / 0; // 抛出运行时异常
		int affectedCount2 = mapper.updateUser(user2); // 未执行
		log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user.getId());
		if (affectedCount == 1 && affectedCount2 == 1) {
			return 1;
		}
		return 0;
	}

	@Transactional
	public int txUpdateUsersWhenException() { // 事务性
		User user = new User(1, "Before exception");
		int affectedCount = mapper.updateUser(user); // 因后面的异常而回滚
		log.info("{} new record was inserted successfully whose id: {}", affectedCount, user.getId());
		User user2 = new User(1, "After exception");
		int i = 1 / 0; // 抛出运行时异常,事务回滚
		int affectedCount2 = mapper.updateUser(user2); // 未执行
		log.info("{} new record was inserted successfully whose id: {}", affectedCount2, user.getId());
		if (affectedCount == 1 && affectedCount2 == 1) {
			return 1;
		}
		return 0;
	}
}

        SpringIntegrateTxTest测试类如下:

package com.bijian.study.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.bijian.study.service.UserService;

public class SpringIntegrationTest {
	
    private static ApplicationContext ctx;
    
    static {
    	ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    
    @Test  
    public void updateUsersExceptionTest() {
    	
    	UserService userService = (UserService) ctx.getBean("userService");
        userService.batchUpdateUsersWhenException();
    }  
  
    @Test  
    public void txUpdateUsersExceptionTest() {
    	
    	UserService userService = (UserService) ctx.getBean("userService");
        userService.txUpdateUsersWhenException();  
    }
}

        运行单元测试updateUsersExceptionTest前数据库表user中的数据如下:

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.00 sec)

    运行单元测试updateUsersExceptionTest后数据库表user中的数据如下:

mysql> select * from user;
+----+------+------+------------------+
| id | name | age  | address          |
+----+------+------+------------------+
|  1 | NULL |    0 | Before exception |
+----+------+------+------------------+
1 row in set (0.00 sec)

    恢复数据库表user中的数据如下:

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.00 sec)

    运行单元测试txUpdateUsersExceptionTest后数据库表user中的数据如下:

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.00 sec)

        从上面的单元测试结果不难发现txUpdateUsersWhenException方法上面因注解了@Transactional事务生效了。

 

2.TransactionTemplate方式

        在上面的application.xml中添加:

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

        在UserService类加上如下内容:

@Autowired(required = false)
private TransactionTemplate txTemplate;

public int txUpdateUsersWhenExceptionViaTxTemplate() {
	int retVal = txTemplate.execute(new TransactionCallback<Integer>() {

		@Override
		public Integer doInTransaction(TransactionStatus status) { // 事务操作
			User user = new User(1, "Before exception");
			int affectedCount = mapper.updateUser(user); // 因后面的异常而回滚
			User user2 = new User(1, "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 userService = (UserService) ctx.getBean("userService");
	userService.txUpdateUsersWhenExceptionViaTxTemplate();
}

        运行单元测试updateUsersWhenExceptionViaTxTemplateTest前数据库表user中的数据如下:

 

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.00 sec)

        运行单元测试updateUsersWhenExceptionViaTxTemplateTest后数据库表user中的数据如下,没有发生变化:

mysql> select * from user;
+----+--------+------+-------------------+
| id | name   | age  | address           |
+----+--------+------+-------------------+
|  1 | bijian |  120 | hangzhou,westlake |
+----+--------+------+-------------------+
1 row in set (0.00 sec)

 

        说明txUpdateUsersWhenExceptionViaTxTemplate方法因抛出运行时异常事务发生了回滚。

 

PS:

1.在有@Transactional注解的方法中,不可catch异常后不抛出

        在有@Transactional注解的方法中,不可catch异常后不抛出,如不抛出,外围框架捕获不到异常,认为执行正确而提交。

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

 

2.The prefix "tx" for element "tx:annotation-driven " is not bound

        配置spring是碰到tx:annotation-driven is not bound 的问题,这个错误的原因很简单是在定义申明AOP的时候没有加载schema。修改applicationContext.xml配置文件头如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
                     http://www.springframework.org/schema/beans/spring-beans.xsd 
                     http://www.springframework.org/schema/tx 
                     http://www.springframework.org/schema/tx/spring-tx.xsd 
                     http://www.springframework.org/schema/aop 
                     http://www.springframework.org/schema/aop/spring-aop.xsd
                     http://www.springframework.org/schema/context 
                     http://www.springframework.org/schema/context/spring-context.xsd">

 

3.运行时抛出java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor异常

        原因是缺少包aopalliance-1.0.jar。

 

参考文章:http://czj4451.iteye.com/blog/2037759

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值