文档章节

Spring的事务管理

丶KKdo
 丶KKdo
发布于 2016/09/26 10:25
字数 1508
阅读 34
收藏 3

一.单一数据源的事务管理

事务的管理有几种方式, 使用注解, 声明式配置等等.

首先,看一下单一数据源的事务配置:

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

	<!-- 扫描注解Bean -->
	<context:component-scan base-package="com.tx">
	</context:component-scan>

	<tx:annotation-driven proxy-target-class="true"
		transaction-manager="txManager" />

	<context:property-placeholder location="classpath:jdbc.properties" />

	<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean>



	<bean id="transactionInterceptor"
		class="org.springframework.transaction.interceptor.TransactionInterceptor">
		<property name="transactionManager" ref="txManager" />
		<property name="transactionAttributes">
			<props>
				<prop key="save*">PROPAGATION_REQUIRED</prop>
				<prop key="insert*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="remove*">PROPAGATION_REQUIRED</prop>
				<prop key="delete*">PROPAGATION_REQUIRED</prop>
				<prop key="clear*">PROPAGATION_REQUIRED</prop>
				<prop key="restore*">PROPAGATION_REQUIRED</prop>
				<prop key="replace*">PROPAGATION_REQUIRED</prop>
				<prop key="process*">PROPAGATION_REQUIRED</prop>
				<prop key="execute*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>
	<!-- 设置代理类 -->
	<bean id="bdf.transaction.beanNameAutoProxyCreator"
		class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="proxyTargetClass" value="true"></property>
		<property name="beanNames" value="*PR,*BO,*Service,*Dao" />
		<property name="interceptorNames">
			<list>
				<value>transactionInterceptor</value>
			</list>
		</property>
	</bean>
	<!-- 设置事务管理器 -->
	<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
<!-- 
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		基本属性 url、user、password
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		配置初始化大小、最小、最大
		<property name="initialSize" value="1" />
		<property name="minIdle" value="1" />
		<property name="maxActive" value="20" />
		配置获取连接等待超时的时间
		<property name="maxWait" value="60000" />
		配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		配置一个连接在池中最小生存的时间,单位是毫秒
		<property name="minEvictableIdleTimeMillis" value="300000" />
		<property name="validationQuery" value="SELECT * from user" />
		<property name="testWhileIdle" value="true" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		打开PSCache,并且指定每个连接上PSCache的大小
		<property name="poolPreparedStatements" value="true" />
		<property name="maxPoolPreparedStatementPerConnectionSize"
			value="20" />
		配置监控统计拦截的filters
		<property name="filters" value="stat" />
	</bean> -->
	
	<!-- 配置数据源,c3p0连接池 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close">
		<property name="driverClass" value="${jdbc.driver}" />
		<property name="jdbcUrl" value="${jdbc.url}" />
		<property name="user" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>
</beans>  

其中transactionInterceptor中配置的拦截方法就是需要事务管理的, 满足命名条件的方法都会被事务管理所拦截.

测试:

 

/* Dao代码 */
public class JdbcTestDao {

	@Resource(name="jdbctemplate")
	JdbcTemplate jdbcTemplate;
	
	/**
	 * 在配置文件中使用了声明式的事务管理,<prop key="execute*">PROPAGATION_REQUIRED</prop>
	 * 所以这个事务是会被拦截的事务
	 */
	public void execute(){
		jdbcTemplate.update("UPDATE user set username = 'fff1' where id=4");
		jdbcTemplate.update("UPDATE user1 set username = 'eee1' where id=4");
		int i = 1/0;
	}
	
	/**
	 * 这个虽然不是在配置文件中需要被拦截的事务,但是有注解声明
	 * 所以这个事务是会被拦截的事务
	 */
	@Transactional
	public void notExtcuteWithTx(){
		jdbcTemplate.update("UPDATE user set username = 'ggg' where id=4");
		jdbcTemplate.update("UPDATE user1 set username = 'hhh' where id=4");
		int i = 1/0;
	}
	
	/**
	 * 不会被拦截的事务
	 */
	public void notExtcute(){
		jdbcTemplate.update("UPDATE user set username = 'iii' where id=4");
		jdbcTemplate.update("UPDATE user1 set username = 'jjjj' where id=4");
		int i = 1/0;
	}
}
/* jUnit代码 */
public class JdbcTestDaoTest {
	ApplicationContext context;
	
	@Before
	public void before() {
		context = new ClassPathXmlApplicationContext("spring-jdbc.xml");
	}

