关键词

spring中12种@Transactional的失效场景(小结)

下面就来详细讲解“Spring中12种@Transactional的失效场景(小结)”。

首先,需要明确的是,@Transactional是用来控制事务的注解,它可以应用于方法、类或接口上,用来确保在执行该方法时开启了一个事务,并在方法结束时提交或回滚事务。但是,在某些情况下,@Transactional注解可能会失效。下面分别讲解12种@Transactional的失效场景:

  1. 在同一个类中调用@Transactional方法

在同一个类中直接调用被@Transactional注解的方法时,事务不会起作用。这是因为@Transactional是通过动态代理去实现的,而如果在同一个类中调用,动态代理并不会被触发。所以,我们需要通过自我注入的方式调用需要使用事务的方法,这样动态代理就会生效。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserService userService;

    @Transactional
    public void addUserAndLog() {
        userService.addUser();
        // 记录日志
    }

    @Transactional
    public void addUser() {
        // 添加用户
    }

}
  1. 在非public的方法上使用@Transactional

在许多情况下,我们可能会在一个类中定义私有的或受保护的方法。然而,如果我们在这些方法上使用@Transactional注解,那么事务也会失效。因为,Spring只会拦截public方法,而不会拦截非public方法。示例代码如下:

public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        // 添加用户
        addUserLog();
    }

    @Transactional
    private void addUserLog() {
        // 添加用户日志
    }

}
  1. 在非事务方法中调用@Transactional方法

如果一个方法没有被@Transactional注解标记,而它又调用了一个被@Transactional注解标记的方法,那么被调用的方法的事务将不会开启。这是因为,被调用的方法已经失去了@Transactional注解的作用。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        // 添加用户
        addUserLog();
    }

    @Transactional
    public void addUserLog() {
        // 添加用户日志
    }

    public void addUserAndLog() {
        addUser();
    }

}
  1. 在静态方法上使用@Transactional

由于@Transactional注解是作用在实例方法上的,所以它不能用于静态方法中,否则会失效。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        User user = User.builder().username("test").password("123456").build();
        User.save(user);
    }

    @Transactional
    public static void save(User user) {
        // 保存用户
    }

}
  1. @Transactional标注方法的异常被try/catch

如果我们在@Transactional标注的方法中使用try/catch捕获了异常,那么就会导致事务无法回滚。因为一旦我们捕获了异常,它就不会再被传递到Spring事务管理器中,事务管理器就无法识别到异常并进行回滚。示例代码如下:

@Transactional
public void addUser() {
    try {
        // 添加用户
    } catch (Exception e) {
        // 异常捕获
    }
}
  1. @Transactional标注方法的异常类为RuntimeException

如果@Transactional标注的方法中抛出了RuntimeException及其子类的异常,默认情况下事务会回滚,可以通过在异常类上使用@Transaction注解的noRollbackFor属性,来使得事务不回滚。示例代码如下:

@Transactional
public void addUser() {
    throw new RuntimeException("测试异常");
}

@Transactional(noRollbackFor = RuntimeException.class)
public void addUser() {
    throw new RuntimeException("测试异常");
}
  1. @Transactional标注方法和catch块中都抛出异常

如果@Transactional标注的方法中发生了异常,并在catch块中继续抛出了异常,那么事务也会失效。因为catch块中的异常会覆盖原来产生的异常。示例代码如下:

@Transactional
public void addUser() {
    try {
        // 添加用户
    } catch (Exception e) {
        // 处理异常
        throw new RuntimeException("处理异常时出错");
    }
}
  1. @Transactional标注方法所在的类没有被Spring容器管理

@Transactional注解只有在Spring容器管辖的类中才会生效,所以如果@Transactional注解标注在一个未被Spring容器管理的类中,那么它将不起作用。示例代码如下:

public class UserServiceImpl implements UserService {

    @Transactional
    public void addUser() {
        // 添加用户
    }

}
  1. @Transactional标注在类级别上中间加入了非@Transactional的方法,造成事务失效

如果在一个类中,@Transactional注解是针对类级别的,在类中间加了一个没有@Transactional注解的方法,这个非@Transactional方法就会出现事务失效。这是因为,Spring在检测到事务注解时,会动态创建一个代理类进行事务处理,而在代理类中,会通过在方法前后添加事务通知来管理事务。但是,如果我们在被代理的类中间插入了一个方法,Spring就无法保证在代理类中添加事务通知的顺序和正确性了。示例代码如下:

@Service
@Transactional
public class UserServiceImpl implements UserService {

    public void test() {
        // 测试方法
    }

    public void addUser() {
        // 添加用户
    }

}
  1. 使用自调用的方式调用@Transactional方法

如果调用@Transactional方法时使用了自调用的方式,事务也会失效。这是因为自调用会绕过Spring的动态代理。示例代码如下:

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserService userService;

    public void addUserAndLog() {
        addUser();
        this.addUserLog();
    }

    @Transactional
    public void addUser() {
        // 添加用户
    }

    @Transactional
    public void addUserLog() {
        // 添加用户日志
    }

}
  1. 同一个方法内,同时调用多个@Transactional注解的方法,传播属性设置不当

如果在同一个方法内,同时调用多个@Transactional注解的方法,并在这些方法之间使用事务传播属性时,需要特别注意传播属性的设置,否则可能会出现一系列问题,比如事务无法回滚、出现死锁等。示例代码如下:

@Transactional
public void addUser() {
    try {
        // 添加用户
        addUserLog1();
        addUserLog2();
    } catch (Exception e) {
        // 异常处理
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserLog1() {
    // 添加用户日志1
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addUserLog2() {
    // 添加用户日志2
}
  1. 在子线程中调用@Transactional标注的方法

在子线程中调用@Transactional标注的方法是不会生效的,因为Spring的事务管理是基于ThreadLocal来实现的,所以在子线程中,Spring的事务管理器无法将事务的上下文传递到新的线程中。所以,如果需要在子线程中使用事务,可以使用TransactionTemplate或者手动同步事务。示例代码如下:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Transactional
    public void addUser() {
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                // 在子线程中使用事务
                return null;
            }
        });
    }

}

以上就是“Spring中12种@Transactional的失效场景(小结)”的详细攻略。希望对大家有所帮助。

本文链接:http://task.lmcjl.com/news/18990.html

展开阅读全文