@Transactional(事务讲解)和springboot 整合事务

概述 事务在编程中分为两种:声明式事务处理和编程式事务处理

编程式事务处理:编码方式实现事务管理,常与模版类TransactionTemplate(推荐使用) 在业务代码中实现事务。

可知编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现。

声明式事务处理:

声明式事务实现方式主要有2种,一种为通过使用Spring的<tx:advice>定义事务通知与AOP相关配置实现,另为一种通过@Transactional实现事务管理实现,下面详细说明2种方法如何配置,已经相关注意点

1)方式一,配置文件如下

1)方式二(推荐),介绍如下

@Transactional 是声明式事务管理 编程中使用的注解

添加位置

接口实现类或接口实现方法上,而不是接口类中
访问权限:public 的方法才起作用

@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解
系统设计:将标签放置在需要进行事务管理的方法上,而不是不假思索的放置在接口实现类上( 接口中所有方法都需要进行事务管理,但其实并不需要,如只读的接口就不需要事务管理,但是 由于配置了@Transactional就需要AOP拦截及事务的处理,影响系统性能)

方法上注解属性会覆盖类注解上的相同属性,当接口与接口中方法上同时带有@Transactional注解时

错误使用:

接口中A、B两个方法,A无@Transactional标签,B有,上层通过A间接调用B,此时事务不生效
接口中异常(运行时异常)被捕获而没有被抛出
默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚
可通过 @Transactional rollbackFor进行配置
多线程下事务管理

因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean
在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制
一个使用了@Transactional 的方法,如果方法内包含多线程的使用,方法内部出现异常,不会回滚线程中调用方法的事务

声明式事务管理实现方式:

  • 基于tx和aop名字空间的xml配置文件

基于@Transactional注解
@Transactional实质是使用了JDBC的事务来进行事务控制的
@Transactional基于Spring的动态代理的机制
@Transactional实现原理
1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令[不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接connection逻辑上新建一个会话session;DataSource与TransactionManager配置相同的数据源)
2)事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

声明式事务的管理实现本质:
事务的两种开启方式
显示开启start transaction | begin,通过 commit | rollback 结束事务
关闭数据库中自动提交 autocommit set autocommit = 0;MySQL 默认开启自动提交;通过手动提交或执行回滚操作来结束事务
Spring 关闭数据库中自动提交:在方法执行前关闭自动提交,方法执行完毕后再开启自动提交

问题:

关闭自动提交后,若事务一直未完成,即未手动执行 commit 或 rollback 时如何处理已经执行过的SQL操作?

C3P0默认的策略是回滚任何未提交的事务
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互
------------------------------------------ --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------

