spring事务管理

编程式事务控制相关对象

自己写代码的方式去控制事务为编程式。不用自己写代码,通过配置让spring去控制称为声明式

PlatformTransactionManager

platformTransactionManager接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法。属于控制行为的

方法说明
TransactionStatus getTransaction(TransactionDefination defination)获取事务的状态信息
void commit(TransactionStatus status)提交事务
void rollback(TransactionStatus status)后滚事务

注:

PlatformTransactionManager是接口类型,不同的Dao层技术则有不同的实现类,例如:Dao层技术是jdbc或mybatis是:org.springframework.jdbc.datasource.DataSourceTransactionManager

TransactionDefinition

TransactionDefinition是事务的定义信息对象,里面有如下方法。属于参数信息

方法说明
int getIsolationLevel()获得事务的隔离级别
int getPropogationBehavior()获得事务的传播行为
int getTimeout()获得超长时间
boolean isReadOnly()是否只读

TransactionStatus

TransactionStatus接口提供的是事务具体的运行状态,方法如下状态

方法说明
boolean hasSavepoint()是否存储回滚点
boolean isCompleted()事务是否完成
boolean isNewTransaction()是否是新事务
boolean isRollbackOnly()事务是否后滚

事务的隔离级别

设置隔离级别,可以决绝事务并发产生的问题,如脏读、不可重复读和虚读

  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED:读未提交
  • ISOLATION_READ_COMMITTED:读已提交
  • ISOLATION_REPEATABLE_READ:重复读
  • ISOLATION_SERIALIZABLE:序列化,可全读,但性能低

事务传播行为

业务在调用业务方法时他们之间的事务统一性的问题

  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  • 超时时间:默认值是-1,没有超时限制。如果没有,以秒为单位进行设置
  • 是否只读:建议查询时设置为只读

基于xml的声明式事务控制

【spring_tx】

声明式事务:采用声明的方式来处理事务

tx是事务的命名空间

注:spring声明式事务控制底层就是AOP

bean配置

<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
    <property name="user" value="${jdbc.name}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountDao" class="com.lhc.dao.impl.AccountDaoImpl">
    <property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!--目标对象 内部的方法就是切点-->
<bean id="accountService" class="com.lhc.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--通知  事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--配置事务的aop织入-->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lhc.service.impl.*.*(..))"></aop:advisor>
</aop:config>

事务管理器的配详解

<!--通知  事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
  • transaction-manager是使用平台事务的管理器

  • 那么<tx:attributes>是设置事务的属性信息的

  • <tx:method name=“*”/>是增强的方法,name是方法名,即TransactionDefinition是事务的定义信息对象属于参数信息

    <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"></tx:method>
    

    还有这些属性可配置

基于注解的声明式事务控制@Transactional

注解说明
@Transactional使用在需要控制的事务上。可用在类或方法
  • @Transactional参数说明
    • int getIsolationLevel()获得事务的隔离级别====>isolation=Isolation.READ_COMMITTED
    • int getPropogationBehavior()获得事务的传播行为====>Propagation=Proagation.REQUIRED
    • int getTimeout()获得超长时间====>
    • boolean isReadOnly()是否只读====>
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    String[] label() default {};

    /** 事务传播机制行为 */
    Propagation propagation() default Propagation.REQUIRED;

    /** 事务隔离级别 */
    Isolation isolation() default Isolation.DEFAULT;

    /** 超时时间/秒,如果超过这个时间还没执行完就回滚 */
    int timeout() default -1;

    String timeoutString() default "";

    /** 只读模式 */
    boolean readOnly() default false;

    /** 回滚规则 */
    /*	默认规则
    	检查性异常不会回滚eg:IOException
    	运行时异常才会回滚eg:RuntimeException
    */
    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    /** 指定出现某个异常时,不回滚  */
    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

spring配置文件需要配置事务的注解驱动,否则注解不起作用

<!--事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>

全注解式的事务

@EnableTransactional、@Transactional

@Transactional

当有多个查询语句是,最好使用@Transactional(readOnly=true),保证多条语句查询到的数据都是一样的,不会因其它事务操作了之后而改变

  • 只能在public方法中起作用
  • 自调用事务,事务不生效

事务传播性(这里说的是Spring)

一个事务调用另一个事务,那么另一个事务是以什么样的格式来加入当前这个事务呢,这就是事务的传播性

一般用在子事务中,相对来说是进入其他事务时候的

传播性描述
REQUIRED(spring默认)如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
REQUIRES_NEW创建一个新的事务(独立的事务);如果当前存在事务,则把当前事务挂起
因为是独立的,所以子事务的回滚不会影响到父事务
NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起
NEVER以非事务方式运行,如果当前存在事务,则抛出异常
NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来执行;
如果当前没有事务,则该取值等价于TransactionDefinition.PROPGATION_REQUIRED
子事务会跟着父事务一样回滚
@Transactional(propagation=Propagation.XXXX)

Propagation :  key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。

1: Propagation.REQUIRED(spring默认的)

加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,
ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA
的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被
提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚

2: Propagation.SUPPORTS

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行

3: Propagation.MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

4: Propagation.REQUIRES_NEW

这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,
那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,
如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。

5: Propagation.NOT_SUPPORTED

当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。

6: Propagation.NEVER

不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB.methodB就要抛出异常了。

7: Propagation.NESTED

理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。

事务失效场景

  1. 类中的自调用:spring事务是基于AOP的,只要使用代理对象调用某个方法时,Spring事务才能生效,而在一个方法中调用,使用this.A()时,this并不是代理对象,所以会导致事务失效(@Transactional无效)
    1. 解决方法1:把调用方法拆分到另一个bean中
    2. 解决方法2:自己注入自己
    3. 解决方法3:(当前类)AopContext.currentProxy() + @EnableAspectJAutoProxy(exposeProxy = true)
  2. 方法是private 的:spring事务会基于CGLIB来进行AOP,而CGLIB会基于父子类来实现,子类是代理类,父类是被代理类,如果父类中的某个方法是private的,那么子类就没有办法重写它,也就没有办法额外增加spring事务的逻辑
  3. 方法是final的:原因和private是一样的,也是由于子类不能重写父类中的final方法
  4. 单独的线程调用方法:当mybatis或jdbcTemplate执行SQL时,会从ThreadLocal中去获取数据库连接对象,如果开始事务的线程和执行SQL的线程时同一个,那么就能拿到数据库连接对象,如果不是同一个线程,那就拿不到数据库连接对象,这样,mybatis或jdbcTemplate就会去新建一个数据库连接用来执行SQL,此数据库连接的autocommit为true,那么执行为SQL就会提交,后续再抛异常也就不能回滚之前已经提交了的SQL了
  5. 没加@Configuration注解:如果用SpringBoot基本没有这个问题,但是如果用的Spring,那么可能会有这个问题,这个问题的原因其实也是由于Mybatis或JdbcTemplate会从ThreadLocal中去获取数据库连接,但是ThreadLocal中存储的一个MAP,MAP的key为DataSource对象,value为连接对象,而如果我们没有在AppConfig上添加@Configuration注解的话,会导致MAP中存的DataSource对象和Mybatis和JdbcTemplate中的DataSource对象不相等,从而拿不到数据库连接,导致自己去创建数据库连接了
  6. 异常被吃掉:如果Spring事务没有捕获到异常,那么也就不会回滚了,默认情况下Spring会捕获RuntimeException和Error
  7. 类没有被Spring管理
  8. 数据库不支持事务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值