	@Test
	public void execute() {
		JdbcTestDao dao = context.getBean(JdbcTestDao.class); 
		dao.execute();
	}
	@Test
	public void notExtcuteWithTx() {
		JdbcTestDao dao = context.getBean(JdbcTestDao.class); 
		dao.notExtcuteWithTx();
	}
	@Test
	public void notExtcute() {
		JdbcTestDao dao = context.getBean(JdbcTestDao.class); 
		dao.notExtcute();
	}
}

分别单元测试三个方法, 可以得到正确的结果, 当抛出异常的时候, 事务都会回滚.

 

 

二.多数据源的事务管理

    在spring中, 提供了很多事务管理的接口, DataSourceTransactionManager, JpaTransactionManager,  JtaTransactionManager等等. 其中JtaTransactionManager是专门用来管理多数据源, 提供了对分布式事务的支持.

<!-- JTA分布式事务配置 -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="  
http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
http://www.springframework.org/schema/context      http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

	<!-- 扫描注解Bean -->
	<context:component-scan base-package="com.tx">
	</context:component-scan>
	<tx:annotation-driven proxy-target-class="true"
		transaction-manager="transactionManager" />
	<bean id="jdbctemplate1" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource1" />
	</bean>

	<bean id="jdbctemplate2" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource2" />
	</bean>

	<bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean"
		init-method="init" destroy-method="close">
		<property name="uniqueResourceName" value="mysql1" />
		<property name="xaDataSourceClassName"
			value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
		<property name="xaProperties">
			<props>
				<prop key="url">jdbc:mysql://localhost:3306/chatroom?useUnicode=true&amp;characterEncoding=utf8
				</prop>
				<prop key="user">root</prop>
				<prop key="password">123456</prop>
				<!-- <prop key="initialSize">1</prop> <prop key="minIdle">1</prop> <prop 
					key="maxActive">20</prop> -->
			</props>
		</property>
		<property name="minPoolSize" value="10" />
		<property name="maxPoolSize" value="100" />
		<property name="borrowConnectionTimeout" value="30" />
		<property name="testQuery" value="select 1" />
		<property name="maintenanceInterval" value="60" />
	</bean>

	<bean id="dataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean"
		init-method="init" destroy-method="close">
		<property name="uniqueResourceName" value="mysql2" />
		<property name="xaDataSourceClassName"
			value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
		<property name="xaProperties">
			<props>
				<prop key="url">jdbc:mysql://localhost:3306/cms?useUnicode=true&amp;characterEncoding=utf8
				</prop>
				<prop key="user">root</prop>
				<prop key="password">123456</prop>
				<!-- <prop key="initialSize">1</prop> <prop key="minIdle">1</prop> <prop 
					key="maxActive">20</prop> -->
			</props>
		</property>
		<property name="minPoolSize" value="10" />
		<property name="maxPoolSize" value="100" />
		<property name="borrowConnectionTimeout" value="30" />
		<property name="testQuery" value="select 1" />
		<property name="maintenanceInterval" value="60" />
	</bean>

	<!-- 设置事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.transaction.jta.JtaTransactionManager">
		<property name="transactionManager">
			<bean class="com.atomikos.icatch.jta.UserTransactionManager"
				init-method="init" destroy-method="close">
				<property name="forceShutdown" value="true" />
			</bean>
		</property>
		<property name="userTransaction">
			<bean class="com.atomikos.icatch.jta.UserTransactionImp">
				<property name="transactionTimeout" value="300" />
			</bean>
		</property>
	</bean>


	<bean id="transactionInterceptor"
		class="org.springframework.transaction.interceptor.TransactionInterceptor">
		<property name="transactionManager" ref="transactionManager" />
		<property name="transactionAttributes">
			<props>
				<prop key="save*">PROPAGATION_REQUIRED</prop>
				<prop key="insert*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="remove*">PROPAGATION_REQUIRED</prop>
				<prop key="delete*">PROPAGATION_REQUIRED</prop>
				<prop key="clear*">PROPAGATION_REQUIRED</prop>
				<prop key="restore*">PROPAGATION_REQUIRED</prop>
				<prop key="replace*">PROPAGATION_REQUIRED</prop>
				<prop key="process*">PROPAGATION_REQUIRED</prop>
				<prop key="execute*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>
	<!-- 设置代理类 -->
	<bean id="transactionBeanNameAutoProxyCreator"
		class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="proxyTargetClass" value="true"></property>
		<property name="beanNames" value="*PR,*BO,*Service,*Dao" />
		<property name="interceptorNames">
			<list>
				<value>transactionInterceptor</value>
			</list>
		</property>
	</bean>
</beans>  

    这里,使用的都是mysql, 只是数据库不同, 也可以使用多种数据库,可替换为oracle等.

    测试:

    