spring事务特性

  • spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口


  • 事务的隔离级别:是指若干个并发
    的事务之间的隔离程度

    @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用

    @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)

    @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)

    @Transactional(isolation = Isolation.SERIALIZABLE):串行化

    事务传播行为:如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为

    TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
    TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
    TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
    TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
    TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
    --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------

    @Transactional 属性配置

    说明:
    value :主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2.然后,用户可以根据这个参数来根据需要指定特定的txManager.
    value 适用场景:在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的
    REQUIRED_NEW和NESTED两种不同的传播机制的区别
    REQUIRED_NEW:内部的事务独立运行,在各自的作用域中,可以独立的回滚或者提交;而外部的事务将不受内部事务的回滚状态影响
    ESTED的事务,基于单一的事务来管理,提供了多个保存点。这种多个保存点的机制允许内部事务的变更触发外部事务的回滚。而外部事务在混滚之后,仍能继续进行事务处理,即使部分操作已经被混滚。 由于这个设置基于JDBC的保存点,所以只能工作在JDBC的机制
    rollbackFor : 让受检查异常回滚;即让本来不应该回滚的进行回滚操作
    noRollbackFor :忽略非检查异常;即让本来应该回滚的不进行回滚操作

    嵌套事务
    带有事务的方法调用其他事务的方法,此时执行的情况取决配置的事务的传播属性

    PROPAGATION_REQUIRES_NEW :
    启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
    PROPAGATION_NESTED :
    如果外部事务 commit, 嵌套事务也会被 commit;如果外部事务 roll back, 嵌套事务也会被 roll back 。
    开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交
    关于Spring的事务Transactional,锁同步,并发线程
    --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------

    spring事务回滚规则
    指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常
    spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务
    默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
    用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    注意事项:
    @Transactional 使用位置 类上方、方法上方
    Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效
    当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

    方法的访问权限为 public
    @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常

    默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰
    例如一:同一个类中方法,A方法未使用此标签,B使用了,C未使用,A 调用 B , B 调用 C ;则外部调用A之后,B的事务是不会起作用的
    例如二:若是有上层(按照 Controller层、Service层、DAO层的顺序)由Action 调用 Service 直接调用,发生异常会发生回滚;若间接调用,Action 调用 Service 中 的A 方法,A无@Transactional 注解,B有,A调用B,B的注解无效
    --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------

    其他
    事务方法的嵌套调用会产生事务传播
    spring 的事务管理是线程安全的
    父类的声明的@Transactional会对子类的所有方法进行事务增强;子类覆盖重写父类方式可覆盖其@Transactional中的声明配置
    类名上方使用@Transactional,类中方法可通过属性配置覆盖类上的@Transactional配置;比如:类上配置全局是可读写,可在某个方法上改为只读
    源码阅读
    如果程序代码中由于历史原因遗留了接口内部方法调用的错误实现,可以用AspectJ 取代 Spring AOP 代理
    透彻的掌握 Spring 中@transactional 的使用
    Spring @Transactional工作原理详解

  • --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------
  • 参考资料
    spring的@Transactional注解详细用法
    Spring中@Transactional用法深度分析之一
    @Transactional事务几点注意
    spring @Transactional注解参数详解
    深入分析@Transactional的用法
    Spring @Transactional (一)
    Spring @Transactional工作原理
    @Transactional注解工作原理
    DBMS
    c3p0
    JNDI
  • --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- 
    多线程事务管理
    描述

    因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean
    在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制
    解决

    如果方法中调用多线程
    方法主题的事务不会传递到线程中
    线程中可以单独调用Service接口,接口的实现方法使用@Transactional,保证线程内部的事务
    多线程实现的方法
    使用异步注解@Async的方法上再加上注解@Transactional,保证新线程调用的方法是有事务管理的
    原理

    Spring中事务信息存储在ThreadLocal变量中,变量是某个线程上进行的事务所特有的(这些变量对于其他线程中发生的事务来讲是不可见的,无关的)
    单线程的情况下,一个事务会在层级式调用的Spring组件之间传播
    在@Transactional注解的服务方法会产生一个新的线程的情况下,事务是不会从调用者线程传播到新建线程的
    参考资料

    Spring和线程:事务
    spring 多线程事务的问题
    --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------

    springBoot中使用事务

  • 1.在启动类上开启事务管理
  • @SpringBootApplication@MapperScan("net.xdclass.xdvideo.mapper")//开启事务管理@EnableTransactionManagementpublic class XdvideoApplication {
    
    public static void main(String[] args) {      SpringApplication.run(XdvideoApplication.class, args);   }}
  • 2在方法上或者类上开启事务
    @Transactional(propagation = Propagation.REQUIRED) 或者加上其他属性 在上面介绍的有
  • 参考博客:  https://blog.csdn.net/mingyundezuoan/article/details/79017659

原文地址:https://www.cnblogs.com/xiaowangbangzhu/p/10373081.html

时间: 2024-07-30 14:31:32

@Transactional(事务讲解)和springboot 整合事务的相关文章

小D课堂【SpringBoot】数据库操作之整合Mybaties和事务讲解

