【Spring Boot系列】- Spring Boot事务应用详解

简介: 笔记

一、事务简介


事务(Transaction)是数据库操作最基本单元,逻辑上一组操作,要么都成功。如果有一个操作失败。则事务操作都失败(回滚(Rollback))。


事务的四个特性(ACID):

1. 原子性(Atomicity)

一个事务(Transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。


2. 一致性(Consistency)

事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则。


3. 隔离性(Isolation)

一个事务的执行不能被其它事务干扰,即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能互相打扰。


4. 隔离性(Durability)

事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

其中,事务隔离又分为以下 4 种不同的级别


▪未提交读(Read uncommitted):最低的隔离级别,允许“脏读”(dirty reads),事务可以看到其他事务“尚未提交”的修改。如果另一个事务回滚,那么当前事务读到的数据就是脏数据。

▪提交读(read committed):一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。

▪可重复读(repeatable read): 一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了

▪串行化(Serializable): 最严格的隔离级别,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用 Serializable 隔离级别。

需要格外注意的是:事务能否生效,取决于数据库引擎是否支持事务。如MySQL的InnoDB引擎是支持事务的,但是MyISAM就不支持事务。


二、Spring事务


Spring对事务提供了很好的支持。Spring借助IOC容器强大的配置能力,为事务提供丰富功能支持。


Spring支持以下2种事务管理方式:


1.声明式事务管理:Spring 声明式事务管理在底层采用了 AOP 技术,其最大的优点在于无须通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。

2.编程式事务管理:编程式事务管理是通过编写代码实现的事务管理。 这种方式能够在代码中精确地定义事务的边界,我们可以根据需求规定事务从哪里开始,到哪里结束。

选择编程式事务还是声明式事务,很大程度上就是在控制权上颗粒度和易用性之间进行权衡。


▪编程式对事物控制的细粒度更高,我们能够精确的控制事务的边界,事务的开始和结束完全取决于我们的需求,但这种方式存在一个致命的缺点,那就是事务规则与业务代码耦合度高,难以维护,因此我们很少使用这种方式对事务进行管理。

▪声明式事务易用性更高,对业务代码没有侵入性,耦合度低,易于维护,因此这种方式也是我们最常用的事务管理方式。


三、Spring声明式事务


Spring的声明式事务管理在底层是建立在AOP的基础上的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。


声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。因为事务管理本身就是一个典型的横切逻辑,正是 AOP 的用武之地。Spring 开发团队也意识到了这一点,为声明式事务提供了简单而强大的支持。


在开发中使用声明式事务,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大方便后期的代码维护。


3.1 声明式事务的2种实现方式

1.配置文件的方式: 即在spring xml文件中进行统一配置,开发者基本上就不用关注事务的事情了,代码中无需关心任何和事务相关的代码,一切交给spring处理。

2.注解的方式: 只需在需要spring来帮忙管理事务的方法上加上@Transaction注解就可以了,注解的方式相对来说更简洁一些,都需要开发者自己去进行配置,可能有些同学对spring不是太熟悉,所以配置这个有一定的风险,做好代码review就可以了。


3.2 声明式事务注解方式5个步骤

1. 启用Spring的注释驱动事务管理功能

在spring配置类上加上@EnableTransactionManagement注解

@EnableTransactionManagement
public class MainConfig4 {
}

当spring容器启动的时候,发现有@EnableTransactionManagement注解,此时会拦截所有bean的创建,扫描看一下bean上是否有@Transaction注解(类、或者父类、或者接口、或者方法中有这个注解都可以),如果有这个注解,spring会通过aop的方式给bean生成代理对象,代理对象中会增加一个拦截器,拦截器会拦截bean中public方法执行,会在方法执行之前启动事务,方法执行完毕之后提交或者回滚事务。


EnableTransactionManagement 的源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
 /**
  * spring是通过aop的方式对bean创建代理对象来实现事务管理的
  * 创建代理对象有2种方式,jdk动态代理和cglib代理
  * proxyTargetClass:为true的时候,就是强制使用cglib来创建代理
  */
 boolean proxyTargetClass() default false;
 /**
  * 用来指定事务拦截器的顺序
  * 我们知道一个方法上可以添加很多拦截器,拦截器是可以指定顺序的
  * 比如你可以自定义一些拦截器,放在事务拦截器之前或者之后执行,就可以通过order来控制
  */
 int order() default Ordered.LOWEST_PRECEDENCE;
}

