Spring事务和事务传播机制

简介: Spring事务和事务传播机制

一:Spring中事务的实现

Spring中的事务操作分为两类:

1:编程式事务(手动写代码操作事务)

2:声明式事务(利用注解自动开启和提交事务)

二:Spring事务的操作

在spring操作中,也分为三个步骤 ,开启事务,回滚事务,提交事务。

下面,我们通过案例来进行操作。

2.1:Spring编程式实现事务

2.1.1:User表

#创建用户表
create table user(
    userId int primary key auto_increment,
    #约定用户名是独一无二的
    username varchar(64) unique ,
    #密码
    password varchar(64)
);

2.1.2:Mapper接口声明的方法

在mapper中声明的方法,我们用的是添加一个用户信息的操作。返回的数字代表影响的行数。

@Mapper
public interface UserMapper {
    //返回int的数据
    int addUserInfo(UserInfo userInfo);
}

2.1.3:Service层

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    public Integer addUserInfo(UserInfo userInfo){
        return userMapper.addUserInfo(userInfo);
    }
}

2.1.4:Controller层

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    //添加这个注解,将事务管理器引入进来
    @Autowired
    private DataSourceTransactionManager transactionManager;
    //设置事务的属性
    @Autowired
    private TransactionDefinition transactionDefinition;
    //返回受影响的行数
    @RequestMapping("/add")
    public Integer add(UserInfo userInfo){
        //第一步:非空校验
        if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())
        ||!StringUtils.hasLength(userInfo.getPassword())){
            return 0;
        }
        //1.开启事务,得到并开启事务
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        Integer integer = userService.addUserInfo(userInfo);
        System.out.println(integer);
        //2:回滚事务
        transactionManager.rollback(transaction);
        return integer;
    }
}

此时,由于我们对添加用户信息这一条进行了回滚,所以,新的用户信息添加不到数据库当中,但是返回的数值仍旧是1.

但当我们将回滚事务转变成提交事务,我们看数据库里面能不能得到添加的用户信息。

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    //添加这个注解,将事务管理器引入进来
    @Autowired
    private DataSourceTransactionManager transactionManager;
    //设置事务的属性
    @Autowired
    private TransactionDefinition transactionDefinition;
    //返回受影响的行数
    @RequestMapping("/add")
    public Integer add(UserInfo userInfo){
        //第一步:非空校验
        if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())
        ||!StringUtils.hasLength(userInfo.getPassword())){
            return 0;
        }
        //1.开启事务,得到并开启事务
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        Integer integer = userService.addUserInfo(userInfo);
        System.out.println(integer);
        //2:回滚事务
        transactionManager.commit(transaction);
        return integer;
    }
}

运行结果:

2.2:Spring声明式事务(自动)

在controller层添加@Transactional这个注解,就可以开启事务了。

特点:

    1:@Transaction这个注解可以加在类和方法上,加在类上代表这个类里面的public方法,都具有事务,加在方法上,代表此处的方法具有事务。

    2:@Transactional,在方法执行前开启事务,在方法执行后提交事务,如若在方法执行过程中,出现异常,则回滚事务。

2.2.1:实现案例

@Transactional
    @RequestMapping("/insert")
    public Integer insert(UserInfo userInfo){
        if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())
        ||!StringUtils.hasLength(userInfo.getPassword())){
            return 0;
        }
        Integer integer = userService.addUserInfo(userInfo);
        return integer;
    }

上述代码中,在方法运行过程中,并没有出现异常,那么,就会自动提交事务。倘若,我们给这个方法中添加算数异常,看看会发生什么?

@Transactional
    @RequestMapping("/insert")
    public Integer insert(UserInfo userInfo){
        if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())
        ||!StringUtils.hasLength(userInfo.getPassword())){
            return 0;
        }
        Integer integer = userService.addUserInfo(userInfo);
        System.out.println(integer);
        //搞一个算数异常
        int num=10/0;
        return integer;
    }

此时就会报出算数异常,在方法中出现算数异常,那么,此时,就会出现事务回滚,即不添加新的用户信息。

2.2.2: @Transactional注意事项

当程序中有try-catch语句时,即使程序中有异常,事务也不会回滚。

@Transactional
    @RequestMapping("/insert")
    public Integer insert(UserInfo userInfo){
        if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())
        ||!StringUtils.hasLength(userInfo.getPassword())){
            return 0;
        }
        Integer integer = userService.addUserInfo(userInfo);
        System.out.println(integer);
        //搞一个算数异常
        try {
            int num=10/0;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return integer;
    }

当在程序中,出现算数异常时,我们添加用户名wangwu和密码12345。

控制台代码:

数据库表:

从上述结果来看,发现添加上了,此时,事务并没有进行回滚。

如何解决?

解决方案一:将异常继续抛出,交给spring

解决方案二:

