1. 多线程调用
两个方法在不同线程中,获取到的数据库连接不一样,从而是两个不同的事务。
public class TestService {
@Autowired
private Test2Service test2Service;
@Transactional
public void test(){
new Thread(()->{
test2Service.test();
}).start();
}
}
@Service
public class Test2Service{
@Transactional
public void test(){
// do do something
}
}
2. 非运行时异常
在Spring事务中,默认情况下只会回滚RuntimeException(运行时异常)和Error(错误),其他异常不会回滚。
@Service
public class TestService {
@Transactional
public void test(){
// do do something
throw new Exception("")
}
}
3. 指定了异常
如果指定了异常,则其他异常不会回滚
@Service
public class TestService {
@Transactional(rollbackFor = XXXXException.class)
public void test(){
// do do something
throw new Exception("")
}
}
4. 捕获了异常
因为没有抛出异常,所以不会回滚
@Service
public class TestService {
@Transactional
public void test(){
try {
// do do something
}catch (Exception e) {
e.printStackTrace();
}
}
}
5. 错误使用传播机制
我们在使用@Transactional注解时,是可以指定propagation参数的。
该参数的作用是指定事务的传播特性,spring目前支持7种传播特性:
REQUIRED 如果上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
MANDATORY 如果当前上下文中存在事务,否则抛出异常。
REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
如下,test2Service的事务不受TestService管控
public class TestService {
@Autowired
private Test2Service test2Service;
@Transactional
public void test(){
test2Service.test();
}
}
@Service
public class Test2Service{
@Transactional(propagation = Propagation.NEVER)
public void test(){
// do do something
}
}
6. 类未被Spring管理
没有将该类交给spring管理
//@Service
public class TestService {
@Transactional
public void test(){
try {
// do do something
}catch (Exception e) {
e.printStackTrace();
}
}
}
7. static修饰的方法
@Transactional的原理是spring使用CGLIB或者JDK动态代理的字节码增强技术对原始类方法进行了增强,在次过程过程中会生成一个新的类对象,但static方法是类的,所以无法对staic的方法增强,所以无法使用@Transactional,请看下边例子
public class Father {
public static void eat(){
System.out.println("爸爸吃");
}
}
public class Son extends Father{
public static void eat(){
System.out.println("儿子吃");
}
public static void main(String[] args) {
//类型是Father 所以输出是爸爸吃,这就类似spring生成的对象 。
//类型是原本的对象Father ,代理生成对象是Son
Father f= new Son();
f.eat();
//类型是Son 所以输出是儿子吃
Son s= new Son();
s.eat();
}
}
8. 调用本类的其他方法
执行test()方法时test2()的事务不会生效,因为spring会生成两个TestService对象,一个是原本TestService,一个是代理增强后的TestServiceProxy(为了方便取个名),结构类似下面代码。
public class TestService{
public void test(){
test2();
}
@Transactional
public void test2(){
// do do something
}
}
public class TestServiceProxy{
TestService testService;
public void test(){
//如果test有事务,事务开始
testService.test();
//如果test有事务事务结束
}
public void test2(){
//如果test有事务,事务开始
testService.test2();
//如果test有事务事务结束
}
}
执行过程:TestServiceProxy.test() --> TestService.test() --> TestService.test2()
因为最终执行的是 TestService.test2() ,所以事务无效。
9. final修饰的方法
因为final方法无法重写,所以CGLIB和JDK动态代理无法重写该方法对其增加,所以无法使用@Transactional。
10. 非public修饰的方法
如果方法不是public,Spring事务也会失败,因为Spring的事务管理源码AbstractFallbackTransactionAttributeSource中有判断computeTransactionAttribute()。如果目标方法不是公共的,则TransactionAttribute返回null。所以无法使用@Transactional
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
11. 其他
代码没有开启事务、数据库不支持事务。