β

Spring 声明式事务

漠然 Blog 254 阅读

一、Spring 事务简介

事务管理对于企业应用而言至关重要。它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,

也不至于破坏后台数据的完整性。传统的事务控制需要我们手动一行一行的书写代码,在每一个程序中指定

从哪里开启事务,从哪里结束事物;大量的事务操作代码会使程序显得臃肿不堪,也不便于维护;Spring借助

与其强大的AOP,适用面向切面的方式提供了声明式的痛的的事务管理;从此我们不必在每段代码前书写枯燥

而重复的事务控制代码,只需要使用AOP 切面的切点表达式 对符合要求的代码块进行统一的事务管理,从繁琐

而枯燥的事务控制中解放出来

二、Spring 注解方式配置事务(Hello World)

1、要使用Spring 的声明式事务,需要在Spring配置文件中启用tx 标签

Spring-tx

2、在Spring 配置文件中开启事务支持(这里的事务管理器只适用于JDBC)

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
	<!-- 包自动扫描 -->
	<context:component-scan base-package="me.mritd"></context:component-scan>
	<!-- 加载配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<!-- c3p0 数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${driverClass}"></property>
		<property name="jdbcUrl" value="${jdbcUrl}"></property>
		<property name="user" value="${user}"></property>
		<property name="password" value="${password}"></property>
		
		<property name="maxPoolSize" value="${maxPoolSize}"></property>
		<property name="minPoolSize" value="${minPoolSize}"></property>
		<property name="initialPoolSize" value="${initialPoolSize}"></property>
	</bean>
	<!-- 配置 JDBCTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 将 JDBC 事务管理器配置为普通的 bean -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 指定事务管理器 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

3、测试声明式事务

若想Spring事务管理来管理事务,可采用xml配置方式和注解方式,以下为注解方式

UserDao

package me.mritd.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
	
	@Autowired
	JdbcTemplate jdbcTemplate;
	
	public void testUpdate(){
		jdbcTemplate.update("INSERT INTO USER VALUES(?,?,?,?)", 1,"ZHANGSAN","N",102);
	}
	
	public void test1Update(){
		System.out.println(1/0);    //抛出异常
		jdbcTemplate.update("DELETE FROM USER WHERE ID = ?", 1);
	}
}

UserController

package me.mritd.mvc.controller;

import me.mritd.dao.UserDao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;

@Controller
public class UserController {
	@Autowired
	private UserDao userDao;
	@Transactional //启用事务管理,一下方法将在一个事务中执行
	public void test(){
		userDao.testUpdate();
		userDao.test1Update();
	}
}

测试结果:第一个方法将插入数据,第二个方法由于打印了1/0;所以会产生异常,此时方法一的INSET 也被回滚

三、Spring 事物的传播性

1、当某一个带有事物的方法,调用了其他带有事物的方法;这是其他方法中的事务如何处理便可以理解为事务

的传播行为,事务的传播行为可以由传播属性指定. Spring 定义了 7  种类传播行为.Spring默认第一种

Spring-tx-chaunbo

2、事务传播属性可以在 @Transactional 注解的 propagation 属性中定义

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void test(){
	userDao.testUpdate();
	userDao.test1Update();
}

此时若 testUpdate 方法中有单独事物,而test1Update中也有单独事物;test方法又被事务进行了管理,结果

就会类似下图

Spring-tx-chaunbo1

test中 Tx1 事务开始,执行到 testUpdate方法后,由于使用了 propagation=Propagation.REQUIRES_NEW 事务传播属性;

那么testUpdate方法会单独在开启一个事务Tx2;并把外面的大事务Tx1挂起;当Tx2 commit后,继续Tx1事务;testUpdate

方法类似,所造成的结果就是 若test1Update方法出现异常,那么testUpdate方法的操作不会被回滚。

四、Spring 声明式事务控制 事物级别

在声明式事务中,Spring可以控制事务的隔离级别;Spring支持的事物的隔离级别如下

Spring-tx-geli

事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持.

Oracle 支持的 2 种事务隔离级别:READ_COMMITED , SERIALIZABLEMysql 支持

4种事务隔离级别.

在使用注解方式时(@Transactional),可通过其isolation 属性来设置隔离级别

@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED)
public void test(){
	userDao.testUpdate();
	userDao.test1Update();
}

同时可通过 readOnly 属性设置是否为只读事务;只读事务有利于数据库引擎对其进行优化,提高程序执行效率

@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,
		readOnly=false)
public void test(){
	userDao.testUpdate();
	userDao.test1Update();
}

五、Spring 声明式事务的回滚属性

Spring 声明式事务允许对事物的回滚加以更加细微的控制;包括控制程序何种异常下回滚事务,

事务超时的强制回滚;在使用注解配置中分别使用 rollbackFor、noRollbackFor 和timeout 设置,

rollbackFor、noRollbackFor 指定那种异常时一定回滚 或那种异常不必回滚;timeout 指定当前

事务被开启后最长可以执行的时间(以秒为单位);一旦超过了这个时间,那么这个事务将被强制回滚

@Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,
		readOnly=false,timeout=10,rollbackFor={IndexOutOfBoundsException.class})
public void test(){
	userDao.testUpdate();
	userDao.test1Update();
}

六、通过XML方式配置Spring 声明式事务

配置样例

<!-- 事务管理器 -->
<bean id="txManager" lazy-init="true"
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource" />
</bean>

<!-- 启用声明式事务 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
	<tx:attributes>
		<tx:method name="select*" read-only="true" propagation="SUPPORTS" />
		<tx:method name="find*" read-only="true" propagation="SUPPORTS" />
		<tx:method name="query*" read-only="true" propagation="SUPPORTS" />
		<tx:method name="*" rollback-for="Throwable" />
	</tx:attributes>
</tx:advice>

<!-- 事务切面 -->
<aop:config>
	<aop:pointcut id="paymentServiceOperations"
		expression="execution(* com.omgd.gdpayments.service.*Service.*(..))" />
	<aop:advisor advice-ref="txAdvice" pointcut-ref="paymentServiceOperations" />
</aop:config>
作者:漠然 Blog
十字路口,繁华街头......
原文地址:Spring 声明式事务, 感谢原作者分享。

发表评论