SpringBoot中的事务处理问题

 更新时间:2024年01月12日 10:18:40   作者:龙域、白泽  
这篇文章主要介绍了SpringBoot中的事务处理问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

介绍

在实际的业务场景中,不光数据库要实现事务,我们的Service层业务也要实现事务。

例如:要实现数据的删除,删除数据库中的数据并删除Redis缓存中的数据,他们在一个ServiceImpl的方法中,我们要实现这两个操作放在一个事务中,两个操作同时成功 / 失败,所有,要使用Spring事务。

SpringBoot中事务实现

ServiceImpl的方法 / ServiceImpl类 上加上@Transactional。

  • 方法:注解只对public方法有效(因为@Transactional 工作原理是基于AOP实现的),如果在 protected、private 或者默认方法上使用,没有作用,也不报错。
  • 类:该类的所有 public 方法将都具有该类型的事务属性

注意:在方法级别使用该注解可以覆盖类级别使用该注解。

// 这个类中的public方法如果抛出Exception,则该方法会回滚
@Transactional(rollbackFor = Exception.class)
public class DefaultFooService implements FooService {
 
  public Foo getFoo(String fooName) {
    // do something
  }
 
  // these settings have precedence for this method
  //方法上注解属性会覆盖类注解上的相同属性
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

@EnableTransactionManagement

SpringBoot启动类上加上不用加@EnableTransactionManagement注解,SpringBoot自动装配已经帮我们处理了,SpringBoot项目默认支持事务。

@Transactional

作用位置:接口、接口方法、类以及类方法

Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。

注意:

默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

Transactional类

package org.springframework.transaction.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;
 
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
 
	@AliasFor("transactionManager")
	String value() default "";
 
	@AliasFor("value")
	String transactionManager() default "";
 
	Propagation propagation() default Propagation.REQUIRED;
 
	Isolation isolation() default Isolation.DEFAULT;
 
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
 
	boolean readOnly() default false;
 
	Class<? extends Throwable>[] rollbackFor() default {};
 
	String[] rollbackForClassName() default {};
 
	Class<? extends Throwable>[] noRollbackFor() default {};
 
	String[] noRollbackForClassName() default {};
}

注解的属性

属性类型描述
valueString可选的限定描述符,指定使用的事务管理器
propagationenum: Propagation可选的事务传播行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组

在Spring中定义了五种隔离级别常量

一般用默认就可以。

package org.springframework.transaction.annotation;
 
import org.springframework.transaction.TransactionDefinition;
 
public enum Isolation {
 
	DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
 
	READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
 
	READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
 
	REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
	
	SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
 
 
	private final int value;
 
	Isolation(int value) {
		this.value = value;
	}
 
	public int value() {
		return this.value;
	}
}
常量说明
TransactionDefinition.ISOLATION_DEFAULT数据库默认的隔离级别,MySQL默认采用的 REPEATABLE_READ隔离级别
TransactionDefinition.ISOLATION_READ_UNCOMMITTED最低的隔离级别,允许读取未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
TransactionDefinition.ISOLATION_REPEATABLE_READ对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。MySQL中通过MVCC解决了该隔离级别下出现幻读的可能。
TransactionDefinition.ISOLATION_SERIALIZABLE串行化隔离级别,该级别可以防止脏读、不可重复读以及幻读,但是串行化会影响性能。

Spring定义了七种事务传播行为

类型说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

Spring事务什么情况下会失效

【1】@Transactional 应用在非 public 修饰的方法上

@Transactional 工作原理是基于AOP实现的

所以,注解必须作用在public的方法上,否则失效。

【2】数据库引擎是否支持事务(MySql的MyIsam引擎不支持事物)

【3】@Transactional注解属性 propagation 设置错误

  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异。

【4】@Transactional 注解属性 rollbackFor 设置错误​       

  • Spring默认是回滚RuntimeException才回滚。
  • 当然自定义的RuntimeException异常类也是可以的。
  • 如果希望Spring能够回滚别类型的异常,那就需要使用rollbackFor去指定(当然如果是指定异常的子类,也同样会回滚)。

