Spring @Transactional 事务机制
几个概念要清楚:事务的传播机制,事务的边界
工作原理
运行配置@Transactional注解的测试类的时候,具体会发生如下步骤
1)事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令[不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
2)事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象
@Transactional不生效的情况
- @Transactional 在private 方法是不生效
- 在同一个bean里,嵌套的public方法@Transactional 也不生效
解决方法
简单粗暴,有需要就在调用service中就开@Transactional,如果实在不行,像里面有调用第三方资源,唔想事务开得太久,可以考虑新建另外一个service去开@Transactional。
那么@Transactional如何工作?
实现了EntityManager接口的持久化上下文代理并不是声明式事务管理的唯一部分,事实上包含三个组成部分:
1. EntityManager Proxy本身
2. 事务的切面
3. 事务管理器
事务的切面
事务的切面是一个“around(环绕)”切面,在注解的业务方法前后都可以被调用。实现切面的具体类是TransactionInterceptor。
事务的切面有两个主要职责:
1. 在’before’时,切面提供一个调用点,来决定被调用业务方法应该在正在进行事务的范围内运行,还是开始一个新的独立事务。
2. 在’after’时,切面需要确定事务被提交,回滚或者继续运行。
3. 在’before’时,事务切面自身不包含任何决策逻辑,是否开始新事务的决策委派给事务管理器完成。
事务管理器
事务管理器需要解决下面两个问题:
新的Entity Manager是否应该被创建?
是否应该开始新的事务?
这些需要事务切面’before’逻辑被调用时决定。事务管理器的决策基于以下两点:
1. 事务是否正在进行
2. 事务方法的propagation属性(比如REQUIRES_NEW总要开始新事务)
如果事务管理器确定要创建新事务,那么将:
1. 创建一个新的entity manager
2. entity manager绑定到当前线程
3. 从数据库连接池中获取连接
4. 将连接绑定到当前线程
特点:
1. 使用ThreadLocal变量将entity manager和数据库连接都绑定到当前线程。
2. 事务运行时他们存储在线程中,当它们不再被使用时,事务管理器决定是否将他们清除。
3. 程序的任何部分如果需要当前的entity manager和数据库连接都可以从线程中获取。
EntityManager proxy
EntityManager proxy(前面已经介绍过)就是谜题的最后一部分。当业务方法调用entityManager.persist()时,这不是由entity manager直接调用的。
而是业务方法调用代理,代理从线程获取当前的entity manager,前面介绍过事务管理器将entity manager绑定到线程。
了解了@Transactional机制的各个部分,我们来看一下实现它的常用Spring配置。
根据上面所述,我们所使用的客户代码应该具有如下能力:
1)每次执行数据库命令的时候
如果在事务的上下文环境中,那么不直接创建新的connection对象,而是尝试从DataSource实例的某个与DataSourceTransactionManager相关的某处容器中获取connection对象;在非事务的上下文环境中,直接创建新的connection对象
2)每次执行完数据库命令的时候
如果在事务的上下文环境中,那么不直接关闭connection对象,因为在整个事务中都需要使用该connection对象,而只是释放本次数据库命令对该connection对象的持有;在非事务的上下文环境中,直接关闭该connection对象
在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。
Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked
如果遇到checked意外就不回滚。
如何改变默认规则:
1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
4 如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error。
注意:
如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。
@Transaction的属性
@Transactional之value
value这里主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2。
然后,用户可以根据这个参数来根据需要指定特定的txManager。
什么情况下使用?
比如在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的。
@Transactional之propagation
Propagation支持7种不同的传播机制:
REQUIRED
业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.。
SUPPORTS
如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
MANDATORY
只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
REQUIRES_NEW
业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
NOT_SUPPORTED
声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
NEVER
声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效.
@Transactional之isolation
隔离级别所要解决的问题是在应用程序中,存在多个事务同时在运行之时,需要解决和处理好的问题。那首先来看看,一般会出现哪些问题呢?先来看看吧。
脏读(dirty read)
一个事物更新了数据库中的某些数据,另一个事物读取了这些数据,这时前一个事物由于某些原因回滚了,那么第二个事物读取的数据就是“脏数据”
不可重复读(non-repeatable read)
一个事物需要两次查询同一数据,但两次查询中间可能有另外一个事物更改了这个数据,导致前一个事物两次读出的数据不一致。
幻读 (phantom read)
一个事物两次查询同一个表,但两次查询中间可能有另外一个事物又向这个表中插入了一些新数据,导致前一个事物的两次查询不一致
@Transactional中Isolation有具备的值
- DEFAULT 使用各个数据库默认的隔离级别
- Read Uncommited :读未提交数据( 会出现脏读,不可重复读,幻读 )
- Read Commited :读已提交的数据(会出现不可重复读,幻读)
- Repeatable Read :可重复读(会出现幻读)
- Serializable :串行化
其与事务中容易出现的问题对应关系具体如下
具体如何来设置具体的隔离界别,则依据业务系统具体可以容忍的程度而定。Serializable最为严格,然而效率最低;Read Uncommited效率最高,但是容易出现各种问题,中间的2个级别介于二者之间,故本质上是效率与出错概率的平衡与妥协。
@Transactional之timeout
用于设置事务处理的时间长度,阻止可能出现的长时间的阻塞系统或者占用系统资源。单位为秒。如果超时设置事务回滚,并抛出TransactionTimedOutException异常。
spring事务超时 = 事务开始时到最后一个Statement创建时时间 + 最后一个Statement的执行时超时时间(即其queryTimeout)。
关于事务超时时间的计算可以参考:http://jinnianshilongnian.iteye.com/blog/1986023
@Transactional之readOnly
默认情况下是false,可以显示指定为true, 告诉程序该方法下使用的是只读操作,如果进行其他非读操作,则会跑出异常;这个紧紧适用于只有readOnly标识的情况下,当其与propagation机制同时使用之时,则会出现只读设置被覆盖的情况,比如在required的情况下。在使用 REQUIRED 传播模式时,会抛出一个只读连接异常。使用 JDBC 时是这样。使用基于 ORM 的框架时,只读标志只是对数据库的一个提示,并且一条基于 ORM 框架的指令(本例中是 hibernate)将对象缓存的 flush 模式设置为 NEVER,表示在这个工作单元中,该对象缓存不应与数据库同步。不过,REQUIRED 传播模式会覆盖所有这些内容,允许事务启动并工作,就好像没有设置只读标志一样。
具体的详细内容可以参考: http://robinsoncrusoe.iteye.com/blog/825531
@Transactional之rollbackForClassName/rollbackFor
Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked,如果遇到checked意外就不回滚。
用来指明回滚的条件是哪些异常类或者异常类名。用法比较简单,这些不再赘述。
@Transactional之noRollbackForClassName/noRollbackFor
作用雷同于8, 用来指明在抛出特定异常的情况下,不进行数据库的事务回滚操作。
参考:
http://www.importnew.com/12300.html
相关推荐
在Spring框架中,`@Transactional`注解是用于标记事务管理的重要工具,它使得开发者能够方便地在代码中声明式地控制事务的边界。本文将深入解析`@Transactional`的事务回滚机制,并通过实例来详细讲解其工作原理,...
理解`@Transactional`的工作原理和Spring AOP代理机制,以及与EntityManager的关系,可以帮助我们更好地诊断和解决问题。 总的来说,`@Transactional`提供了声明式的事务管理,极大地简化了事务相关的代码,同时...
Spring框架中的@Transactional注解是用来实现事务管理的,但是有时候我们可能会遇到@Transactional注解无效的情况。在这篇文章中,我们将 introducethe 解决方案,并通过示例代码对其进行详细的介绍。 首先,让我们...
在Java编程中,`@Transactional`注解是Spring框架提供的一种事务管理机制,它使得开发者能够在方法级别方便地声明事务边界。然而,在某些特定情况下,`@Transactional`可能会失效,导致事务无法正常工作。以下是一些...
浅谈Spring中@Transactional事务回滚及示例 @Transactional是Spring Framework中的一种事务管理机制,用于管理数据库事务。它可以使得数据库操作更加安全和可靠。本文将详细介绍@Transactional的使用场景、checked...
2. 默认回滚机制:Spring 基于注解的声明式事物 @Transactional 默认情况下只会对运行期异常(java.lang.RuntimeException 及其子类)和 Error 进行回滚。 3. 数据库引擎支持:数据库引擎要支持事务,使用 InnoDB。 ...
Java注解@Transactional事务类内调用不生效问题及解决办法 Java注解@Transactional是Java中的一种注解,主要用于标记事务边界。然而,在某些情况下,@Transactional注解可能不会生效,例如在同一个类中的方法调用...
@Transactional 注解是 Spring Framework 提供的一种机制,用于管理事务。在 Spring Boot 应用程序中,我们可以使用 @Transactional 注解来声明事务。例如,在 StudentDao 类中,我们使用 @Transactional 注解来...
在Spring框架中,`@Transactional`注解是事务管理的核心组件,它允许开发者在方法级别声明事务边界。本文将深入探讨这个注解的工作原理、如何...这有助于理解Spring事务管理机制的运作方式,从而提升开发和维护效率。
总之,理解Spring的代理机制和事务、异步执行的工作原理是解决此类问题的关键。在开发过程中,应尽量避免出现复杂的循环依赖,特别是当涉及到事务和异步处理时,以确保代码的稳定性和可维护性。
@Transactional 注解的工作原理是通过代理模式和 AOP 机制来实现事务管理的。首先,通过 <tx:annotation-driven/> 配置,Spring 容器注册了一个 InfrastructureAdvisorAutoProxyCreator 类,用于创建代理对象。然后...
Spring 框架提供了声明式事务处理机制,通过使用@Transactional 注解来标注需要事务处理的方法。这样,业务代码中不需要再关心事务的处理,Spring 框架将自动处理事务的提交和回滚。 三、@Transactional 注解 @...
总的来说,了解和掌握Spring的事务管理机制以及如何在JUnit测试中使用@Transactional注解,对于Java开发者来说是非常重要的技能。这不仅可以帮助你在开发过程中确保数据的一致性,也能在单元测试中有效地验证代码的...
Spring 事务使用 AOP 的机制实现,会在@Transactional 注解修饰的方法前后分别织入开启事务的逻辑,以及提交或回滚的逻辑。@Transactional 可以修饰在方法或者类上,区别就在于修饰于类上的,会对该类下符合条件的...
本资源主要介绍基于Spring JDBC的事务管理,包括事务的定义、使用 @Transactional 注解、Spring JDBC的事务管理机制、事务的ACID特性、事务的传播、事务的隔离等内容。 事务的定义 事务是一种数据库中能够保证一...
最后,Spring通过AOP(面向切面编程)的动态代理机制实现@Transactional的事务管理。在使用时,需要确保已经启用了Spring的事务管理器,如DataSourceTransactionManager或JtaTransactionManager,并且相关bean已经...
此外,Spring还提供了一种基于AOP(面向切面编程)的机制来处理事务。当带有`@Transactional`注解的方法被调用时,Spring会创建一个代理,这个代理会在方法执行前后自动处理事务的开始、提交或回滚。 总之,Spring...
Spring的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现。例如,JPA事务使用JpaTransactionManager实现,JDBC事务使用DataSourceTransactionManager实现。 在程序...