基于Spring中的事务@Transactional细节与易错点、幻读

让我们来详细讲解基于Spring中的事务 @Transactional 细节与易错点、幻读的完整攻略。

什么是事务?

事务是一组操作,这些操作要么全部执行成功,要么全部不执行。如果其中任何一项操作失败,事务会回滚到开始状态,以确保数据在数据库中的完整性。

Spring中的事务管理

Spring是一个开发框架,也提供了很好的事务管理。Spring的事务管理可以统一管理不同类型的事务,无论是JDBC的事务,还是一些支持JTA的应用服务器的事务。 Spring中的事务管理主要由两个重要的接口组成:TransactionManagerPlatformTransactionManager,其中 PlatformTransactionManager 是用来针对具体的持久化技术,向上对接了 TransactionManager。在实践中,我们主要通过配置 DataSourceTransactionManagerJpaTransactionManager 等具体实现类去进行具体的事务管理。

事务的属性配置

@Transactional 常用的属性有 propagationisolationtimeoutreadOnlyrollbackFor 等。

propagation

事务传播行为的属性有 REQUIREDREQUIRES_NEWSUPPORTSNOT_SUPPORTEDNEVERMANDATORYNESTED

通过 REQUIRED 标签指示方法需要事务支持,如果当前上下文中已经有事务了,那么它在当前事务中运行;否则,它将创建一个新的事务。下面的代码演示了 REQUIRED 行为标签的用法:

@Transactional(propagation = Propagation.REQUIRED)

isolation

隔离级别是用来处理并发事务执行所引起的问题,比如脏读、不可重复读、幻读。

常用的隔离级别包括 READ_COMMITTEDREAD_UNCOMMITTEDREPEATABLE_READSERIALIZABLE,其中 READ_COMMITTED 级别是最常用的,默认级别。

下面的代码演示了隔离级别的用法:

@Transactional(isolation = Isolation.REPEATABLE_READ)

timeout

timeout 属性指定了事务的超时时间。超时就会抛出异常,回滚事务。

下面的代码演示了超时时间 5 秒的用法:

@Transactional(timeout=5)

readOnly

readOnly 属性说明当前事务是否只读。如果只读,那么不允许事务在执行期间修改任何数据。

下面的代码演示了 readOnly 属性的实现:

@Transactional(readOnly = true)
public List<Customer> listCustomers() {
    return customerDao.findAll();
}

rollbackFor

默认情况下, Spring 事务只有在出现 Runtime 异常才回滚,在出现其他类型异常时不回滚。通过 rollbackFor 属性显式设置一些异常,可以实现在遇到这些异常时回滚事务。下面的代码演示了回滚指定异常的用法:

@Transactional(rollbackFor = {Exception.class})
public void save(Customer customer) throws Exception {
    // do something
}

常见易错点

非 Runtime 异常导致事务不回滚

事务默认只回滚 RuntimeException 异常,对于 Exception 异常不会自动回滚,因此如果你写了一个抛出 Exception 的异常,事务将不会自动回滚。需要在 @TransactionalrollbackFor 中声明,例如:

@Transactional(rollbackFor = Exception.class)

异常被吃掉导致事务不回滚

当某个异常没有被正确的抛出,甚至被吃掉了,事务也会失效。

比如在以下的代码中,由于异常被 try-catch 了,导致事务不会回滚:

@Transactional
public void test() {
    try {
        // do something
    } catch (Exception e) {
        // 异常被捕获
    }
}

外部调用类、方法没有加事务注解,导致事务失效

Spring 的事务管理针对 @Transactional 的注解才会生效,如果被调用方法所在的类没有加上事务注解,事务会失效。例如:

@Transactional
public class UserService {
    public void saveUser() {
        // do something
    }
}

public class UserController {
    private UserService userService;
    public void saveUser() {
        userService.saveUser();
    }
}

在这段代码中,UserController 调用 UserServicesaveUser() 方法,由于 UserService 类没有使用事务注解,事务不会生效。

幻读问题

幻读问题出现的原因是在并发情况下事务只锁住语句执行的数据行,而没有锁定数据页,因此其他事务可以在数据页中插入数据。这样就导致了在读取数据的时候,出现了虚假的行的情况,就好像魔幻般出现了新的行。

以下是一个处理幻读问题的示例:

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void handlePhantomRead() {
    // Step 1: 查询一次结果集
    List<Customer> customers = customerDao.findCustomersByAddress("xxx");
    // Step 2: 其他事务新增数据
    customerDao.insertCustomer(new Customer("xxx", "male", 30));
    // Step 3: 再次查询,导致出现幻读
    List<Customer> customers2 = customerDao.findCustomersByAddress("xxx");
}