2. 定义事务管理器

事务交给spring管理,那么你肯定要创建一个或者多个事务管理者,有这些管理者来管理具体的事务,比如启动事务、提交事务、回滚事务,这些都是管理者来负责的。


spring中使用PlatformTransactionManager这个接口来表示事务管理者。


PlatformTransactionManager多个实现类,用来应对不同的环境


JpaTransactionManager: 如果你用jpa来操作db,那么需要用这个管理器来帮你控制事务。


DataSourceTransactionManager: 如果你用是指定数据源的方式,比如操作数据库用的是:JdbcTemplate、mybatis、ibatis,那么需要用这个管理器来帮你控制事务。


HibernateTransactionManager: 如果你用hibernate来操作db,那么需要用这个管理器来帮你控制事务。


JtaTransactionManager: 如果你用的是java中的jta来操作db,这种通常是分布式事务,此时需要用这种管理器来控制事务。


3. 需使用事务的目标上加@Transaction注解

▪@Transaction放在接口上,那么接口的实现类中所有public都被spring自动加上事务。

▪@Transaction放在类上,那么当前类以及其下无限级子类中所有pubilc方法将被spring自动加上事务。

▪@Transaction放在public方法上,那么该方法将被spring自动加上事务。

Transaction参数介绍

20.png

4. 执行db业务操作

在@Transaction标注类或者目标方法上执行业务操作,此时这些方法会自动被spring进行事务管理。

@Component
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Transactional
    public void insertBatch(String  names) {
        jdbcTemplate.update("truncate table t_user");
        for (String name : names) {
            jdbcTemplate.update("INSERT INTO t_user(name) VALUES (?)", name);
        }
    }
}

5. 启动spring容器,使用bean执行业务操作

@Test
public void test1() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(MainConfig4.class);
    context.refresh();
    UserService userService = context.getBean(UserService.class);
    userService.insertBatch("java高并发系列", "mysql系列", "maven系列", "mybatis系列");
}


四、Spring编程式事务


通过硬编码的方式使用spring中提供的事务相关的类来控制事务。


编程式事务主要的两种用法:


1.通过PlatformTransactionManager控制事务。

2.通过TransactionTemplate控制事务。

4.1 PlatfornTransactionManager

这种是最原始的方式,代码量较大,后面其他方式都是针对这种方式的封装

@Test
    public void test1() throws Exception {
        //定义一个数据源
        DataSource dataSource = new DataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/int?characterEncoding=UTF-8&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setInitialSize(5);
        //定义一个JdbcTemplate,用来方便执行数据库增删改查
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        //1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
        PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
        //2.定义事务属性:TransactionDefinition,
        // TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
        TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        //3.开启事务:调用platformTransactionManager.getTransaction开启事务操作,得到事务状态(TransactionStatus)对象
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        //4.执行业务操作,下面就执行2个插入操作
        try {
            System.out.println("before:" + jdbcTemplate.queryForList("SELECT * from t_user"));
            jdbcTemplate.update("insert into t_user (name) values (?)", "test1-1");
            jdbcTemplate.update("insert into t_user (name) values (?)", "test1-2");
            //5.提交事务:platformTransactionManager.commit
            platformTransactionManager.commit(transactionStatus);
        } catch (Exception e) {
            //6.回滚事务:platformTransactionManager.rollback
            platformTransactionManager.rollback(transactionStatus);
        }
        System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
    }


4.2 代码分析

步骤一:定义事务管理器PlatformTransactionManager

事务管理器相当于一个管理员,这个管理员就是用来帮你控制事务的,比如开启事务,提交事务,回滚事务等等。


spring中使用PlatformTransactionManager这个接口来表示事务管理器

public interface PlatformTransactionManager {
 //获取一个事务(开启事务)
 TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
   throws TransactionException;
 //提交事务
 void commit(TransactionStatus status) throws TransactionException;
 //回滚事务
 void rollback(TransactionStatus status) throws TransactionException;
}

步骤二:定义事务属性TransactionDefinition

定义事务属性,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。

spring中使用TransactionDefinition接口来表示事务的定义信息,有个子类比较常用:DefaultTransactionDefinition。


步骤三:开启事务