【5】同一个类中方法调用,导致@Transactional失效​       

在同一个类中,没有加事务的方法A调用加事务的方法B,方法B的事务失效。

( 其实这还是由于使用Spring AOP代理造成的),所以:建议把整个类加上事务注解。

【6】异常被捕获了​        

这个就比较简单了,就是你自己捕捉到了异常,并且自己处理,并不会抛出到上层的方法调用。

那就不会生效了。

@Transactional
public void contextLoads() {
    try {
        int i = 0;
    }catch (Exception e){
        System.out.println("不可以回滚");
    }
}

可以手动设置回滚

@Transactional
public void contextLoads() {
    try {
        int i = 0;
    }catch (Exception e){
        // 手动回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        System.out.println("可以回滚");
    }
}

【7】新开启一个线程        

Spring实现事务的原理是通过ThreadLocal把数据库连接绑定到当前线程中,新开启一个线程获取到的连接就不是同一个了。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring Bean自动装配入门到精通

    Spring Bean自动装配入门到精通

    自动装配是使用spring满足bean依赖的一种方法,spring会在应用上下文中为某个bean寻找其依赖的bean,Spring中bean有三种装配机制,分别是:在xml中显式配置、在java中显式配置、隐式的bean发现机制和自动装配
    2022-08-08
  • Java经典排序算法之插入排序代码实例

    Java经典排序算法之插入排序代码实例

    这篇文章主要介绍了Java经典排序算法之插入排序代码实例,插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入,需要的朋友可以参考下
    2023-10-10
  • MyBatis源码分析之日志logging详解

    MyBatis源码分析之日志logging详解

    这篇文章主要给大家介绍了关于MyBatis源码分析之日志logging的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-03-03
  • Maven中dependency和plugins的继承与约束

    Maven中dependency和plugins的继承与约束

    这篇文章主要介绍了Maven中dependency和plugins的继承与约束,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 详解JAVA抓取网页的图片,JAVA利用正则表达式抓取网站图片

    详解JAVA抓取网页的图片,JAVA利用正则表达式抓取网站图片

    这篇文章主要介绍了详解JAVA抓取网页的图片,JAVA利用正则表达式抓取网站图片,非常具有实用价值,需要的朋友可以参考下。
    2016-12-12
  • 华为鸿蒙系统应用开发工具 DevEco Studio的安装和使用图文教程

    华为鸿蒙系统应用开发工具 DevEco Studio的安装和使用图文教程

    HUAWEI DevEco Studio 是华为消费者业务为开发者提供的集成开发环境(IDE),旨在帮助开发者快捷、方便、高效地使用华为EMUI开放能力。这篇文章主要介绍了华为鸿蒙系统应用开发工具 DevEco Studio的安装和使用图文教程,需要的朋友可以参考下
    2021-04-04
  • Java构造器与传值学习总结

    Java构造器与传值学习总结

    这篇文章主要为大家详细介绍了Java构造器与传值学习总结,文中示例介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Java字符串相关类StringBuffer的用法详解

    Java字符串相关类StringBuffer的用法详解

    java.lang包下的StringBuffer类,代表着可变的字符序列,可以用来对字符串内容进行增删改操作。本文将通过示例详细说说它的用法,感兴趣的可以跟随小编一起学习一下
    2022-10-10
  • SpringAOP核心对象的创建图解

    SpringAOP核心对象的创建图解

    这篇文章主要介绍了SpringAOP核心对象的创建详解,通过使用AOP,我们可以将横切关注点(如日志记录、性能监控、事务管理等)从业务逻辑中分离出来,使得代码更加模块化、可维护性更高,需要的朋友可以参考下
    2023-10-10
  • 使用Java进行FreeMarker的web模板开发的基础教程

    使用Java进行FreeMarker的web模板开发的基础教程

    这篇文章主要介绍了使用Java进行FreeMarker模板引擎开发的基础教程,文中针对FreeMarker的网页标签用法给出了一些例子,需要的朋友可以参考下
    2016-03-03

最新评论