@Transactional 注解的错误使用场景
2024-09-13 13:19 阅读(246)

不必要的使用场景

1. 无需事务的方法:

问题描述:在只包含查询或只读操作的方法上使用@Transactional是不必要的,因为这些操作不需要事务管理。

示例代码:

@Transactional
public String testQuery() {
    standardBak2Service.getById(1L);
    return "testB";
}

详细说明:虽然在这些方法上添加@Transactional不会对系统造成太大影响,但从代码规范和性能优化的角度来看,应该避免不必要的事务开销。


2. 事务范围过大:


问题描述:将@Transactional注解应用于整个类或抽象类,导致所有方法都被事务管理,这可能导致不必要的性能开销和事务管理的复杂性。

示例代码:

java 代码解读复制代码

@Transactional
public abstract class BaseService {
}

详细说明:应该根据具体方法的业务需求来决定是否使用事务,而不是一概而论地对整个类应用事务管理。


不生效的场景

1. 私有方法:


问题描述:在private方法上使用@Transactional不会生效,因为Spring AOP无法代理私有方法。

示例代码:

java 代码解读复制代码

@Transactional
private String testMerge() {
    testAService.testA();
    testBService.testB();
    return "ok";
}

详细说明:Spring AOP通过代理方式实现@Transactional注解的功能,而私有方法无法被代理,因此事务管理不会生效。


2. final和static方法:


问题描述:在final和static方法上使用@Transactional不会生效,因为这些方法无法被Spring代理。

示例代码:

@Transactional
public static void b() {
}
@Transactional
public final void b() {
}

详细说明:static方法属于类而非实例,而final方法不能被子类重写,因此Spring无法通过代理这些方法来实现事务管理。


3. 同类内部方法调用:


问题描述:在同一个类内部调用其他方法时,事务不会生效,因为这种调用不经过Spring代理。

示例代码:

@Transactional
public String testMerge() {
    a();
    b();
    return "ok";
}
public void a() {
    standardBakService.save(testAService.buildEntity());
}
public void b() {
    standardBak2Service.save(testBService.buildEntity2());
    throw new RuntimeException("b error");
}

详细说明:为了确保事务生效,应该避免在同一个类的内部方法之间直接调用,而是通过Spring容器管理的代理对象来调用。


4. Bean未被Spring管理:


问题描述:如果Bean没有被Spring管理,@Transactional不会生效。

示例代码:

java 代码解读复制代码

@Service
public class TestBService {
    @Transactional
    public String testB() {
        standardBak2Service.save(entity2);
        return "testB";
    }
}

详细说明:确保Bean被Spring管理,可以通过加上@Service、@Component等注解来实现。

5. 异步线程调用:

问题描述:在异步线程中调用事务方法通常不会触发事务回滚。

示例代码:

@Transactional
public String testMerge() {
    testAService.testA();
    new Thread(() -> {
        try {
            testBService.testB();
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }).start();
    return "ok";
}

详细说明:Spring的事务管理依赖于线程本地存储(ThreadLocal),在异步线程中,事务上下文不会被自动传递,因此事务管理不会生效。


不回滚的场景

1. 错误的传播属性:


问题描述:@Transactional的propagation属性配置错误可能导致事务不回滚。

示例代码:

@Transactional(propagation = Propagation.REQUIRED)
public String testMerge() {
    testAService.testA();
    testBService.testB();
    return "ok";
}

详细说明:理解不同propagation属性的含义和适用场景,确保事务在预期的范围内生效。

2. 异常被捕获:

问题描述:在方法内部捕获异常并处理,而不重新抛出,会导致事务无法回滚。

示例代码:

@Transactional
public String testMerge() {
    try {
        testAService.testA();
        testBService.testB();
    } catch (Exception e) {
        log.error("testMerge error:{}", e);
    }
    return "ok";
}

详细说明:为了确保事务能够正确回滚,应该在捕获异常后重新抛出一个RuntimeException或Error类型的异常。


3. 事务无法捕获的异常:


问题描述:默认情况下,事务只回滚RuntimeException及其子类和Error类型的异常。其他类型的异常,如SQLException,不会触发回滚。

示例代码:

@Transactional(rollbackFor = Exception.class)
public String testMerge() throws Exception {
    try {
        testAService.testA();
        testBService.testB();
    } catch (Exception e) {
        log.error("testMerge error:{}", e);
        throw new Exception(e);
    }
    return "ok";
}

详细说明:通过在@Transactional注解中指定rollbackFor属性,可以扩大事务回滚的范围,包括非运行时异常。

返回顶部