spring中12种@Transactional的失效场景(小结)

yizhihongxing

下面就来详细讲解“Spring中12种@Transactional的失效场景(小结)”。

首先,需要明确的是,@Transactional是用来控制事务的注解,它可以应用于方法、类或接口上,用来确保在执行该方法时开启了一个事务,并在方法结束时提交或回滚事务。但是,在某些情况下,@Transactional注解可能会失效。下面分别讲解12种@Transactional的失效场景:

  1. 在同一个类中调用@Transactional方法

在同一个类中直接调用被@Transactional注解的方法时,事务不会起作用。这是因为@Transactional是通过动态代理去实现的,而如果在同一个类中调用,动态代理并不会被触发。所以,我们需要通过自我注入的方式调用需要使用事务的方法,这样动态代理就会生效。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserService userService;

    @Transactional
    public void addUserAndLog() {
        userService.addUser();
        // 记录日志
    }

    @Transactional
    public void addUser() {
        // 添加用户
    }

}
  1. 在非public的方法上使用@Transactional

在许多情况下,我们可能会在一个类中定义私有的或受保护的方法。然而,如果我们在这些方法上使用@Transactional注解,那么事务也会失效。因为,Spring只会拦截public方法,而不会拦截非public方法。示例代码如下:

public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        // 添加用户
        addUserLog();
    }

    @Transactional
    private void addUserLog() {
        // 添加用户日志
    }

}
  1. 在非事务方法中调用@Transactional方法

如果一个方法没有被@Transactional注解标记,而它又调用了一个被@Transactional注解标记的方法,那么被调用的方法的事务将不会开启。这是因为,被调用的方法已经失去了@Transactional注解的作用。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        // 添加用户
        addUserLog();
    }

    @Transactional
    public void addUserLog() {
        // 添加用户日志
    }

    public void addUserAndLog() {
        addUser();
    }

}
  1. 在静态方法上使用@Transactional

由于@Transactional注解是作用在实例方法上的,所以它不能用于静态方法中,否则会失效。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        User user = User.builder().username("test").password("123456").build();
        User.save(user);
    }

    @Transactional
    public static void save(User user) {
        // 保存用户
    }

}
  1. @Transactional标注方法的异常被try/catch

如果我们在@Transactional标注的方法中使用try/catch捕获了异常,那么就会导致事务无法回滚。因为一旦我们捕获了异常,它就不会再被传递到Spring事务管理器中,事务管理器就无法识别到异常并进行回滚。示例代码如下:

@Transactional
public void addUser() {
    try {
        // 添加用户
    } catch (Exception e) {
        // 异常捕获
    }
}
  1. @Transactional标注方法的异常类为RuntimeException

如果@Transactional标注的方法中抛出了RuntimeException及其子类的异常,默认情况下事务会回滚,可以通过在异常类上使用@Transaction注解的noRollbackFor属性,来使得事务不回滚。示例代码如下:

@Transactional
public void addUser() {
    throw new RuntimeException("测试异常");
}

@Transactional(noRollbackFor = RuntimeException.class)
public void addUser() {
    throw new RuntimeException("测试异常");
}
  1. @Transactional标注方法和catch块中都抛出异常

如果@Transactional标注的方法中发生了异常,并在catch块中继续抛出了异常,那么事务也会失效。因为catch块中的异常会覆盖原来产生的异常。示例代码如下:

@Transactional
public void addUser() {
    try {
        // 添加用户
    } catch (Exception e) {
        // 处理异常
        throw new RuntimeException("处理异常时出错");
    }
}
  1. @Transactional标注方法所在的类没有被Spring容器管理

@Transactional注解只有在Spring容器管辖的类中才会生效,所以如果@Transactional注解标注在一个未被Spring容器管理的类中,那么它将不起作用。示例代码如下:

public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        // 添加用户
    }

}
  1. @Transactional标注在类级别上中间加入了非@Transactional的方法,造成事务失效

如果在一个类中,@Transactional注解是针对类级别的,在类中间加了一个没有@Transactional注解的方法,这个非@Transactional方法就会出现事务失效。这是因为,Spring在检测到事务注解时,会动态创建一个代理类进行事务处理,而在代理类中,会通过在方法前后添加事务通知来管理事务。但是,如果我们在被代理的类中间插入了一个方法,Spring就无法保证在代理类中添加事务通知的顺序和正确性了。示例代码如下:

@Service
@Transactional
public class UserServiceImpl implements UserService {

    public void test() {
        // 测试方法
    }

    public void addUser() {
        // 添加用户
    }

}
  1. 使用自调用的方式调用@Transactional方法

如果调用@Transactional方法时使用了自调用的方式,事务也会失效。这是因为自调用会绕过Spring的动态代理。示例代码如下:

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserService userService;

    public void addUserAndLog() {
        addUser();
        this.addUserLog();
    }

    @Transactional
    public void addUser() {
        // 添加用户
    }

    @Transactional
    public void addUserLog() {
        // 添加用户日志
    }

}
  1. 同一个方法内,同时调用多个@Transactional注解的方法,传播属性设置不当

