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

下面就来详细讲解“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日

相关文章

  • MySQL数据库数据删除操作详解

    下面就来详细讲解“MySQL数据库数据删除操作详解”的完整攻略: 1. 背景介绍 MySQL数据库是目前世界上最流行的关系型数据库之一,提供了完善的数据存储和管理功能,其中包括了数据删除操作。不过,数据删除操作需要谨慎操作,否则可能会导致数据的丢失。 2. 删除操作的语法 以下是MySQL数据库中删除操作的基本语法: DELETE FROM table_na…

    database 2023年5月22日
    00
  • 内容标记的存储的好处

    作为网站的作者,使用内容标记语言(如Markdown)来编写网站文章和页面的内容是一个非常好的选择。由于内容标记可以被存储为纯文本,因此具有以下好处: 易于维护和编辑:将内容存储为纯文本文档可以使作者轻松地编辑和维护内容,而无需使用复杂的图形用户界面。具有标记语言的文本中的格式化元素(如标题、引用、列表等)是非常直观的,并且使用常规文本编辑器就可以完成。 可…

    database 2023年3月27日
    00
  • MySQL本地版本升级超详细教程(从5.5.20升到8.0.21)

    MySQL本地版本升级超详细教程 如果你使用 MySQL 数据库,想要将本地 MySQL 升级到最新版本(如从 5.5.20 升级到 8.0.21),那么你可以按照下面的步骤进行操作,此方式延续自 5.5 版本(在 Windows 平台上)运行至 8.0 版本。 步骤1:备份旧版本的 MySQL 数据库 首先,你需要备份旧版本 MySQL 数据库。备份有助于…

    database 2023年5月22日
    00
  • vscode内网访问服务器的方法

    下面是详细的“vscode内网访问服务器的方法”的攻略。 什么是vscode内网访问服务器? 通常情况下,我们的电脑和服务器一般都处于同一个局域网,如果我们直接在vscode上连接服务器,即使服务器开了对外映射的端口,也无法直接连接,这就是内网访问。 解决方法 要解决这个问题,我们可以通过在本地电脑与服务器之间建立一个SSH隧道,来实现内网访问。下面我们将具…

    database 2023年5月22日
    00
  • SQL 从不固定位置提取字符串的元素

    当我们需要从字符串中提取指定的元素时,通过在SQL中使用一些内置的函数,例如SUBSTRING()和CHARINDEX()函数,可以轻松完成这个任务。 在下面的示例中,我将向您展示如何从不同位置提取字符串中的元素: 示例1:从起始位置提取字符串的元素 假设我们有以下这个字符串 “Hello World”,现在我们想要从字符串的起始位置提取前4个字符。可以通过…

    database 2023年3月27日
    00
  • Docker私有仓库Harbor介绍和部署方法详解

    下面是Docker私有仓库Harbor介绍和部署方法详解的完整攻略。 什么是Harbor Harbor是一个开源的私有Docker仓库,它提供了安全、可靠的Docker仓库解决方案,并且具有以下特点: 权限控制:可以通过用户组、项目和角色来管理访问权限 复制和同步:支持主从模式、跨数据中心的复制和同步功能 漏洞扫描:支持在代码提交之前或者镜像推送之后进行安全…

    database 2023年5月22日
    00
  • 在Linux系统上同时监控多个Oracle数据库表空间的方法

    在Linux系统上同时监控多个Oracle数据库表空间的方法有多种,下面我们将介绍两种方法: 方法一:使用脚本实现 编写脚本 首先,我们需要创建一个脚本,用于监控多个表空间。如下所示: #!/bin/bash # 定义要监控的表空间 tablespaces=("USERS" "EXAMPLE") while true …

    database 2023年5月22日
    00
  • Mysql基础入门 轻松学习Mysql命令

    Mysql基础入门 轻松学习Mysql命令 Mysql是一种常用的关系型数据库管理系统,本文将带你入门学习Mysql的基本命令。 安装Mysql 首先需要安装Mysql,可以从官方网站上下载并安装适合自己操作系统的版本。在安装完成后,可以通过以下命令登录到Mysql的命令行界面: mysql -u username -p 其中username为用户名。执行上…

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