@Transactional
    @RequestMapping("/insert")
    public Integer insert(UserInfo userInfo){
        if(userInfo==null||!StringUtils.hasLength(userInfo.getUsername())
        ||!StringUtils.hasLength(userInfo.getPassword())){
            return 0;
        }
        Integer integer = userService.addUserInfo(userInfo);
        System.out.println(integer);
        //搞一个算数异常
        try {
            int num=10/0;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            //使用代码,手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return integer;
    }

三:事务的隔离级别

3.1:原子性

多个操作被打包成一个整体,要么全部都能够正确执行,要么就一个都不执行。

3.2:一致性

事务之前和事务之后,数据都能对上。


3.3:持久性

事务执行的各种操作,都是持久化存储在硬盘上的。(一旦事务执行成功,这里所有的操作,都是写在硬盘上的)


3.4:隔离性

并发执行事务的时候,隔离性,会在执行效率和数据可靠之间做出权衡。(“隔离"描述的是同时执行的事务之间,相互的影响。隔离性越高,并发性越低,数据越可靠)

如果这两个事务,修改的是同一张表,这个时候存在麻烦。

由于上面的执行顺序可能存在差异,可能导致执行结果不同。


3.4.1:脏读

脏数据,是临时的,并非是最终的数据。

    如何解决脏读?

3.4.2:不可重复读

在同一个读取事务中,可能会涉及到读操作,多个读操作,读到的数据不一样。

   如何解决不可重复读?

3.4.3:幻读

一个事务在多次读的过程中,虽然读到的数据值是一样的,但是结果集不一样。

eg:第一次的记录是10条,第二次读的是11条记录。前十次记录是一样的,但是多出来一条。

 如何解决幻读?

3.4.4:Spring事务隔离级别

Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。

四:Spring事务传播机制

4.1:什么是事务传播机制?

Spring事务传播机制定义了多个包含事务的方法,相互调用时,事务是如何传递的。

当三个方法用同一个模式时,任何一个方法出问题了,都会进行事务的回滚操作。

三个方法用三种不同的模式,各个之间的方式不影响。

4.2:事务传播机制有哪些?

相关文章
|
2月前
|
SQL Java 关系型数据库
Spring事务传播机制:7种姿势教你玩转"事务接力赛"
事务传播机制是Spring框架中用于管理事务行为的重要概念,它决定了在方法调用时事务如何传递与执行。通过7种传播行为,开发者可以灵活控制事务边界,适应不同业务场景。例如:REQUIRED默认加入或新建事务,REQUIRES_NEW独立开启新事务,NESTED支持嵌套回滚等。合理使用传播机制不仅能保障数据一致性,还能提升系统性能与健壮性。掌握这“七种人格”,才能在复杂业务中游刃有余。
|
3月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
8月前
|
Java Spring
Spring中事务失效的场景
因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是被代理对象调用, 那么@Transactional会失效 同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的利⽤代理,也会导致@Transactianal失效 如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常, @Transactional也会失效
|
8月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——常见问题总结
本文总结了Spring Boot中使用事务的常见问题,虽然通过`@Transactional`注解可以轻松实现事务管理,但在实际项目中仍有许多潜在坑点。文章详细分析了三个典型问题:1) 异常未被捕获导致事务未回滚,需明确指定`rollbackFor`属性;2) 异常被try-catch“吃掉”,应避免在事务方法中直接处理异常;3) 事务范围与锁范围不一致引发并发问题,建议调整锁策略以覆盖事务范围。这些问题看似简单,但一旦发生,排查难度较大,因此开发时需格外留意。最后,文章提供了课程源代码下载地址,供读者实践参考。
194 0
|
8月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
1100 0
|
8月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——事务相关
本文介绍Spring Boot事务配置管理,阐述事务在企业应用开发中的重要性。事务确保数据操作可靠,任一异常均可回滚至初始状态,如转账、购票等场景需全流程执行成功才算完成。同时,事务管理在Spring Boot的service层广泛应用,但根据实际需求也可能存在无需事务的情况,例如独立数据插入操作。
214 0
|
6月前
|
人工智能 Java 数据库连接
Spring事务失效场景
本文深入探讨了Spring框架中事务管理可能失效的几种常见场景及解决方案,包括事务方法访问级别不当、方法内部自调用、错误的异常处理、事务管理器或数据源配置错误、数据库不支持事务以及不合理的事务传播行为或隔离级别。通过合理配置和正确使用`@Transactional`注解,开发者可以有效避免这些问题,确保应用的数据一致性和完整性。
335 10
|
5月前
|
Java 关系型数据库 MySQL
【Spring】【事务】初学者直呼学会了的Spring事务入门
本文深入解析了Spring事务的核心概念与使用方法。Spring事务是一种数据库事务管理机制,通过确保操作的原子性、一致性、隔离性和持久性(ACID),维护数据完整性。文章详细讲解了声明式事务(@Transactional注解)和编程式事务(TransactionTemplate、PlatformTransactionManager)的区别与用法,并探讨了事务传播行为(如REQUIRED、REQUIRES_NEW等)及隔离级别(如READ_COMMITTED、REPEATABLE_READ)。
435 1
|
8月前
|
SQL Java 数据库连接
Spring中的事务是如何实现的
1. Spring事务底层是基于数据库事务和AOP机制的 2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean 3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮ 常重要的⼀步 6. 然后执⾏当前⽅法,⽅法中会执⾏sql 7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务
|
8月前
|
JavaScript Java 开发者
Spring事务失效,常见的情况有哪些?
本文总结了Spring事务失效的7种常见情况,包括未启用事务管理功能、方法非public类型、数据源未配置事务管理器、自身调用问题、异常类型错误、异常被吞以及业务和事务代码不在同一线程中。同时提供了两种快速定位事务相关Bug的方法:通过查看日志(设置为debug模式)或调试代码(在TransactionInterceptor的invoke方法中设置断点)。文章帮助开发者更好地理解和解决Spring事务中的问题。
318 7