前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >事务传递机制

事务传递机制

原创
作者头像
芥藍
发布2025-03-05 18:53:11
发布2025-03-05 18:53:11
580
举报

@Slf4j

public abstract class SerialNumberBaseService implements SerialNumberService {

   @Override

    public String generate(SerialNumberIdEnum serialNumberIdEnum) {

        List<String> generateList = this.generate(serialNumberIdEnum, 1);

        if (generateList == null || generateList.isEmpty()) {

            throw new BusinessException("cannot generate : " + serialNumberIdEnum.toString());

        }

        return generateList.get(0);

    }

    @Override

    public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {

        SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());

        if (serialNumberInfoBO == null) {

            throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());

        }

        return this.generateSerialNumberList(serialNumberInfoBO, count);

    }

}

@Slf4j

@Service

public class SerialNumberMysqlService extends SerialNumberBaseService {

    @Override

    @Transactional(rollbackFor = Exception.class,transactionManager = "secondaryTransactionManager")

    public List<String> generateSerialNumberList(SerialNumberInfoBO serialNumberInfo, int count) {

        // 获取当前事务状态

        TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();

        log.info("当前事务是否是新事务: {}", status.isNewTransaction());

        log.info("当前事务是否有保存点: {}", status.hasSavepoint());

}

}

解释一下为什么这个事务会失效

在你的代码中,SerialNumberMysqlService 继承了 SerialNumberBaseService,并且在 SerialNumberMysqlService 中重写了 generateSerialNumberList 方法,并为其添加了 @Transactional 注解。然而,事务可能会失效,原因在于 Spring 的事务管理机制和代理机制的特性。

事务失效的原因分析

1. 事务注解的作用范围

Spring 的事务是通过动态代理实现的。只有通过 Spring 代理调用的方法,事务注解才会生效。如果直接在类内部调用方法(绕过代理),事务注解不会生效。

在你的代码中,SerialNumberBaseService 中的 generate 方法调用了 generateSerialNumberList 方法:

代码语言:java
复制
return this.generateSerialNumberList(serialNumberInfoBO, count);

这里直接通过 this 调用了 generateSerialNumberList 方法,绕过了 Spring 的代理机制。因此,事务注解不会生效。

2. 事务注解的继承问题

虽然 SerialNumberMysqlService 继承了 SerialNumberBaseService,但事务注解(@Transactional)不会被继承。事务注解的作用范围仅限于它被定义的方法上。因此,即使 SerialNumberMysqlService 重写了父类的方法并添加了事务注解,父类中的调用仍然不会触发事务管理。

3. 事务传播行为

即使事务注解生效,事务传播行为(Propagation.REQUIRED)决定了事务的共享或独立性。在你的代码中,事务注解被定义在 generateSerialNumberList 方法上,而不是在 generate 方法上。这意味着事务管理仅适用于 generateSerialNumberList 方法,而 generate 方法的调用不会触发事务。

事务失效的具体原因

在你的代码中:

代码语言:java
复制
@Override
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
    SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
    if (serialNumberInfoBO == null) {
        throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
    }
    return this.generateSerialNumberList(serialNumberInfoBO, count); // 直接调用,绕过代理
}

generateSerialNumberList 方法被直接调用,绕过了 Spring 的代理机制。因此,事务注解不会生效。

解决方案

方法 1:将事务注解移到父类的 generate 方法上

将事务注解放在父类的 generate 方法上,确保事务管理生效:

代码语言:java
复制
@Slf4j
public abstract class SerialNumberBaseService implements SerialNumberService {

    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
    public String generate(SerialNumberIdEnum serialNumberIdEnum) {
        List<String> generateList = this.generate(serialNumberIdEnum, 1);
        if (generateList == null || generateList.isEmpty()) {
            throw new BusinessException("cannot generate : " + serialNumberIdEnum.toString());
        }
        return generateList.get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
    public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
        SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
        if (serialNumberInfoBO == null) {
            throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
        }
        return this.generateSerialNumberList(serialNumberInfoBO, count);
    }
}

这样可以确保事务管理生效,因为事务注解直接作用于父类的方法。

方法 2:通过代理调用方法

如果事务注解必须放在子类中,可以通过 Spring 的代理机制调用方法:

代码语言:java
复制
@Override
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
    SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
    if (serialNumberInfoBO == null) {
        throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
    }
    return ((SerialNumberBaseService) AopContext.currentProxy()).generateSerialNumberList(serialNumberInfoBO, count);
}

这里使用了 AopContext.currentProxy() 来获取当前的代理对象,从而确保事务注解生效。注意,使用 AopContext.currentProxy() 需要启用 expose-proxy

代码语言:java
复制
@EnableAspectJAutoProxy(exposeProxy = true)
方法 3:重构代码

将事务逻辑直接放在子类中,避免内部调用:

代码语言:java
复制
@Slf4j
@Service
public class SerialNumberMysqlService extends SerialNumberBaseService {

    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
    public String generate(SerialNumberIdEnum serialNumberIdEnum) {
        List<String> generateList = generate(serialNumberIdEnum, 1);
        if (generateList == null || generateList.isEmpty()) {
            throw new BusinessException("cannot generate : " + serialNumberIdEnum.toString());
        }
        return generateList.get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
    public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
        SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
        if (serialNumberInfoBO == null) {
            throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
        }
        return generateSerialNumberList(serialNumberInfoBO, count);
    }
}

