`

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

分享到:
评论

相关推荐

    myBatis系列之五:与Spring3集成

    本篇文章将深入探讨MyBatis系列中的第五个主题——如何将MyBatis与Spring3进行集成,以实现更高效、更灵活的Java企业级应用开发。 MyBatis是一个轻量级的SQL映射框架,它允许开发者将SQL语句直接写在XML配置文件...

    myBatis系列之一:搭建开发环境

    然后,配置MyBatis的核心文件`mybatis-config.xml`,定义数据源、事务管理器以及日志设置。例如: ```xml &lt;property name="url" value="jdbc:mysql://localhost:3306/mydatabase?useSSL=false&amp;...

    mybatis系列一:开发环境搭建

    在本篇中,我们将深入探讨"Mybatis系列一:开发环境搭建"的相关知识,这将包括如何设置一个基于Maven的Mybatis开发环境,以及如何理解并使用Mybatis的核心概念。首先,我们需要明白Mybatis是一个优秀的持久层框架,...

    mybatis-3.4.5

    这个版本是MyBatis系列的一个稳定版本,包含了对先前版本的改进和新特性。 在MyBatis 3.4.5中,开发者可以期待以下关键特性: 1. **动态SQL**:MyBatis的强项之一是其强大的动态SQL支持。你可以直接在XML映射文件...

    Mybatis系列学习源码

    本资源是"Mybatis系列学习源码",适合初学者和有一定经验的开发者深入理解Mybatis的工作原理和实践应用。下面将详细探讨Mybatis的核心概念和关键功能。 1. **SqlSession与Executor** - **SqlSession**:它是...

    Mybatis系列教程Mybatis基本应用共9页.pdf

    【标题】:Mybatis系列教程Mybatis基本应用共9页.pdf 【描述】:这份教程是关于Mybatis基本应用的详细讲解,包含了9页丰富的内容,旨在帮助学习者深入理解和掌握Mybatis这一强大的持久层框架。 【标签】:Mybatis...

    Spring3MVC+MyBatis+ExtJs3整合开发系列之四:角色管理模块

    MyBatis需要配置数据源、事务管理器以及Mapper扫描器。Ext Js 3则需要配置Ajax proxy来与Spring MVC的RESTful API进行通信。 在角色管理模块的实现中,通常会包含以下功能: 1. 角色列表展示:使用GridPanel展示...

    spring3.1MVC+mybatis3.1框架集成及事务,分页使用

    在本文中,我们将深入探讨如何将Spring MVC 3.1与MyBatis 3.1框架集成,并讨论其中涉及的关键技术,如事务管理、分页和JSON数据交换。此外,我们还将简要提及开发环境中使用的工具,如Eclipse 4.3、Maven 3.0.5和...

    Mybatis系列教程Mybatis插件共8页.pdf.z

    6. **插件与事务管理**:讨论插件如何与Mybatis的事务管理机制协同工作,确保数据的一致性。 7. **性能优化**:通过实例展示如何使用插件进行性能优化,比如减少不必要的数据库查询,提高SQL执行效率。 8. **最佳...

    mybatis demo

    - 配置文件(mybatis-config.xml):这是MyBatis的核心配置,包含了数据源、事务管理器和Mappers的定义。 - 映射文件(UserMapper.xml):展示了如何定义SQL语句,如SELECT、INSERT、UPDATE和DELETE,并将它们映射...

    Mybatis notes系列博客配套资源

    - XML 配置文件:定义数据源、事务管理器、映射文件等,是 Mybatis 的全局配置。 - 映射文件(Mapper XML):包含 SQL 语句和结果映射,可以使用参数占位符和动态 SQL。 - 注解配置:在 Java 类上直接使用注解...

    Mybatis系列教程Mybatis源码剖析共15页.pd

    9. **事务管理**:Mybatis支持手动和自动事务管理,通过SqlSession的beginTransaction、commit和rollback方法控制。 10. **插件机制**:Mybatis允许自定义插件拦截Executor、StatementHandler、ParameterHandler和...

    b站up遇见狂神说 系列视频Mybatis 项目源码

    08`、`mybatis-02`、`mybatis-09`、`mybatis-01`、`mybatis-07`:这些文件可能代表了不同章节的源代码或者笔记,按照顺序可能是Mybatis的基础到进阶的各个主题,例如配置、SQL映射文件、动态SQL、事务管理等。...

    走进MyBatis世界之基础入门(二)

    MyBatis是一款优秀的ORM...本章主要内容:MyBatis与Spring如何集成,声明式事务管理,MapperScannerConfigurer本次课程,在YY上进行,YY频道:71042615课程回放:http://bbs.51cto.com/open/do/course/cid/65系列课程:...

    开源框架面试题系列:Spring+SpringMVC+MyBatis

    5. **Spring与MyBatis整合**:解释如何将MyBatis集成到Spring项目中,利用Spring的事务管理。 最后,SpringMVC是Spring框架的Web模块,用于构建MVC架构的Web应用。SpringMVC面试专题可能包含: 1. **请求处理流程*...

    Mybatis系列教程Mybatis注解开发共9页.pdf

    【标题】"Mybatis系列教程Mybatis注解开发共9页.pdf" 提供的是一个关于Mybatis框架注解开发的教程,重点在于讲解如何在Mybatis中使用注解进行数据库操作。Mybatis是一个轻量级的Java持久层框架,它允许开发者通过...

    mybatis笔记.zip

    6. **MyBatis与Spring整合**:在SSM架构中,MyBatis与Spring的整合可以实现事务管理、依赖注入等功能,简化项目开发。 7. **Mapper接口与XML映射文件**:Mapper接口定义业务方法,XML映射文件中定义SQL语句,通过...

    Mybatis系列教程Mybatis架构原理共4页.pdf

    【标题】:Mybatis系列教程Mybatis架构原理共4页.pdf 【描述】:这个压缩文件包含了一份关于Mybatis架构原理的系列教程,总计四页的内容。Mybatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。...

Global site tag (gtag.js) - Google Analytics