Spring事务开启了却不生效

示例 :


public interface TestService {
    /**
     * 保存list
     * @param datas 数据
     */
    void save(List datas);
}
public class TestServiceImpl implements TestService {
    @Transactional
    @Override
    public void save(List datas) {
        //业务处理
    }
}

可能的原因:

1.未开启事务 

springboot开启事务的方式:在config类(一般在启动类)上增加注解@EnableTransactionManagement,或者xml中增加事务管理器配置

2.多个数据源,多个事务管理器

在加载数据源时,为每个数据源声明开启一个事务管理器,并修改事务注解@Transactional("transactionManager名字")指定事务管理器

3.业务处理抛出异常未触发事务回滚

Spring的事务注解默认回滚异常为RuntimeException与Error,未配置rollbackFor,则走默认回滚异常,例如IOException就不会回滚(还可能存在一些自定义的不在RuntimeException或Error范围内的异常)。可修改注解为@Transactional(rollbackFor = Throwable.class)

  •  Spring源码如下:
@Override
public boolean rollbackOn(Throwable ex) {
   return (ex instanceof RuntimeException || ex instanceof Error);
}

4.调用的姿势不正确

Spring声明事务采用的AOP+动态代理实现的(默认是代理),Spring维护的事务bean是个代理对象,做了事务增强的,可见下文 EnableTransactionManagement代码。

  •  第一种错误姿势,如下,通过spring bean调用到savePre方法,savePre再调用带有事务的save方法,事务不生效
    1. 通过spring bean调用到savePre方法时,走的是代理对象,但savePre没有开启事务
    2. savePre调用save,走的是this的实际对象,并非代理对象,没有事务增强,随开启事务但不生效

错误示范1: 

public interface TestService {
    /**
     * 保存list
     * @param datas 数据
     */
    void savePre(List datas);
}
public class TestServiceImpl implements TestService {
    @Transactional
    public void save(List datas) {
        //业务处理
    }
    @Override
    public void savePre(List datas){
        this.save(datas);
    }
}
  •  第二种错误姿势,如下,调用方法的对象并非spring bean,而是通过其他方式生成的,如使用new关键字
    1. 通过new 生成的对象,并非spring 代理的对象,不会有事务增强

错误示例2:

public interface TestService {
    /**
     * 保存list
     * @param datas 数据
     */
    void save(List datas);
}
public class TestServiceImpl implements TestService {
    @Transactional
    @Override
    public void save(List datas) {
        //业务处理
    }
}
/**
* 调用方
*/
public class Caller {
    private static TestService testService = new TestServiceImpl();
    public void save(List datas){
        testService.save(datas);
    }
}
  •  第三种错误姿势,如下,
  1. 注解@Transactional标注在interface方法上,但代理使用的CGLIB

 错误示例3:

public interface TestService {
    /**
     * 保存list
     * @param datas 数据
     */
    @Transactional
    void save(List datas);
}
public class TestServiceImpl implements TestService {
    
    @Override
    public void save(List datas) {
        //业务处理
    }
}
/**
* 调用方
*/
public class Caller {
    @Autowired
    private TestService testService;
    public void save(List datas){
        testService.save(datas);
    }
}

//开启事务设置,设置为CGLIB代理
@EnableTransactionManagement(proxyTargetClass = true)

 


PS:EnableTransactionManagement源码(在springboot中有“坑”,详见 《SpringBoot中修改proxyTargetClass,但事务代理始终为CGLIB》)

proxyTargetClass:只有在 mode = AdviceMode.PROXY 时生效,false时基于java接口代理,true时基于CGLIB代理,也就是false的时候,@Transactional在 接口及接口方法 和 实现类及实现类方法 都生效,true的时候,@Transactional在接口及接口方法不生效,实现类及实现类方法 生效。

mode:PROXY、ASPECTJ两个选择,基于代理与基于切面。PROXY时,通过代理bean事务才能生效。

ps:翻译的不好,见谅,可以自己翻译理解下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    
	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as
	 * opposed to standard Java interface-based proxies ({@code false}). The default is
	 * {@code false}. <strong>Applicable only if {@link #mode()} is set to
	 * {@link AdviceMode#PROXY}</strong>.
	 * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
	 * Spring-managed beans requiring proxying, not just those marked with
	 * {@code @Transactional}. For example, other beans marked with Spring's
	 * {@code @Async} annotation will be upgraded to subclass proxying at the same
	 * time. This approach has no negative impact in practice unless one is explicitly
	 * expecting one type of proxy vs another, e.g. in tests.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate how transactional advice should be applied.
	 * <p><b>The default is {@link AdviceMode#PROXY}.</b>
	 * Please note that proxy mode allows for interception of calls through the proxy
	 * only. Local calls within the same class cannot get intercepted that way; an
	 * {@link Transactional} annotation on such a method within a local call will be
	 * ignored since Spring's interceptor does not even kick in for such a runtime
	 * scenario. For a more advanced mode of interception, consider switching this to
	 * {@link AdviceMode#ASPECTJ}.
	 */
	AdviceMode mode() default AdviceMode.PROXY;

	/**
	 * Indicate the ordering of the execution of the transaction advisor
	 * when multiple advices are applied at a specific joinpoint.
	 * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;

}

 以上均是在实际开发中遇到过的场景(有自己遇到的也有阅读别人代码见到的),如有错误,欢迎纠正,也欢迎补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值