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:事务传播机制有哪些?

相关文章
|
4月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
5月前
|
Java 关系型数据库 MySQL
Spring 事务失效场景总结
Spring 事务失效场景总结
65 4
|
24天前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
40 1
Spring高手之路24——事务类型及传播行为实战指南
|
18天前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
31 3
|
3月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
3月前
|
Java 测试技术 数据库
Spring事务传播机制(最全示例)
在使用Spring框架进行开发时,`service`层的方法通常带有事务。本文详细探讨了Spring事务在多个方法间的传播机制,主要包括7种传播类型:`REQUIRED`、`SUPPORTS`、`MANDATORY`、`REQUIRES_NEW`、`NOT_SUPPORTED`、`NEVER` 和 `NESTED`。通过示例代码和数据库插入测试,逐一展示了每种类型的运作方式。例如,`REQUIRED`表示如果当前存在事务则加入该事务,否则创建新事务;`SUPPORTS`表示如果当前存在事务则加入,否则以非事务方式执行;`MANDATORY`表示必须在现有事务中运行,否则抛出异常;
160 4
Spring事务传播机制(最全示例)
|
2月前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
35 2
|
2月前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题
|
3月前
|
Java Spring
Spring 事务传播机制是什么?
Spring 事务传播机制是什么?
24 4
|
2月前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
62 0