以上代码中的第二步插入数据的操作,会在第一步查询数据之后被执行,从而导致第三步查询出现了幻读。要避免幻读,可以在查询前先执行 SELECT … FOR UPDATE 操作来加锁:

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void handlePhantomRead() {
    // Step 1: 查询一次结果集,并且在查询前先执行 SELECT … FOR UPDATE 操作来加锁
    List<Customer> customers = customerDao.findCustomersByAddressForUpdate("xxx");
    // Step 2: 其他事务新增数据
    customerDao.insertCustomer(new Customer("xxx", "male", 30));
    // Step 3: 再次查询,由于在查询时加锁,避免了幻读问题
    List<Customer> customers2 = customerDao.findCustomersByAddress("xxx");
}

以上便是基于Spring中的事务 @Transactional 细节与易错点、幻读的完整攻略,希望对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:基于Spring中的事务@Transactional细节与易错点、幻读 - Python技术站

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

相关文章

  • 写给正在读计算机专业的同学 该如何学习

    写给正在读计算机专业的同学 学习目标的明确与制定 首先,你需要明确自己的学习目标,比如是想学习编程语言,学习算法与数据结构,还是学习计算机系统知识等等。随之而来,你需要制定一个合理可行的学习计划,详细规划每一步的学习目标和时间安排。同时,要充分考虑到自己的实际情况和能力水平,以及资源情况等因素。 学习资源的获取 学习计划和目标确定之后,就需要开始获取学习资源…

    database 2023年5月22日
    00
  • Python与数据库的交互问题小结

    针对“Python与数据库的交互问题小结”,以下是详细的攻略: 一、数据库与Python的交互 1.1 数据库 数据库(Database)是以一定方式储存在一起并且能够被应用程序开发人员使用的数据集合,它支持数据的持久化保存、高效读取、可靠保护、安全性控制、并发操作等多种应用需求。 1.2 Python与数据库交互 Python 作为一种优秀的编程语言,支持…

    database 2023年5月21日
    00
  • php循环输出数据库内容的代码

    首先我们来讲解如何使用PHP循环输出数据库内容的代码。 准备工作 在开始编写代码之前,我们需要准备好以下事项: 一台安装了PHP和MySQL的Web服务器。 一个数据库,里面包含我们要输出的数据表。 一个用于连接数据库的PHP文件,例如 config.php。 连接数据库 在开始循环输出数据库内容之前,我们需要先连接数据库。可以使用如下代码来连接数据库: &…

    database 2023年5月21日
    00
  • MySQL数据库数据删除操作详解

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

    database 2023年5月22日
    00
  • mysql 查询指定日期时间内sql语句实现原理与代码

    MySQL 查询指定日期时间内的数据需要用到 SQL 语句中的 WHERE 子句和日期时间函数。具体实现原理和代码步骤如下: 在 SQL 语句中使用 WHERE 子句筛选指定日期时间内的数据。 WHERE 子句基本语法为 WHERE column operator value ,其中 column 表示指定的字段名称,operator 表示比较运算符,val…

    database 2023年5月22日
    00
  • nginx 负载均衡 多站点共享Session

    NGINX负载均衡多站点共享Session攻略 背景介绍 NGINX是一款高性能的反向代理、负载均衡服务器,可用于集群、高并发等场景。在多站点应用中,通常会出现需要多个站点之间共享Session的情况,本文将详细介绍如何使用NGINX实现负载均衡多站点共享Session。 实现步骤 1. Session存储 ​ Session存储是实现Session共享的前…

    database 2023年5月22日
    00
  • PHP date()格式MySQL中插入datetime方法

    了解PHP date()函数的格式非常重要,因为在操作MySQL数据库时,经常需要在datetime类型的字段中插入日期和时间。以下是使用PHP date()函数来插入datetime的方法。 步骤一:创建一个datetime变量 首先,创建一个datetime变量,然后将当前日期和时间赋给它。可以使用date()函数的”Y-m-d H:i:s”格式来生成此…

    database 2023年5月22日
    00
  • mysql下普通索引和唯一索引的效率对比

    下面是详细讲解“mysql下普通索引和唯一索引的效率对比”的完整攻略。 介绍 在MySQL中,普通索引和唯一索引都是提高检索效率的重要手段。普通索引可以让数据检索更快,而唯一索引则保证了列的唯一性、防止出现重复数据。两种索引在实际应用中各自有着各自的优势和劣势。本文将详细讲解它们的效率对比。 测试环境 本次测试的环境是: MySQL版本:8.0.22 数据库…

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