这样可以确保事务管理生效,因为事务注解直接作用于子类的方法。

总结

  • 事务失效的原因:直接调用方法绕过了 Spring 的代理机制,导致事务注解不生效。
  • 解决方案:   1. 将事务注解放在父类的 generate 方法上。   2. 通过 Spring 的代理机制调用方法(如使用 AopContext.currentProxy())。   3. 重构代码,将事务逻辑直接放在子类中。

根据你的需求选择合适的解决方案,以确保事务管理生效。

解释一下 事务注解的作用范围仅限于它被定义的方法上

在 Spring 中,@Transactional 注解的作用范围仅限于它被定义的方法上,这意味着事务管理器只会对被 @Transactional 注解直接修饰的方法生效。这一特性与 Java 的注解继承规则以及 Spring 的事务代理机制有关。

为什么事务注解的作用范围仅限于被定义的方法?

1. 注解的非继承性

在 Java 中,注解默认不会被继承。即使一个子类方法重写了父类的注解方法,子类也不会自动继承父类的注解。例如:

代码语言:java
复制
public abstract class Parent {
    @Transactional
    public abstract void method();
}

public class Child extends Parent {
    @Override
    public void method() {
        // 实现逻辑
    }
}

在上述代码中,虽然 Parent 类的 method 方法被 @Transactional 注解修饰,但 Child 类的 method 方法并不会自动继承这个注解。如果需要在子类中使用事务,必须显式地在子类方法上重新添加 @Transactional 注解。

2. Spring 的事务代理机制

Spring 的事务是通过动态代理实现的。当一个方法被 @Transactional 注解修饰时,Spring 会为该方法创建一个事务代理。这个代理会拦截对方法的调用,并在调用前后插入事务管理逻辑。然而,这种代理机制仅对通过代理调用的方法生效,而不会自动扩展到父类或其他未被注解的方法。

例如:

代码语言:java
复制
@Service
public class Child extends Parent {
    @Override
    @Transactional
    public void method() {
        super.method(); // 调用父类方法
    }
}

在上述代码中:

  • Child.method()@Transactional 注解修饰,因此事务管理器会为它创建代理。
  • 然而,super.method() 的调用是通过 this 发生的,绕过了 Spring 的代理机制。因此,即使父类方法原本有事务注解,事务管理也不会生效。
3. 事务注解的作用范围

@Transactional 注解的作用范围仅限于它被定义的方法,这意味着:

  • 事务管理器只会在被注解的方法上生效:Spring 的事务管理器会根据注解的定义来管理事务,而不会自动扩展到其他方法。
  • 子类方法不会自动继承父类的事务注解:即使子类方法重写了父类的事务方法,子类方法也不会自动继承父类的事务行为,除非显式地重新添加注解。

示例

假设你有以下代码:

代码语言:java
复制
public abstract class Parent {
    @Transactional
    public void parentMethod() {
        // 父类方法逻辑
    }
}

@Service
public class Child extends Parent {
    @Override
    public void parentMethod() {
        super.parentMethod(); // 调用父类方法
    }
}

在这种情况下:

  • Parent.parentMethod()@Transactional 注解修饰,但事务管理器不会自动生效,因为 Child 类没有显式地添加事务注解。
  • Child.parentMethod() 调用 super.parentMethod() 时,事务管理器不会介入,因为调用是通过 this 发生的,绕过了 Spring 的代理机制。

解决方法

为了确保事务管理生效,需要在子类方法上显式地添加 @Transactional 注解:

代码语言:java
复制
@Service
public class Child extends Parent {
    @Override
    @Transactional
    public void parentMethod() {
        super.parentMethod();
    }
}

这样,Spring 的事务管理器会为 Child.parentMethod() 创建代理,并确保事务管理生效。

总结

  • 事务注解的作用范围仅限于被定义的方法:Spring 的事务管理器只会对被 @Transactional 注解直接修饰的方法生效。
  • 子类方法不会自动继承父类的事务注解:即使子类方法重写了父类的事务方法,也需要显式地重新添加注解。
  • 事务管理依赖于 Spring 的代理机制:只有通过代理调用的方法,事务管理器才会介入。

因此,为了确保事务管理生效,需要在子类方法上显式地添加 @Transactional 注解,并确保方法调用通过 Spring 的代理机制进行。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 事务失效的原因分析
    • 1. 事务注解的作用范围
    • 2. 事务注解的继承问题
    • 3. 事务传播行为
  • 事务失效的具体原因
  • 解决方案
    • 方法 1:将事务注解移到父类的 generate 方法上
    • 方法 2:通过代理调用方法
    • 方法 3:重构代码
  • 总结
  • 为什么事务注解的作用范围仅限于被定义的方法?
    • 1. 注解的非继承性
    • 2. Spring 的事务代理机制
    • 3. 事务注解的作用范围
  • 示例
  • 解决方法
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档