【问题标题】:Starting new transaction in Spring bean在 Spring bean 中启动新事务
【发布时间】:2011-03-03 11:52:18
【问题描述】:

我们有:

@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implementes MyInterface { ...

MyInterface 有一个方法:go()

当 go() 执行时,我们启动一个新事务,该事务在方法完成时提交/回滚 - 这很好。

现在让我们在 go() 中调用 MyClass 中的私有方法,该方法具有 @Transactional(propagation = Propagation.REQUIRES_NEW。似乎 Spring“忽略”了 REQUIRES_NEW 注释并且没有启动新事务。我相信这是因为 Spring AOP 在接口级别(MyInterface)上运行,不会拦截对 MyClass 方法的任何调用。这是正确的吗?

有没有办法在 go() 事务中启动一个新事务?是调用另一个将事务配置为 REQUIRES_NEW 的 Spring 托管 bean 的唯一方法吗?


更新:添加当客户端执行go() 时,它们是通过对接口的引用而不是类来执行的:

@Autowired
MyInterface impl;

impl.go();

【问题讨论】:

    标签: java spring transactions


    【解决方案1】:

    @Transactional 只有在 public 方法上才会被注意到,这取决于 Spring AOP 的工作方式。

    但是,如果您愿意,可以使用programmatically start a new transaction,例如使用TransactionTemplate,例如

    TransactionTemplate txTemplate = new TransactionTemplate(txManager);                
    txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    txTemplate.execute(status -> {
            // do stuff
    });
    

    【讨论】:

    • 但是即使@Transactional 在 MyClass 的公共方法上,Spring 似乎仍然不会选择它除非该方法在接口中定义 - 正确?
    • 补充一下,这是因为正在执行 go() 方法的客户端引用了接口,而不是类。
    • @Marcus:有点。如果MyClass 实现了一个接口,那么 Spring 将只使用该接口来生成事务代理,甚至忽略公共的非接口方法。但是,如果MyClass 没有实现接口,则将使用所有公共方法。
    • 是否是接口并不重要,它只需要在一个代理后面,一个注入的@Autowired 依赖。
    【解决方案2】:

    简而言之,您必须通过代理调用该方法才能实现事务行为。 如问题中所述,可以在同一个 bean 中调用“REQUIRES_NEW”。为此,您必须进行“自我”参考。 在春天,这不是简单的可能。你必须注入它 带有@Resource 注释。

    @Service("someService")
    public class ServieImpl implements Service {
    
       @Resource(name = "someService")
       Service selfReference;
    
       @Transactional
       public void firstMethod() {
           selfReference.secondMethod();
       }
    
       @Transactional(propagation = Propagation.REQUIRES_NEW) 
       public void secondMethod() {    
             //do in new transaction
       }
    
    } 
    

    firstMethod 中的调用调用代理而不是“this”,这应该使“REQUIRES_NEW”事务工作。

    【讨论】:

      【解决方案3】:

      来自 Spring 参考 2.5:

      使用代理时,@Transactional 注释应该只应用于 公开可见的方法。如果您确实注释了受保护的、私有的或 带有 @Transactional 注释的包可见方法,不会出错 被提出,但带注释的方法不会显示配置的事务 设置。

      所以 Spring 会忽略非公共方法上的 @Transactional 注释。

      还有,

      在代理模式下(默认),只有“外部”方法调用进入 通过代理会被拦截。这意味着“自我调用”, 即目标对象中的方法调用目标的其他方法 对象,即使被调用也不会在运行时导致实际事务 方法标记为@Transactional!

      因此,即使您将方法设为public,从同一类的方法中调用它也不会启动新事务。

      您可以在事务设置中使用aspectj模式,以便事务相关代码编织在类中,并且在运行时不创建代理。

      更多详情请见the reference document

      另一种可能的方法是在类本身中获取类的 spring 代理并在其上调用方法而不是 this:

      @Service
      @Transactional(propagation = Propagation.REQUIRED)
      public class SomeService {
      
          @Autowired
          private ApplicationContext applicationContext;
      
          private SomeService  getSpringProxy() {
              return applicationContext.getBean(this.getClass());
          }
      
          private void doSomeAndThenMore() {
              // instead of
              // this.doSometingPublicly();
              // do the following to run in transaction
              getSpringProxy().doSometingPublicly();
          }
      
          public void doSometingPublicly() {
              //do some transactional stuff here
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多