调用事务管理器的getTransaction方法,即可以开启一个事务。这个方法会返回一个TransactionStatus表示事务状态的一个对象,通过TransactionStatus提供的一些方法可以用来控制事务的一些状态,比如事务最终是需要回滚还是需要提交。


步骤四:执行业务操作

事务管理器开启事务的时候,会创建一个连接,将datasource和connection映射之后丢在了ThreadLocal中,而JdbcTemplate内部执行db操作的时候,也需要获取连接,JdbcTemplate会以自己内部的datasource去上面的threadlocal中找有没有关联的连接,如果有直接拿来用,若没找到将重新创建一个连接,而此时是可以找到的,那么JdbcTemplate就参与到spring的事务中了。


4.2 TransactionTemplate

@Test
    public void test2() throws Exception {
        //定义一个数据源
        DataSource dataSource = new DataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/int?characterEncoding=UTF-8&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setInitialSize(5);
        //定义一个JdbcTemplate,用来方便执行数据库增删改查
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        //1.定义事务管理器,给其指定一个数据源(可以把事务管理器想象为一个人,这个人来负责事务的控制操作)
        PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
        //2.定义事务属性:TransactionDefinition,TransactionDefinition可以用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等等。
        DefaultTransactionDefinition  transactionDefinition = new DefaultTransactionDefinition();
        transactionDefinition.setTimeout(10);
        //3.创建TransactionTemplate对象
        TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager,transactionDefinition);/**
         * 4.通过TransactionTemplate提供的方法执行业务操作
         * 主要有2个方法:
         * (1).executeWithoutResult(Consumer<TransactionStatus> action):没有返回值的,需传递一个Consumer对象,在accept方法中做业务操作
         * (2).<T> T execute(TransactionCallback<T> action):有返回值的,需要传递一个TransactionCallback对象,在doInTransaction方法中做业务操作
         * 调用execute方法或者executeWithoutResult方法执行完毕之后,事务管理器会自动提交事务或者回滚事务。
         * 那么什么时候事务会回滚,有2种方式:
         * (1)transactionStatus.setRollbackOnly();将事务状态标注为回滚状态
         * (2)execute方法或者executeWithoutResult方法内部抛出异常
         * 什么时候事务会提交?
         * 方法没有异常 && 未调用过transactionStatus.setRollbackOnly();
         */
        transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
            @Override
            public void accept(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into t_user (name) values (?)", "transactionTemplate-1");
                jdbcTemplate.update("insert into t_user (name) values (?)", "transactionTemplate-2");
            }
        });
        System.out.println("after:" + jdbcTemplate.queryForList("SELECT * from t_user"));
    }


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
9月前
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
9月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
9月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
788 2
|
9月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
806 8
|
10月前
|
人工智能 Java 机器人
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
Spring AI Alibaba集成Ollama,基于Java构建本地大模型应用,支持流式对话、knife4j接口可视化,实现高隐私、免API密钥的离线AI服务。
7382 2
基于Spring AI Alibaba + Spring Boot + Ollama搭建本地AI对话机器人API
存储 JSON Java
979 0
|
10月前
|
SQL Java 关系型数据库
Spring事务传播机制:7种姿势教你玩转"事务接力赛"
事务传播机制是Spring框架中用于管理事务行为的重要概念,它决定了在方法调用时事务如何传递与执行。通过7种传播行为,开发者可以灵活控制事务边界,适应不同业务场景。例如:REQUIRED默认加入或新建事务,REQUIRES_NEW独立开启新事务,NESTED支持嵌套回滚等。合理使用传播机制不仅能保障数据一致性,还能提升系统性能与健壮性。掌握这“七种人格”,才能在复杂业务中游刃有余。
|
10月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
2989 0
|
10月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
931 0
|
11月前
|
Java 应用服务中间件 开发者
Spring Boot 技术详解与应用实践
本文档旨在全面介绍 Spring Boot 这一广泛应用于现代企业级应用开发的框架。内容将涵盖 Spring Boot 的核心概念、核心特性、项目自动生成与结构解析、基础功能实现(如 RESTful API、数据访问)、配置管理以及最终的构建与部署。通过本文档,读者将能够理解 Spring Boot 如何简化 Spring 应用的初始搭建和开发过程,并掌握其基本使用方法。
775 2