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

yizhihongxing

让我们来详细讲解基于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日

相关文章

  • MyBatis多表关联查询的实现示例

    下面是关于”MyBatis多表关联查询的实现示例”的完整攻略。 标题 MyBatis多表关联查询的实现示例 简介 在MyBatis框架中,多表关联查询是非常常见的,本文将介绍如何使用MyBatis实现多表关联查询。 数据准备 在实现多表关联查询之前,我们需要先准备好测试数据。假设我们有两张表:学生表(student)和班级表(class),并且学生表中有一个…

    database 2023年5月22日
    00
  • Teradata和Neo4j的区别

    Teradata和Neo4j是两种不同类型的数据库管理系统,它们的设计和应用场景有所不同。下面将详细讲解Teradata和Neo4j的区别,并且使用实例进行说明: 1. 数据类型和数据结构 Teradata:面向列的存储结构,适合处理数值型数据,支持丰富的数据类型,如整型、浮点型、日期型、时间型等。 Neo4j:面向图的存储结构,适合存储复杂的关系型数据。它…

    database 2023年3月27日
    00
  • [Redis] ** cannot be cast to java.lang.String

    先上问题: java.lang.ClassCastException: com.ppdai.cbd.ddp.thirdparty.contract.bhxtzx.BHXTZXTask cannot be cast to java.lang.String at org.springframework.data.redis.serializer.StringRe…

    Redis 2023年4月12日
    00
  • 一个查询的SQL语句请教,希望能够用一条SQL语句得到结果

    首先需要明确查询的目的和查询的表,然后才能构建出相应的SQL语句。 SQL语句由若干个关键字组成,通常包括 SELECT、FROM、WHERE、GROUP BY、HAVING和ORDER BY。以下是每个关键字的作用: SELECT:指定要查询的字段,也可以使用聚合函数计算结果; FROM:指定要查询的表; WHERE:指定查询条件,过滤结果; GROUP …

    database 2023年5月21日
    00
  • 详细讲解PostgreSQL中的全文搜索的用法

    PostgreSQL中的全文搜索 PostgreSQL是一款强大的开源数据库,它除了支持传统的数据库功能之外,还支持全文搜索。这意味着,我们可以在表的某个字段中进行文本搜索,并高效地返回匹配的结果。 步骤 要使用全文搜索功能,我们需要做以下几个步骤: 安装pg_trgm扩展。pg_trgm是PostgreSQL的一个文本搜索扩展,提供了元音间距离算法和n-g…

    database 2023年5月19日
    00
  • 建立在Tablestore的Wifi设备监管系统架构实现

    建立在Tablestore的Wifi设备监管系统架构实现是一个典型的大型云计算项目,具有一定的技术难度和复杂度。下面是一个完整的攻略,帮助开发人员搭建该系统。 系统架构实现概述 该系统主要包含以下几个模块: WiFi设备数据采集模块:负责采集WiFi设备的数据,包括MAC地址、信号强度、入网时间、出网时间等; 数据存储模块:负责存储采集到的WiFi设备数据,…

    database 2023年5月22日
    00
  • 关于Oracle listener日志解析利器的使用方法

    关于Oracle listener日志解析利器的使用方法 1. 前言 Oracle Listener日志是Oracle数据库用来记录与其他应用程序之间的通信信息的一种日志文件,通常存储在$ORACLE_HOME/network/log目录下。而Oracle Listener日志解析利器则是一种用来快速分析Oracle Listener日志的工具,可以提高分析…

    database 2023年5月21日
    00
  • mysql中is null语句的用法分享

    当我们需要查询某个字段的值是否为 null 时,可以使用 MySQL 中的 is null 语句进行查询。 具体用法如下: SELECT * FROM table_name WHERE column_name IS NULL; 在此语句中,我们使用了 SELECT 语句来选择表格中所有的列,然后使用 WHERE 语句来筛选出其中的行。 而在 WHERE 语句…

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