@Transactional(事务讲解)和springboot整合事务
概述事务在编程中分为两种:声明式事务处理和编程式事务处理
编程式事务处理:编码⽅式实现事务管理,常与模版类TransactionTemplate(推荐使⽤)在业务代码中实现事务。
可知编程式事务每次实现都要单独实现,但业务量⼤功能复杂时,使⽤编程式事务⽆疑是痛苦的,⽽声明式事务不同,声明式事务属于⽆侵⼊式,不会影响业务逻辑的实现。
声明式事务处理:
声明式事务实现⽅式主要有2种,⼀种为通过使⽤Spring的<tx:advice>定义事务通知与AOP相关配置实现,另为⼀种通过@Transactional实现事务管理实现,下⾯详细说明2种⽅法如何配置,已经相关注意点
1)⽅式⼀,配置⽂件如下
1)⽅式⼆(推荐),介绍如下
//获取连接
21.Connection con = Connection()
3//开启事务
53.执⾏CRUD
6//提交事务/回滚事务
74. conmit() / llback();
8//关闭连接
95. conn.close();
@Transactional 是声明式事务管理编程中使⽤的注解
添加位置
接⼝实现类或接⼝实现⽅法上,⽽不是接⼝类中
访问权限:public 的⽅法才起作⽤
Spring本⾝并不提供事务,⽽是对JDBC事务通过AOP做了封装,隐藏了2和4的操作,简化了JDBC的应⽤。
spring对JDBC事务的封装,是通过AOP动态代理来实现的,在调⽤⽬标⽅法(也就是第3步)前后会通过代理类来执⾏事务的开启、提交或者回滚操作。
注意关键词“动态代理”,这意味着要⽣成⼀个代理类,那么我们就不能在⼀个类内直接调⽤事务⽅法,否则⽆法代理,⽽且该事务⽅法必须是public,如果定义成 protected、private 或者默认可见性,则⽆法调⽤!
忽略这两点,则很容易误⽤spring事务!
系统设计:将标签放置在需要进⾏事务管理的⽅法上,⽽不是不假思索的放置在接⼝实现类上(接⼝中所有⽅法都需要进⾏事务管理,但其实并不需要,如
只读的接⼝就不需要事务管理,但是由于配置了@Transactional就需要AOP拦截及事务的处理,影响系统性能)
⽅法上注解属性会覆盖类注解上的相同属性,当接⼝与接⼝中⽅法上同时带有@Transactional注解时
错误使⽤:spring aop应用场景
接⼝中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所有的事务管理策略类都继承⾃ansaction.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")
//开启事务管理
@EnableTransactionManagement
public class XdvideoApplication {
public static void main(String[] args) {
SpringApplication.run(XdvideoApplication.class, args);
}
}
2在⽅法上或者类上开启事务
@Transactional(propagation = Propagation.REQUIRED) 或者加上其他属性在上⾯介绍的有
参考博客:  blog.csdn/mingyundezuoan/article/details/79017659