`

Spring事务实践

阅读更多

        大家都知晓的Spring的事物是基于动态机制的,支持CGLIB和JDK动态代理两种。如下所示:

一.CGLIB方式

        CGLIB代理无须必须实现接口。

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.annotation.Transactional;

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

@Service("userServiceAOP")
public class UserServiceAOP {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
	
	@Autowired
	private IUserMapper mapper;
	
	@Transactional
	public int txUpdateUsersWhenException() { // 事务性
		return innerMethod();
	}
	
	private int innerMethod() {
		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;
	}
}

        调试发现确实是CglibAop代理,此时事务是生效的。



二.JDK动态代理

package com.bijian.study.service;

public interface UserService {

	public int txUpdateUsersWhenException();
}
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.annotation.Transactional;

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

@Service("userService")
public class UserServiceImpl implements UserService {

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

	@Transactional
	public int txUpdateUsersWhenException() { // 事务性
		return innserMethod();
	}

	private int innserMethod() {
		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;
	}
}

        调试发现确实是JDK动态代理,此时事务是生效的。



三.测试代码

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;
import com.bijian.study.service.UserServiceAOP;

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

        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">

	<!-- 数据库配置文件 -->
	<context:property-placeholder location="classpath:/database.properties" />
	
	<!-- 数据源配置 -->
	<!--本示例采用DBCP连接池,应预先把DBCP的jar包复制到工程的lib目录下。 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		p:driverClassName="${driverClassName}" p:url="${url}" p:username="${user_name}"
		p:password="${password}" />
	
	<!-- sqlSessionFactory对象 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!--dataSource属性指定要用到的连接池 -->
		<property name="dataSource" ref="dataSource" />
		<!--configLocation属性指定mybatis的核心配置文件 -->
		<property name="configLocation" value="classpath:Configuration.xml" />
		<!-- 可以在Configuration.xml或此处配置映射文件,但其中不能有相同id的parameterMap, resultMap或sql等 -->
		<property name="mapperLocations" value="classpath*:com/bijian/study/model/*.xml" />
	</bean>

	<!-- 扫描指定包以获取映射器 -->
	<bean id="mapperConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.bijian.study.dao" />
	</bean>

	<!-- 事务管理器 -->
	<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.UserServiceImpl" />
	
	<bean id="userServiceAOP" class="com.bijian.study.service.UserServiceAOP" />
	
	<bean id="txTemplate" class="org.springframework.transaction.support.TransactionTemplate">
  		<constructor-arg type="org.springframework.transaction.PlatformTransactionManager" ref="txManager" />
  	</bean>
</beans>

        database.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.235.1:3306/hbatis?characterEncoding=utf8
user_name=bijian
password=123456

 

四.将@Transactional放到非外部直接调用的方法上

        将@Transactional放到非外部直接调用的方法上,调试发现无代理,事务失效。

1.修改CGLIB方式的服务方法

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.annotation.Transactional;

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

@Service("userServiceAOP")
public class UserServiceAOP {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
	
	@Autowired
	private IUserMapper mapper;
	
	public int txUpdateUsersWhenException() { // 事务性
		return innerMethod();
	}
	
	@Transactional
	private int innerMethod() {
		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方法不是服务类的入口方法,无CGLIB代理,事务失效。



2.修改JDK动态代理方式的服务方法

package com.bijian.study.service;

public interface UserService {

	public int txUpdateUsersWhenException();
}
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.annotation.Transactional;

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

@Service("userService")
public class UserServiceImpl implements UserService {

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

	public int txUpdateUsersWhenException() { // 事务性
		return innserMethod();
	}

	@Transactional
	private int innserMethod() {
		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方法不是服务类的入口方法,无JDK动态代理,事务失效。


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

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

        测试方法的innserMethod中虽然抛出异常,但事务并没有回滚,测试后数据库中user表数据如下:

mysql> select * from user;
+----+------+------+------------------+
| id | name | age  | address          |
+----+------+------+------------------+
|  1 | NULL |    0 | Before exception |
+----+------+------+------------------+
1 row in set (0.00 sec)
  • 大小: 64.8 KB
  • 大小: 58.7 KB
  • 大小: 59.5 KB
  • 大小: 61.5 KB
分享到:
评论

相关推荐

    分布式事务实践 解决数据一致性

    1-1 导学-分布式事务实践 第2章 事务原则与实现 介绍了事务的四大原则,并通过实例介绍数据库实现事务的方法,以及使用JDBC实现事务的方法。 2-1 事务原则与实现:事务 2-2 事务原则与实现:SQL事务 2-3 事务原则与...

    spring cloud最佳实践项目

    spring cloud最佳实践项目实例,使用了spring cloud全家桶,TCC事务管理,EDA事务最终一致性等技术的下单示例

    深入理解Spring声明式事务:源码分析与应用实践

    此外,Spring事务管理器支持多种类型的事务策略,包括不同的传播行为和隔离级别,允许开发者根据具体业务场景选择最合适的事务管理策略。深入理解Spring声明式事务的工作原理,不仅能帮助开发者更高效地使用Spring...

    Spring事务管理

    Spring提供了两种使用JDBC API的最佳实践,一种是以JdbcTemplate为核心的基于Template的JDBC的使用方式,另一种则是在JdbcTemplate基础之上的构建的基于操作对象的JDBC的使用方式。

    Spring中的四种声明式事务的配置

    本文系统的介绍了Spring中的四种声明式事务的配置。可应用于实践项目中。

    Java架构师必备分布式事务实践视频教程

    基于微服务架构的分布式系统...本课程从本地事务出发,介绍了分布式系统和Spring cloud框架及其使用,以及分布式事务的几种实现模式。课程中还提供了大量的实例,让同学们在实战过程中,掌握分布式事务实现方式与思路。

    Spring源码解析4章150页+Spring3.2.4中文注释源码

    一阶段 1、Spring概述 2、一切从bean开始 3、俯瞰Spring架构设计 4、Spring源码下载 二阶段 1、什么是IOC/DI 2、SpringIOC体系结构 3、源码分析-IOC容器的初始化 ...Spring事务源码解析 需要其他源码请私信我

    Spring攻略PDF版

    Spring专家力作 理论与实践完美结合 问题描述→解决方案→实现方法 第一部分 核心概念  第1章 控制反转和容器   1.1 使用容器管理组件   1.1.1 问题描述   1.1.2 解决方案   1.1.3 实现方法  ...

    spring ioc aop基础理论实践笔记

    1,spring是一个开源的免费的框架(容器)。 2,spring是一个轻量级的,非入侵式的框架。...4,支持事务的处理,对框架整合的支持。 总结:spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。

    精通spring--源代码

    包括忘却的Spring高级话题,Spring最佳实践  对Spring2.5内置的各种XML Schema支持进行了全方位阐述。包括基于XML Schema的权威配置指南  全书理论与实践并重,通过大量的实例帮助读者尽快掌握Spring2,5的各种基本...

    精通Spring(书签)

     Spring 2.5是迄今为止完美的Java EE架构级框架,全面深入、多维度演绎Spring 2.5的各个方面,本书蕴含作者多年Java EE研发实践及经验。  凝聚Java魅力,成就开发专家  看清Java万花的本质,从复杂的表象中寻找...

    Spring攻略中文版PDF

    Spring专家力作 理论与实践完美结合 问题描述→解决方案→实现方法 第一部分 核心概念  第1章 控制反转和容器   1.1 使用容器管理组件   1.1.1 问题描述   1.1.2 解决方案   1.1.3 实现方法  ...

    分布式事务框架Fescar在SpringCloud环境下的应用实践-fescar-demo.zip

    分布式事务框架Fescar在SpringCloud环境下的应用实践-fescar-demo

    分布式事务JTA之实践:Spring+ATOMIKOS

    NULL 博文链接:https://sjsky.iteye.com/blog/1112012

    Spring攻略英文版(附带源码)

    Spring专家力作 理论与实践完美结合 问题描述→解决方案→实现方法 第一部分 核心概念  第1章 控制反转和容器   1.1 使用容器管理组件   1.1.1 问题描述   1.1.2 解决方案   1.1.3 实现方法   1.2...

    Spring教程合集.zip

    Spring教程合集.zip ...在Spring中配置Hibernate事务 Struts+Spring+Hibernate开发实例 Struts+Hibernate+Spring练习 Struts+Spring+Hibernate快速入门 sping mvc 理解AOP,IOC,Spring Spring Framework最佳实践

    javaSpring-经典概念题-试题-中文

    此外,还包括了Spring事务管理和Spring Boot框架的介绍,帮助学习者了解如何在Spring应用程序中实现事务管理和利用Spring Boot简化项目开发。通过这组练习题,学习者可以全面掌握Spring框架的核心知识和常用技术,为...

    spring.net中文手册在线版

    14.5.1.理解Spring.NET声明式事务管理的实现 14.5.2.第一个例子 14.5.3.Transaction特性的设置 14.5.4.通过AutoProxyCreator使用声明式事务 14.5.5.通过TransactionProxyFactoryObject使用声明式事务 14.5.6. 通过...

Global site tag (gtag.js) - Google Analytics