/* Dao代码 */
@Component
public class JtaTestDao implements ApplicationContextAware ,InitializingBean{
	private ApplicationContext context;
	
	public void execute() {
		JdbcTemplate chatroom = context.getBean("jdbctemplate1",
				JdbcTemplate.class);
		JdbcTemplate cms = context.getBean("jdbctemplate2",
				JdbcTemplate.class);
		chatroom.update("UPDATE user set username = 'lzxfgw' where id=4");
		cms.update("INSERT INTO c_user (id, login_name, password) values (100, 'fgw', '123')");

	}
	
	//@PostConstruct
    //实现ApplicationContextAware接口, 可以在初始化的时候注入applicationContext
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.context = applicationContext;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
	}
}
/* jUnit代码 */
public class JtaTestDaoTest {
	ApplicationContext context;

	@Test
	public void test() {
		JtaTestDao t = context.getBean(JtaTestDao.class);
		t.execute();
	}

	@Before
	public void before() {
		context = new ClassPathXmlApplicationContext("spring-jta.xml");
	}
}

    运行jUnit第一次是没有问题的, 修改一下dao代码中的username再次运行,会抛出主键重复的异常, 可以看到数据库中的第一条update语句的username也没有改变, 说明事务在抛出异常之后正常回滚了.

© 著作权归作者所有

丶KKdo
粉丝 1
博文 24
码字总数 13683
作品 0
成都
程序员
私信 提问
MyBatis在Spring中的事务管理

项目中经常遇到MyBatis与Spring的组合开发,并且相应的事务管理交给Spring。今天我这里记录一下Spring中Mybatis的事务管理。 先看代码: spring-context.xml db.properties db.sql UserDAO....

linxyz
2018/07/24
126
0
Spring AOP 日志拦截器的事务管理

如果要在方法执行前或后或抛出异常后加上一个自己的拦截器,或者一个环绕拦截器,在拦截器中执行一些操作,比如执行一些数据库操作,记录一些信 息,这些操作通过调用一个服务类的方法来执行...

哲别0
2018/05/18
527
0
Spring的事务管理实现原理初探

这里主要是通过分析部分源码进行剖析Spring事务管理的实现原理。 再分析源码前,现从理论上大概分析哈: 纯JDBC操作数据库的基本步骤: 1. 获取连接 Connection conn = DriverManager.getCon...

rockypeng
2014/01/19
0
0
Spring编程式和声明式事务实例讲解

Java面试通关手册(Java学习指南):https://github.com/Snailclimb/JavaGuide 历史回顾: 可能是最漂亮的Spring事务管理详解 Spring事务管理 Spring支持两种方式的事务管理: 编程式事务管理...

snailclimb
2018/05/23
0
0
Spring事务管理机制的实现原理-动态代理 .

分析一下Spring事务管理机制的实现原理。由于Spring内置AOP默认使用动态代理模式实现,我们就先来分析一下动态代理模式的实现方 法。动态代理模式的核心就在于代码中不出现与具体应用层相关联...

swk998741
2014/03/17
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

射频特征阻抗

Characteris Impendance(特性阻抗,也称为‘特征阻抗’)是我们经常看到并使用自己的术语之一,但非常模糊且难以解释。以下是来自几个不同来源的Characteris Impendance(特性阻抗)的一些定义。...

demyar
18分钟前
2
0
Spring Boot Tomcat配置项

参数配置容器 server.xx开头的是所有servlet容器通用的配置,server.tomcat.xx开头的是tomcat特有的参数,其它类似。 所有参数绑定配置类:org.springframework.boot.autoconfigure.web.Serv...

xiaomin0322
22分钟前
4
0
微课程 | 第二课《Docker 安装方式介绍》

微课程 | 第二课《Docker 安装方式介绍》视频 https://v.youku.com/v_show/id_XNDQ0NDUzNjkxNg==.html?spm=a2hzp.8244740.0.0 上一期我们介绍了 DBLE 的基本概况,下面进入我们的正题,正题基...

爱可生
22分钟前
4
0
GUAVA--基础工具(Preconditions)

1、前置条件 俗话说丑话讲在前面,在做某些事情的时候是需要做一些前置条件的。假如需要修改一条数据的话,当参数传进来,我们要先查询这条数据是否存在。这时候就需要一个if了,如果参数还需...

MrYuZixian
36分钟前
7
0
PythonSpot 中文系列教程 · 翻译完成

原文:PythonSpot Python Tutorials 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。 在线阅读 ApacheCN 学习资源 目录 PythonSpot 中文系列教...

ApacheCN_飞龙
44分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部