如果在同一个方法内,同时调用多个@Transactional注解的方法,并在这些方法之间使用事务传播属性时,需要特别注意传播属性的设置,否则可能会出现一系列问题,比如事务无法回滚、出现死锁等。示例代码如下:

@Transactional
public void addUser() {
    try {
        // 添加用户
        addUserLog1();
        addUserLog2();
    } catch (Exception e) {
        // 异常处理
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserLog1() {
    // 添加用户日志1
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserLog2() {
    // 添加用户日志2
}
  1. 在子线程中调用@Transactional标注的方法

在子线程中调用@Transactional标注的方法是不会生效的,因为Spring的事务管理是基于ThreadLocal来实现的,所以在子线程中,Spring的事务管理器无法将事务的上下文传递到新的线程中。所以,如果需要在子线程中使用事务,可以使用TransactionTemplate或者手动同步事务。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Transactional
    public void addUser() {
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                // 在子线程中使用事务
                return null;
            }
        });
    }

}

以上就是“Spring中12种@Transactional的失效场景(小结)”的详细攻略。希望对大家有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring中12种@Transactional的失效场景(小结) - Python技术站

(0)
上一篇 2023年5月21日
下一篇 2023年5月21日

相关文章

  • 详解Redis5种数据类型的使用方法

    Redis是一个使用内存作为数据存储的高性能键值数据库。它支持多种数据类型,包括字符串、哈希、列表、集合和有序集合,每种类型都有各自的特点和用途。接下来,我们将介绍Redis支持的每种数据类型及其使用方法。 string字符串 String 是 Redis 最基本的数据类型。字符串是二进制安全(binary safe)的,意味着可以存储任何数据,如字符串、数…

    Redis 2023年3月18日
    00
  • 如何使用Python在MySQL中使用字符集?

    在MySQL中,字符集用于指定表中的文本数据的编码方式。在Python中,可以使用MySQL连接来执行字符集查询和设置。以下是在Python中使用字符集的完整攻略,包括字符集的基本语法、使用字符集的示例以及如何在Python中使用字符集。 字符集的基本语法 在MySQL中,可以使用CHAR SET关键字来指定表中的字符集。以下是创建表时指定字符集的基本语法:…

    python 2023年5月12日
    00
  • SpringBoot整合MyCat实现读写分离的方法

    下面我为你详细讲解如何通过Spring Boot和MyCat实现读写分离。 一、概述 MyCat是一个开源的数据库中间件,提供了多种高性能、高可用性的数据库分片集群方案。MyCat支持读写分离、数据分片、数据分区、集群高可用等多种特性。Spring Boot是一个快速开发、便捷启动的框架,支持自动化配置和快速集成第三方组件。 这里我将介绍如何使用Spring…

    database 2023年5月22日
    00
  • 详解Spring中的Transactional属性

    详解Spring中的Transactional属性 在Spring框架中,事务管理是非常重要且常用的一个功能。而@Transactional属性是管理事务的重要属性之一。本文将详细讲解@Transactional属性,并提供一些示例来解释常见的用途。 什么是@Transactional属性? @Transactional属性用于指定带有事务性质的方法。它可以…

    database 2023年5月21日
    00
  • 详解一条update语句是怎样执行原理解析

    下面我将逐步详细讲解一条update语句是怎样执行的。 1. 概述 在数据库中,update语句是用于更新一张表中的数据的。update语句的执行过程可以分为以下三个步骤: 解析和检查update语句:在执行update语句之前,数据库会对语句进行解析和检查,以确保语句的正确性和有效性。 执行update语句:一旦语句被认为是有效的,数据库就会执行该语句来更…

    database 2023年5月22日
    00
  • VPS自动备份数据库到FTP的脚本代码

    首先,我们需要明确什么是VPS、FTP和数据库自动备份脚本。VPS指的是Virtual Private Server,即虚拟专用服务器,是一种虚拟化技术,能够在一台物理机器上划分出多个独立的虚拟服务器。FTP指的是文件传输协议,是一种用于文件传输的网络协议。数据库自动备份脚本则是一段自动化脚本代码,能够在设定时间内自动备份VPS服务器上的数据库,并将备份文件…

    database 2023年5月22日
    00
  • pyqt远程批量执行Linux命令程序的方法

    要实现pyqt远程批量执行Linux命令程序,一般可以使用Paramiko库来连接和操作远程主机。同时,QT提供的QProcess类可以用来在PyQt中执行外部程序。下面是具体的步骤: 步骤一:安装Paramiko库 使用以下命令安装Paramiko库: pip install paramiko 步骤二:连接远程主机 使用Paramiko库连接远程主机,需要…

    database 2023年5月22日
    00
  • Asp.net中使用Sqlite数据库的方法

    要在Asp.net中使用Sqlite数据库,需要进行以下步骤: 1. 安装SQLite包 首先需要安装System.Data.SQLite.Core NuGet包,可以在Visual Studio中打开NuGet包管理器(NuGet Package Manager),在管理器搜索框中输入System.Data.SQLite.Core,找到该包并安装。 2. …

    database 2023年5月21日
    00
合作推广
合作推广
分享本页
返回顶部