在Spring bean中启动新的事务

10 浏览
0 Comments

在Spring bean中启动新的事务

我们有:

@Transactional(propagation = Propagation.REQUIRED)
public class MyClass implements 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();

0
0 Comments

在Spring bean中启动新事务的原因是为了实现事务行为。要在同一个bean中调用"REQUIRES_NEW"事务,可以通过创建一个"self"引用来实现。但在Spring中,这并不是直接可行的,需要使用注解进行注入。

下面是实现的代码示例:

@Service("someService")
public class ServiceImpl implements Service {
   @Autowired
   @Qualifier("someService")
   private Service selfReference;
   public void firstMethod() {
       selfReference.secondMethod();
   }
   @Transactional(propagation = Propagation.REQUIRES_NEW) 
   public void secondMethod() {    
         // 在新的事务中执行操作
   }
}

在`firstMethod`中调用`selfReference.secondMethod()`会调用代理对象而不是`this`,这样就可以实现"REQUIRES_NEW"事务的工作。

0
0 Comments

在Spring bean中启动新事务的问题:

问题原因:Spring AOP的工作方式决定了只能在公共方法上起作用。即使在MyClass的公共方法上,Spring也不会识别它,除非该方法在接口中定义。

解决方法:可以使用TransactionTemplate以编程方式启动新事务。具体步骤如下:

1. 创建TransactionTemplate对象,并传入事务管理器(TransactionManager)作为参数。

2. 设置事务传播行为为PROPAGATION_REQUIRES_NEW,表示启动一个新事务。

3. 使用execute方法执行需要在事务中执行的代码块。

代码示例:

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

补充说明:如果MyClass实现了一个接口,Spring只会使用该接口生成事务代理,并忽略所有公共的、非接口方法。如果MyClass没有实现接口,则所有公共方法都会被使用。

需要注意的是,无论是接口还是类,方法需要通过代理来执行,且必须是一个注入的依赖项。

通过使用TransactionTemplate以编程方式启动新事务,可以解决在Spring bean中启动新事务的问题。

0
0 Comments

Spring bean中启动新事务的问题通常是由于以下原因导致的:

- Spring只会拦截通过代理调用的方法,因此在同一个类中调用方法不会启动新的事务。

- 使用@Transactional注解时,只有公共方法才会被拦截,保护、私有或包可见的方法不会被拦截。

- 在使用代理模式时,默认情况下只有通过代理调用的方法才会被拦截。

为了解决这个问题,可以采取以下方法:

- 使用@Transactional注解时,只对公共方法使用该注解。

- 使用aspectj模式进行事务设置,从而在类中进行代码织入,而不是在运行时创建代理。

- 在类中获取Spring代理,并调用该代理的方法,而不是直接调用this

下面是一个示例代码:

(propagation = Propagation.REQUIRED)
public class SomeService {
    private ApplicationContext applicationContext;
    
    private SomeService getSpringProxy() {
        return applicationContext.getBean(this.getClass());
    }
    
    private void doSomeAndThenMore() {
        // 替代
        // this.doSometingPublicly();
        // 使用以下方式在事务中运行
        getSpringProxy().doSometingPublicly();
    }
    
    public void doSometingPublicly() {
        // 在此执行一些需要事务的操作
    }
}

更多详细信息请参考Spring参考文档中的事务声明式注解部分。

0