Spring @Transactional事务失效的原因分析

让我们来详细讲解 Spring @Transactional事务失效的原因分析。事务是应用程序中非常重要的概念,对于保证数据一致性具有至关重要的作用。Spring框架提供了@Transactional注解作为声明式事务管理的方式,可以极大的减轻我们对事务的控制。然而,有时候我们会发现@Transactional失效了,这时候我们需要对其原因进行分析。

一. 什么是 @Transactional?

@Transactional是Spring框架的注解,用于声明式的事务管理。在使用@Transactional注解的方法中,Spring会为其生成一个动态代理,这个代理会在方法开始调用时开启一个事务,在方法执行结束后,根据方法的执行情况决定是提交事务,还是回滚事务。

二. @Transactional 失效的原因

在实际的应用场景中,我们很容易发现@Transactional注解失效了的情况。下面我们将逐个解释事务失效的原因。

1. 对于只有一个@Transactional的方法内部调用了其他带有事务注解的方法

示例1

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public void addUser(User user) {
        userDao.addUser(user);
        try {
            // 在当前方法内调用带有事务注解的方法
            // 由于事务的传播行为默认为REQUIRED,所以会继承当前的事务
            // 这个调用的方法执行了回滚操作,导致当前事务也回滚了
            addUserWithError(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Transactional
    public void addUserWithError(User user) {
        userDao.addUser(user);
        throw new RuntimeException("模拟数据重复添加异常");
    }

}

如果这个时候调用UserService.addUser方法,在执行addUserWithError方法时发生了异常,则由于事务传播行为的默认设置为Required,当前事务也会回滚。在addUser方法结束以后,针对于User的事务逻辑并没有生效,数据没有被插入进去。

示例2

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleService roleService;

    @Override
    public void addUser(User user, Role role) {
        userDao.addUser(user);
        // 在当前方法内调用另一个 Service 方法
        // 默认情况下,这个 Service 方法会在当前事务内进行操作
        roleService.addRole(role);
    }

}

@Service
@Transactional
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleDao roleDao;

    @Override
    public void addRole(Role role) {
        roleDao.addRole(role);
    }

}

因为在调用addRole方法时,当前事务并没有被关闭,所以实际上数据并没有被提交。因此,如果发现在@Transactional注解声明的方法内部调用了另一个带有事务注解的方法,务必保证被调用的方法不会回滚事务。

2. 对于一个类中的方法相互调用

假如一个类中有多个方法,在其中一个方法中使用了@Transactional注解,而另一个方法却直接调用了该方法。这时候事务就会失效。这是因为在这个类中,Spring通过生成代理类来管理事务,因此在同一个类中的方法中的@Transactional注解不会被代理类所拦截,从而导致事务失效。

示例

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public void addUsers(List<User> users) {
        users.forEach(this::addUser);
    }

    public void addUser(User user) {
        userDao.addUser(user);
    }

}

如果在调用addUsers方法之前没有开启事务,则在addUser方法内插入数据库的数据并不会回滚。要解决这个问题,我们可以使用编程式事务的方式,在每个方法内部都手动开启和关闭事务。

三. 总结

在使用@Transactional注解时,务必要注意各个事务之间的传播行为。对于只有一个@Transactional的方法内部调用了其他带有事务注解的方法,务必保证被调用的方法不会回滚事务。对于一个类中的方法相互调用,需要注意事务的传播行为。如果遇到事务失效的问题,可以使用编程式事务的方式手动开启和关闭事务。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring @Transactional事务失效的原因分析 - Python技术站

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

相关文章

  • 数据库SQL SELECT查询的工作原理

    数据库 SQL SELECT 查询是数据库操作的一个重要部分,它用于从一个或多个表中检索所需数据。下面是 SELECT 查询的工作原理: 1. SELECT 查询语法 SELECT 查询要求使用正确的 SQL 语法,其中包括以下基本组件: SELECT:要查询的列名或通配符 FROM:要从哪些表中检索数据 WHERE(可选):条件约束查询结果 ORDER B…

    database 2023年5月21日
    00
  • 实现在线 + 离线模式进行迁移 Redis 数据实战指南

    下面我就详细讲解一下“实现在线 + 离线模式进行迁移 Redis 数据实战指南”的完整攻略。 1、在线迁移 在线迁移是指在 Redis 服务运行正常的情况下,将部分或全部数据迁移到新的 Redis 服务上,而原先的 Redis 服务依然在运行状态。在线迁移有如下两种方法: 1.1 使用 Migrate 命令 Migrate 命令是 Redis 提供的一个在线…

    database 2023年5月22日
    00
  • MySQL UPDATE:修改数据(更新数据)详解

    MySQL UPDATE语句用于更新表中的现有数据。它允许您修改现有行,而不是添加新行。 语法: UPDATE table_name SET column1 = value1, column2 = value2, … WHERE condition; 参数说明: table_name:要更新数据的表名。 SET:指定要更改的列和它们新值的列表。 WHER…

    MySQL 2023年3月9日
    00
  • Linux 下进程的挂起和恢复命令

    进程的挂起和恢复是 Linux 系统下常见的操作。本文将介绍在 Linux 下进程的挂起和恢复命令的完整攻略,同时提供两个实际的示例说明。 进程的挂起 当系统中有一些进程正在执行时,有时需要挂起某些进程以便进行其他操作。在 Linux 下,可以使用以下命令来暂停进程的执行: kill -STOP <PID> 其中,PID 是需要暂停的进程的进程 …

    database 2023年5月22日
    00
  • MYSQL查询某字段中以逗号分隔的字符串的方法

    首先我们建立一张带有逗号分隔的字符串。 CREATE TABLE test(id int(6) NOT NULL AUTO_INCREMENT,PRIMARY KEY (id),pname VARCHAR(20) NOT NULL,pnum VARCHAR(50) NOT NULL); 然后插入带有逗号分隔的测试数据INSERT INTO test(pnam…

    MySQL 2023年4月13日
    00
  • Javascript new Date().valueOf()的作用与时间戳由来详解

    JavaScript中的new Date().valueOf()用于获取当前时间的时间戳。时间戳指的是自1970年1月1日00:00:00 UTC(世界标准时间)起至当前时间的毫秒数。JavaScript使用时间戳来表示日期和时间,这个时间戳也称为Epoch时间。 JavaScript中的new Date().valueOf()返回的是一个数值,该数值表示1…

    database 2023年5月21日
    00
  • Memcache,Redis,MongoDB(数据缓存系统)方案对比与分析

    一、问题:           数据库表数据量极大(千万条),要求让服务器更加快速地响应用户的需求。   二、解决方案:      1.通过高速服务器Cache缓存数据库数据      2.内存数据库     (这里仅从数据缓存方面考虑,当然,后期可以采用Hadoop+HBase+Hive等分布式存储分析平台) 三、主流解Cache和数据库对比:      …

    Redis 2023年4月13日
    00
  • SQL 删除全表记录

    要删除一张表中所有的记录,可以使用 SQL 中的 DELETE 语句结合 WHERE 子句来完成。下面是SQL删除全表记录的完整攻略: 步骤一:备份表格数据 在执行 DELETE 语句之前,建议您先备份整张表的数据。这一步虽然不是必需的,但是如果不小心误删了数据,备份数据可以方便地帮助您找回丢失的数据。 步骤二:使用 DELETE 语句删除表格数据 执行 D…

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