Spring 事务失效的7种场景
@Transactional(rollbackFor = {异常类型列表})
@EnableTransactionManagement 注解用来启用spring事务自动管理事务的功能,这个注解千万不要忘记写了。
@Transaction 可以用在类上、接口上、public方法上,如果将@Trasaction用在了非public方法上,事务将无效。
spring是通过事务管理器了来管理事务的,一定不要忘记配置事务管理器了,要注意为每个数据源配置一个事务管理器:
spring是通过aop的方式,对需要spring管理事务的bean生成了代理对象,然后通过代理对象拦截了目标方法的执行,在方法前后添加了事务的功能,所以必须通过代理对象调用目标方法的时候,事务才会起效。
看下面代码,大家思考一个问题:当外部直接调用m1的时候,m2方法的事务会生效么?
显然不会生效,因为m1中通过this的方式调用了m2方法,而this并不是代理对象,this.m2()不会被事务拦截器,所以事务是无效的,如果外部直接调用通过UserService这个bean来调用m2方法,事务是有效的,上面代码可以做一下调整,如下,@1在UserService中注入了自己,此时m1中的m2事务是生效的
重点:必须通过代理对象访问方法,事务才会生效。
spring事务回滚的机制:对业务方法进行try catch,当捕获到有指定的异常时,spring自动对事务进行回滚,那么问题来了,哪些异常spring会回滚事务呢?
并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。
也可以自定义回滚的异常类型:
当业务方法抛出异常,spring感知到异常的时候,才会做事务回滚的操作,若方法内部将异常给吞了,那么事务无法感知到异常了,事务就不会回滚了。
如下代码,事务操作2发生了异常,但是被捕获了,此时事务并不会被回滚
1.7、业务和spring事务代码必须在一个线程中
spring事务实现中使用了ThreadLocal,ThreadLocal大家应该知道吧,可以实现同一个线程中数据共享,必须是同一个线程的时候,数据才可以共享,这就要求业务代码必须和spring事务的源码执行过程必须在一个线程中,才会受spring事务的控制,比如下面代码,方法内部的子线程内部执行的事务操作将不受m1方法上spring事务的控制,这个大家一定要注意
2种方式
方式1:看日志
如果你使用了logback或者log4j来输出日志,可以修改一下日志级别为debug模式,可以看到事务的详细执行日志,帮助你定位错误
方式2:调试代码
如果你对源码比较了解,那么你会知道被spring管理事务的业务方法,执行的时候都会被TransactionInterceptor拦截器拦截,会进入到它的invoke方法中,咱们可以在invoke方法中设置一些断点,可以看到详细的执行过程,排错也就比较容易了。
整体上来说,还是需要你深入理解原理,原理了解了,写代码的时候本身就会避免很多坑。
显示推荐内容
spring 事务实现方式有哪些?
***种方式:每个Bean都有一个代理
bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
property name="configLocation" value="classpath:hibernate.cfg.xml" /
property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /
/bean
!-- 定义事务管理器(声明式的事务) --
bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
property name="sessionFactory" ref="sessionFactory" /
/bean
!-- 配置DAO --
bean id="userDaoTarget"
property name="sessionFactory" ref="sessionFactory" /
/bean
bean id="userDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
!-- 配置事务管理器 --
property name="transactionManager" ref="transactionManager" /
property name="target" ref="userDaoTarget" /
property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /
!-- 配置事务属性 --
property name="transactionAttributes"
props
prop key="*"PROPAGATION_REQUIRED/prop
/props
/property
/bean
/beans
第二种方式:所有Bean共享一个代理基类
bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
property name="configLocation" value="classpath:hibernate.cfg.xml" /
property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /
/bean
!-- 定义事务管理器(声明式的事务) --
bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
property name="sessionFactory" ref="sessionFactory" /
/bean
bean id="transactionbase"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true"
!-- 配置事务管理器 --
property name="transactionManager" ref="transactionManager" /
!-- 配置事务属性 --
property name="transactionAttributes"
props
prop key="*"PROPAGATION_REQUIRED/prop
/props
/property
/bean
!-- 配置DAO --
bean id="userDaoTarget"
property name="sessionFactory" ref="sessionFactory" /
/bean
bean id="userDao" parent="transactionbase"
property name="target" ref="userDaoTarget" /
/bean
/beans
第三种方式:使用拦截器
bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
property name="configLocation" value="classpath:hibernate.cfg.xml" /
property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /
/bean
!-- 定义事务管理器(声明式的事务) --
bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
property name="sessionFactory" ref="sessionFactory" /
/bean
bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor"
property name="transactionManager" ref="transactionManager" /
!-- 配置事务属性 --
property name="transactionAttributes"
props
prop key="*"PROPAGATION_REQUIRED/prop
/props
/property
/bean
bean class="org.springframework.aop.framework.***toproxy.BeanNameAutoProxyCreator"
property name="beanNames"
list
value*Dao/value
/list
/property
property name="interceptorNames"
list
valuetransactionInterceptor/value
/list
/property
/bean
!-- 配置DAO --
bean id="userDao"
property name="sessionFactory" ref="sessionFactory" /
/bean
/beans
第四种方式:使用tx标签配置的拦截器
bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
property name="configLocation" value="classpath:hibernate.cfg.xml" /
property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /
/bean
!-- 定义事务管理器(声明式的事务) --
bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
property name="sessionFactory" ref="sessionFactory" /
/bean
tx:advice id="txAdvice" transaction-manager="transactionManager"
tx:attributes
tx:method name="*" propagation="REQUIRED" /
/tx:attributes
/tx:advice
aop:config
aop:pointcut id="interceptorPointCuts"
expression="execution(* com.bluesky.spring.dao.*.*(..))" /
aop:advisor advice-ref="txAdvice"
pointcut-ref="interceptorPointCuts" /
/aop:config
/beans
第五种方式:全注
tx:annotation-driven transaction-manager="transactionManager"/
bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
property name="configLocation" value="classpath:hibernate.cfg.xml" /
property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /
/bean
!-- 定义事务管理器(声明式的事务) --
bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
property name="sessionFactory" ref="sessionFactory" /
/bean
/beans
什么叫做spring的声明式事务
事物管理对于企业应用来说是至关重要的,好使出现异常情况,它也可以保证数据的一致性。
spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务***的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
spring事务特性
spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口
其中TransactionDefinition接口定义以下特性:
事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
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。
事务超时
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
事务只读属性
只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。
“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。
因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可
spring事务回滚规则
指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。还可以编程性的通过setRollbackonly()方法来指示一个事务必须回滚,在调用完setRollbackonly()后你所能执行的唯一操作就是回滚。
关于spring事务和spring事务传播机制和隔离级别的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。