【Spring】Spring提供的事务管理

Spring提供的声明式事务管理:

大多数情况下比编程式事务管理更好用,它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,Spring声明式事务管理建立在AOP基础之上,是一个典型的横切关注点,通过环绕增强来实现,其原理是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完毕之后根据执行情况提交或回滚事务,其模型如下:

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
	try {
		//开启事务
		return joinPoint.proceed();
		//提交事务
	} catch (Throwable e) {
		//回滚事务
		throw e;
	}finally {
		//释放资源
	}
}
如何实现声明式事务管理:

以下面的三个表为例:
有两种书《活着》和《死了》,每种库存5本,每本单价9元
在这里插入图片描述
用户的钱包余额有10元
在这里插入图片描述
交易成功后会增加一条订单记录
在这里插入图片描述

1、添加spring提供的aspects的jar包,这里用“spring-aspects-4.3.10.RELEASE.jar”;
2、在Spring配置文件中添加如下配置:
<?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:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- 配置自动实例化"cn.jingpengchong"包下带特定注解的类放进IOC容器 -->
	<context:component-scan base-package="cn.jingpengchong"></context:component-scan>
	
	<!-- 配置Hikari数据库连接池 -->
	<bean id="hikariDSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" p:username="root" p:password="1234">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/mytest"></property>
	</bean>
	
	<!-- 配置自动实例化JdbcTemplate类放进IOC容器 -->
	<bean class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="hikariDSource"></bean>

	<!-- 配置数据源事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="hikariDSource"></property>
	</bean>
	
	<!-- 开启事务注解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
3、在Service层需要用到事务的方法上添加事务注解:@Transactional
  • ① 一个类含有@Transactional注解修饰的方法,则Spring框架自动为该类创建代理对象,默认使用JDK创建代理对象,可以通过添加<aop:aspectj-autoproxy proxy-target-class=“true”/>使用CGLib创建代理对象,此时需要添加aspectjweaver-x.x.x.jar包。
  • ② 不能在protected、默认或者private的方法上使用@Transactional注解,否则无效。
    在这里插入图片描述
4、编个测试类试一下:

购买5本《活着》,如果没有使用事务的话,执行后《活着》库存就没了,然而验证钱包余额的时候发现余额不够,会抛出一个异常,所以用户的钱包和订单记录不会有任何变化。但是!我们使用事务了,此时,事务捕获到异常后会把之前对book表的操作给回滚了,因此,这个测试代码执行后数据库不会有变化。

package cn.jingpengchong.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.jingpengchong.coupon.service.ICouponService;

public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml");

		ICouponService couponService = application.getBean(ICouponService.class);
		System.out.println(couponService.getClass().getName());
		String userId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
		String bookId = "07f2c23d-5c44-4f42-9afc-27310e96e385";
		int count=5;
		boolean b = couponService.insert(userId, bookId, count);
		System.out.println(b);
		application.close();
	}
}

执行后控制台打印了个错误,而数据库的数据没有变化:
在这里插入图片描述

@Transactional注解属性:
1、rollbackFor和rollbackForClassName:

@Transactional默认不会捕获检查时异常,如果想要捕获检查时异常,需要用给该注解的rollbackFor或rollbackForClassName赋值,指定捕获哪种异常,例如,将自定义异常改为检查时异常后,将insert方法的下层方法的自定义检查时异常抛出(不能try-catch!!!),当抛给insert方法后,如果想要事务将该异常捕获然后回滚操作,注解需要写成下面的形式:(MoneyException是自定义的检查时异常)

@Transactional(rollbackFor = MoneyException.class)

或:

@Transactional(rollbackForClassName = "cn.jingpengchong.exception.MoneyException")
2、noRollbackFor和noRollbackForClassName:

@Transactional默认会捕获所有的运行时异常,然后进行回滚操作。但是当出现某些异常时并不需要回滚。此时就需要用到noRollbackFor 或 noRollbackForClassName 指定对哪些异常不回滚事务,例如出现类型转换异常时不执行回滚操作就需要这样写:

@Transactional(noRollbackFor = ClassCastException.class)

或:

@Transactional(noRollbackForClassName = "java.lang.ClassCastException")
3、readOnly:

将该属性值设置为true后,在该事务内只能进行读取操作,而不能进行数据库数据的修改,因此可以用该属性来保障数据库的数据安全,使用格式如下:

@Transactional(readOnly = true)
4、timeout:

该属性用来设置事务的最大存活时间(单位:秒),如果超过这个时间事务还没结束,那么将会抛出事务超时异常并执行回滚操作,例如设置事务的最大存活时间为5秒:

@Transactional(timeout = 5)
5、propagation:

该属性用来指定事务传播行为,一个事务方法被另一个事务方法调用时,必须指定事务应该如何传播,例如:方法可能继承在现有事务中运行,也可能开启一个新事物,并在自己的事务中运行。Spring定义了如下7种事务传播行为:
REQUIRED:默认值,如果a方法调用的b方法有事务,则a方法就在这个事务内运行;如果a方法调用的b方法没有事务,则生成一个新事物供a方法使用。
SUPPORTS:如果a方法调用的b方法有事务,则a方法就在这个事务内运行;如果a方法调用的b方法没有事务,则a方法以非事务的方式运行。
MANDATORY(mandatory [ˈmændətɔːri] adj.强制的):如果a方法调用的b方法有事务,则a方法就在这个事务内运行;如果a方法调用的b方法没有事务,则将抛出异常;
REQUIRES_NEW:当前方法启动新事务,并在它自己的事务内运行,如果有事务在运行,则把当前事务挂起,直到新的事务提交或者回滚才恢复执行;
NOT_SUPPORTED:当前的方法不应该运行在事务中,如果有运行的事务,则将它挂起;
NEVER:当前方法不应该运行在事务中,否则将抛出异常;
NESTED(nest [nest] v.嵌套):如果有事务在运行,当前的方法在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行,此时等价于REQUIRED。注意:对于NESTED内层事务而言,内层事务独立于外层事务,可以独立递交或者回滚,如果内层事务抛出的是运行异常,外层事务进行回滚,内层事务也会进行回滚。
例如要在事务内新开一个事务,应该这样写:

@Transactional(propagation = Propagation.REQUIRES_NEW)
除了以上注解方式使用声明式事务,还可用以下xml配置的方式使用:

可在配置文件中做如下配置,此时类中的@Transactional注解就不再使用:

<?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:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<!-- 配置自动实例化"cn.jingpengchong"包下带特定注解的类放进IOC容器 -->
	<context:component-scan base-package="cn.jingpengchong"></context:component-scan>
	
	<!-- 配置Hikari数据库连接池 -->
	<bean id="hikariDSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" p:username="root" p:password="1234">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/mytest"></property>
	</bean>
	
	<!-- 配置自动实例化JdbcTemplate类放进IOC容器 -->
	<bean class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="hikariDSource"></bean>

	<!-- 配置数据源事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="hikariDSource"></property>
	</bean>
	
	<!-- 配置事务增强 -->
	<tx:advice id="txAdvice">
		<tx:attributes>
			<tx:method name="update" propagation="REQUIRED" isolation="DEFAULT"/>
			<tx:method name="insert" propagation="REQUIRED" isolation="DEFAULT"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置AOP -->
	<aop:config>
		<aop:pointcut expression="execution(* cn.jingpengchong.coupon.service.CouponService.*(..))" id="pc1"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
	</aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值