初步认识事务(二)

在上一篇,我们已经大约了解数据库层面的事务,今天让我们再来熟悉下,spring是怎么管理事务的,以及spring用事务的一些坑,我是大白,不对大神们轻点喷。

spring事务

spring事务分为两种:
1.编程式事务
2.声明式事务
主要先熟悉一下声明式事务。(编程式事务,以后也不会在熟悉了!)

spring事务实现方式

第一步:配置xml

<!– 配置事务管理器 >
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>   
</bean>

<!– 启用事务注解 >
<tx:annotation-driven transaction-manager="transactionManager"/>

第二步:方法上加@Transactional注解

@Transactional
public int save(User user){
	userService.insert(user);
	...
}

springboot实现方式

直接在需要开启事务的方法上添加注解就可以。由于在springboot项目中解@SpringBootApplication在加载容器的时候,已经开启事务管理的功能了,所以,不需要额外添加@EnableTransactionManagement注解。

@Transactional注解中的属性讲解:

  • propagation:指定事务的传播行为(默认值为REQUIRED)。
  • isolation:指定事务的隔离级别,最常用的取值READ_COMMITTED。
  • 默认异常:RuntimeException或者Error。
  • readOnly:指定事务是否为只读,表示这个事务只读取数据但不更新数据。这样可以帮助数据库引擎优化事务.。若真的是一个只读取数据库值的方法,应设置 readOnly=true。
  • timeout:指定强制回滚之前事务可以占用的时间。若超过了timeout指定的时间还为执行,则会抛出异常。

事务的传播行为

事务行为说明
PROPAGATION_REQUIRED如果当前有事务在运行,那么就加入当前事务,否则,就开启一个新的事务
PROPAGATION_SUPPORTS当前如果存在事务,那么就加入当前事务,如果当前没有事务,那么就以非事务的状态运行
PROPAGATION_MANDATORY当前存在事务就加入当前事务,当前没有事务,就抛出异常
PROPAGATION_REQUIRES_NEW创建一个新的事务,假如当前有事务,那么就挂起该当前事务
PROPAGATION_NOT_SUPPORTED以非事务状态运行,如果当前有事务,那么就挂起该当前事务
PROPAGATION_NEVER不使用事务,如果当前有事务,抛出异常
PROPAGATION_NESTED如果当前存在事务,那么在嵌套事务中运行,如果当前没有事务,就开启一个新的事务

看了说明,还有点懵的同学们,栗子来了:
REQUIRED :

@Transactional(propagation = Propagation.REQUIRED)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
	throws Exception;//在新增B数据时,出现错误抛出异常
}

public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
}

上述代码运行的结果是:
C对象新增成功,A、B对象新增失败。因为methodC方法中没有事务,那么C对象新增时没有异常出现,新增成功。执行方法methodAB传播特性为
REQUIRED当前执行没有事务(开启一个新的事务)methodAB发生回滚。A、B新增失败。

还是这个栗子,这时候我们在methodAB也加上注解:

@Transactional(propagation = Propagation.REQUIRED)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
	throws Exception;//在新增B数据时,出现错误抛出异常
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
}

此时的运行结果:
A,B,C都新增失败,因为methodAB在运行时加入到methodC中的事务中(如果当前有事务在运行,那么就加入当前事务),这时A、B、C共处于一个事务中,这时候抛出异常,事务都产生回滚,此时数据新增失败。

SUPPORTS:

@Transactional(propagation = Propagation.SUPPORTS)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
	throws Exception;//在新增B数据时,出现错误抛出异常
}

public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
}

执行结果:
A、C新增成功。B执行失败。因为methodAB运行在methodC中,当前methodC没有声明事务的,所以B在新增时抛出异常,B新增失败,A、C新增成功(如果当前没有事务,那么就以非事务的状态运行)。

MANDATORY:


@Transactional(propagation = Propagation.MANDATORY)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
}

public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
}

执行结果为:
C对象新增成功,A、B对象新增失败。因为当前方法methodC运行的过程中,methodAB的传播属性为MANDATORY(当前没有事务,就抛出异常),所以A、B抛出异常,新增失败。

REQUIRES_NEW:


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
	throw Exception;
}

执行结果:
A、B新增成功,C新增失败,因为methodC事务中出现异常,事务发生回滚,C对象存储失败,A、B因为传播属性为REQUIRES_NEW(假如当前有事务,那么就挂起该当前事务)处于一个新的事务中,所以新增成功。

NOT_SUPPORTED:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
	throws Exception;
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
}

执行结果:
A新增成功,C、B新增失败,因为methodAB方法中出现异常,并且methodAB方法中事务的传播特性是NOT_SUPPORTED(如果当前有事务,那么就挂起该当前事务)所以B新增失败,而methodAB中的异常抛出到methodC中此时C发生回滚,所以A新增成功。

NEVER

@Transactional(propagation = Propagation.NEVER)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
}

执行结果:
A、B、C都新增失败。因为NEVER传播属性(如果当前有事务,抛出异常)此时A、B抛出异常新增失败,C检测到异常,产生回滚,同样新增失败,所以A、B、C都新增失败。

NESTED
场景1:

@Transactional(propagation = Propagation.NESTED)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodC(A a,B b,C c){
	//数据库中新增C对象
	cService.save(c);
	//调用methodAB
	methodAB(a,b);
	throws Exception;
}

运行结果:
A、B、C都新增失败。因为在methodC发生异常时,父事务回滚则子事务也跟着回滚了,所以A、B、C都新增失败。
场景2:

@Transactional(propagation = Propagation.NESTED)
public void methodAB(A a,B b){
	//数据库中新增A对象
	aService.saveA(a);
	//数据库中新增B对象
	bService.saveB(b);
	throws Exception;
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodC(A a,B b,C c,D d){
	//数据库中新增C对象
	cService.save(c);
	try{
		//调用methodAB
		methodAB(a,b);
	}catch{
		log.info("===========打印异常===========")
	}
	dService.save(d);
}

运行结果:
A、B新增失败。C、D新增成功。因为调用方catch了被调方的异常,所以只有子事务回滚了。
NESTED传播特性需要和REQUIRES_NEW、REQUIRED对比着去看。

事务的传播特性就写到这里,接下来记录一下我们在开发中调用事务的时候,那些坑:
1、@Transactional 应用在非 public 修饰的方法上。
2、@Transactional 默认异常为运行时异常(RuntimeException),我们需要指明rollbackFor = Exception.class。
3、同一个类中方法调用,导致@Transactional失效,因为Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
4、异常被catch捕获导致@Transactional失效
好了,今天就写到这里吧,希望同学们在开发的过程中,能注意到声明式事务的那些坑,避免踩坑。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值