示例 :
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方法,事务不生效
- 通过spring bean调用到savePre方法时,走的是代理对象,但savePre没有开启事务
- 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关键字
- 通过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);
}
}
- 第三种错误姿势,如下,
- 注解@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;
}
以上均是在实际开发中遇到过的场景(有自己遇到的也有阅读别人代码见到的),如有错误,欢迎纠正,也欢迎补充。