========================8.数据库操作之整合Mybaties和事务讲解 5节课================================ 加入小D课堂技术交流答疑群:Q群:699347262 1.SpringBoot2.x持久化数据方式介绍 简介:介绍近几年常用的访问数据库的方式和优缺点 1.原始java访问数据库 开发流程麻烦 1.注册驱动/加载驱动 Class.forName("com.mysql.jdbc.Driver") 2.建立连接 Connec

SpringBoot整合Shiro 涉及跨域和@Cacheable缓存/@Transactional事务注解失效问题(五)

1. 跨域(多出现在前后端分离项目中) (1) 跨域介绍可参考:跨域(CORS) (2) SpringBoot中解决跨域方式有: A. 使用@CrossOrigin注解: B. 实现Filter类,重写doFilter方法 package com.ruhuanxingyun.config; import cn.hutool.core.util.StrUtil; import org.springframework.context.annotation.Configuration; import

数据库操作之整合Mybaties和事务讲解 5节课

1.SpringBoot2.x持久化数据方式介绍          简介:介绍近几年常用的访问数据库的方式和优缺点 1.原始java访问数据库             开发流程麻烦             1.注册驱动/加载驱动                 Class.forName("com.mysql.jdbc.Driver")             2.建立连接                 Connection con = DriverManager.getConnec

springboot(整合事务和分布式事务)

springboot +mybatis 单数据源,事务 事务:简单理解指的是一组操作,里面包含许多个单一的逻辑,只要有一个逻辑没有执行成功 ,那么都算失败.所有的数据都回归到最初的状态(回滚) 代码实例: @RequestMapping("/testTranSctional") @ResponseBody public void testTranSctional(String name, Integer age) { userMapperTest01.insert(name, age)

RocketMQ 整合SpringBoot发送事务消息

环境 jdk: 8u22rocketmq: rocketmq-all-4.5.2-bin-releasespringboot: 2.1.6.RELEASErocketmq-springboot: 2.0.3 发送流程(事务消息) Rocket发送事务消息:1.由producer发送prepare(半消息)给MQ的broker2.prepare消息发送成功以后执行本地业务(本地事务),根据本地事务执行结果手动返回相应状态(RocketMQLocalTransactionState.COMMIT.R

springboot mybatis 事务管理

本文主要讲述springboot提供的声明式的事务管理机制. 一.一些概念 声明式的事务管理是基于AOP的,在springboot中可以通过@Transactional注解的方式获得支持,这种方式的优点是: 1)非侵入式,业务逻辑不受事务管理代码的污染. 2)方法级别的事务回滚,合理划分方法的粒度可以做到符合各种业务场景的事务管理. 本文使用目前最常用的mybatis框架来配置springboot的事务管理机制.下面进入配置方法介绍. 二.springboot mybatis事务配置 1.看一下

Spring中的事物管理,用 @Transactional 注解声明式地管理事务

事物: 事务管理是企业级应用程序开发中必不可少的技术,  用来确保数据的 完整性和 一致性. 事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 事务的四个关键属性: 原子性:事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用. 一致性:一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中. 隔离性:可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来,

spring + mybatis + 多数据源整合事务

1.核心思想,spring提供了一个DataSource的子类,该类支持多个数据源 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 该类的源码如下: org.springframework.jdbc.datasource.lookup; java.sql.Connection; java.sql.SQLException; java.util.HashMap; java.util.Iterator; jav

Transactional replication-如何跳过一个事务

在transactional replication, 经常会遇到数据同步延迟的情况.有时候这些延迟是由于在publication中执行了一个更新,例如update ta set col=? Where ?,这个更新包含巨大的数据量.在subscription端,这个更新会分解成多条命令(默认情况下每个数据行一个命令),应用到subscription上. 不得已的情况下,我们需要跳过这个大的事务,让replication继续运行下去. 现在介绍一下transactional replicatio