返回

从细节处了解Spring中@Transactional注解失效的常见场景

后端

7 种常见场景会导致 @Transactional 注解失效

在使用 Spring 框架进行事务管理时,@Transactional 注解是必不可少的工具。它允许我们轻松地定义事务边界,确保数据操作要么完全成功,要么完全回滚。然而,在某些情况下,@Transactional 注解可能不起作用,导致事务失效。本文将探讨这 7 种常见的场景,帮助你避免这些陷阱,确保你的事务管理始终如一。

1. @Transactional 应用在非 public 方法上

@Transactional 注解只能应用在 public 方法上。如果应用在 protected、private 或 default 方法上,则该注解不会起作用,事务也不会生效。

示例:

@Transactional
private void doSomething() {
    // 事务不会生效
}

2. 方法抛出 unchecked 异常

如果方法抛出 unchecked 异常,例如 NullPointerException 或 ArrayIndexOutOfBoundsException,则事务也会失效。这是因为 unchecked 异常不会导致方法被回滚,而 @Transactional 注解默认情况下只会回滚 checked 异常。

示例:

@Transactional
public void doSomething() {
    throw new NullPointerException();
    // 事务不会回滚
}

3. 方法中调用了带有 @Transactional(propagation = Propagation.REQUIRES_NEW) 注解的方法

如果方法中调用了带有 @Transactional(propagation = Propagation.REQUIRES_NEW) 注解的方法,则新方法将开启一个新的事务,而旧方法的事务将被挂起。如果新方法执行成功,则两个事务都会提交;如果新方法执行失败,则两个事务都会回滚。

示例:

@Transactional
public void doSomething() {
    doSomethingElse();
    // 如果 doSomethingElse() 执行失败,两个事务都会回滚
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomethingElse() {
    throw new RuntimeException();
}

4. 方法中调用了带有 @Transactional(readOnly = true) 注解的方法

如果方法中调用了带有 @Transactional(readOnly = true) 注解的方法,则该方法将以只读方式执行,并且不会开启事务。

示例:

@Transactional
public void doSomething() {
    findSomething();
    // 不开启事务,不会执行任何更新
}

@Transactional(readOnly = true)
public void findSomething() {
    // 只读操作
}

5. 方法中调用了带有 @Transactional(noRollbackFor = Exception.class) 注解的方法

如果方法中调用了带有 @Transactional(noRollbackFor = Exception.class) 注解的方法,则该方法将不会对指定的异常进行回滚。

示例:

@Transactional
public void doSomething() {
    try {
        doSomethingElse();
    } catch (Exception e) {
        // 事务不会回滚
    }
}

@Transactional(noRollbackFor = Exception.class)
public void doSomethingElse() {
    throw new RuntimeException();
}

6. 方法中调用了本地方法或使用了本地代码

如果方法中调用了本地方法或使用了本地代码,则事务也可能失效。这是因为本地代码不受 Java 虚拟机的管理,因此它可以绕过事务机制。

示例:

@Transactional
public void doSomething() {
    System.loadLibrary("native-library");
    // 本地代码不受事务管理
}

7. 使用了 Spring 的声明式事务管理,但没有配置事务管理器

如果使用了 Spring 的声明式事务管理,但没有配置事务管理器,则 @Transactional 注解也不会起作用。这是因为事务管理器是事务管理的关键组件,它负责创建和管理事务。

示例:

<!-- 没有配置事务管理器 -->
<bean id="myService" class="com.example.MyService" />

结论

了解和避免这些会导致 @Transactional 注解失效的常见场景至关重要。通过谨慎地应用这些注解,你可以确保你的事务管理始终如一,避免意外的数据损坏或丢失。

常见问题解答

  1. 如果方法抛出了 checked 异常,事务总是会被回滚吗?

    • 不一定。如果 @Transactional 注解的 rollbackFor 属性设置为 false,则事务将不会回滚。
  2. @Transactional 注解可以应用在接口方法上吗?

    • 不可以。@Transactional 注解只能应用在实现类的方法上。
  3. 如果在方法中嵌套调用了多个带有 @Transactional 注解的方法,会发生什么?

    • 嵌套的事务将与外层事务使用相同的传播行为。
  4. 如何禁用事务?

    • 可以通过将 @Transactional 注解的 value 属性设置为 "PROPAGATION_NEVER" 或 "PROPAGATION_NOT_SUPPORTED" 来禁用事务。
  5. 如果事务管理器配置了多个数据源,会发生什么?

    • 默认情况下,@Transactional 注解将使用默认数据源。可以通过指定 dataSource 属性来覆盖此设置。