在使用Spring事务时如何创建一个提交后执行的操作

50

由于某些原因,我使用Spring PlatformTransactionManager手动执行了事务提交和回滚操作,现在我需要设置一个钩子,在事务提交后执行后置提交操作。

参考以下内容:

 void commit(TransactionStatus status) throws TransactionException;

我无法确定除非没有抛出异常,否则如何确定交易是否成功。
我可以使用AOP作为一种选择,但是如何通过编程来实现呢?也许可以使用回调方法?
4个回答

92

您可以通过更简单的方法获得所需内容,使用TransactionSynchronizationManagerTransactionSynchronization

使用 TransactionSynchronizationManager,您可以使用静态方法获取有关当前事务的信息,并且可以注册 TransactionSynchronization,从而可以在调用时自动执行提交后操作。

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
           void afterCommit(){
                //do what you want to do after commit
           }
})

请注意,TransactionSynchronization 是基于每个线程的(对于基本的 Web 请求通常不是问题)。

4
我写过一篇关于Spring事务同步和如何轻松注册提交后回调函数的博客文章。链接为http://azagorneanu.blogspot.com/2013/06/transaction-synchronization-callbacks.html - Andrei Zagorneanu
11
使用TransactionSynchronizationAdapter作为实现单个方法的更简洁方式。 - Alex
上述实现需要覆盖 TransactionSynchronization 的所有方法,即使它只显示了一个。请查看我基于 @Alex 建议的答案。 - Michail Michailidis
5
自Spring 5以来,TransactionSynchronization现在使用default作为其方法的默认实现。因此,无需使用TransactionSynchronizationAdapter - m.sarhan
TransactionSynchronizationManager的文档指出:“该类应由资源管理代码使用,而不是典型的应用程序代码。” 因此,基本上您不应该使用它。 - Christian
显示剩余6条评论

34
Credit to Grooveek的回答和其中Alex的评论 - 我把这里放在这里,因为结合建议提供了一个坚实而干净的解决方案,在网络上很难找到。
如果您需要在成功提交后对@Transactional方法进行回调,请在方法的开头添加它。使用Spring 4+。
@Service
public class OneService {

    @Autowired
    OneDao dao;

    @Transactional
    public void a transactionalMethod() {
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter(){
            public void afterCommit(){
                //do stuff right after commit
                System.out.println("commit!!!");

            }
        });
        //do db stuff
        dao.save();
    }
}

4
感谢@Michail的回答。对于事务回滚,可以使用“afterCompletion(int status)”方法并检查status == TransactionSynchronization.STATUS_ROLLED_BACK来实现该方法。 - deep
TransactionSynchronizationAdapter已经被弃用。您可以在此代码示例中直接将其替换为TransactionSynchronization。 - Franziskus Karsunke

21
自Spring 4.2以来,使用基于注解的配置定义后提交事件(或更一般地事务同步事件,例如回滚)的监听器已成为可能。这是基于core spring中的事件处理。使用此方法可以更轻松地测试代码,因为您避免了对TransactionSynchronizationManager的直接依赖,而在单元测试中该依赖可能不会处于活动状态。您可以轻松测试您的事务服务发布事件,并确保当您接收到事件时,您的监听器执行正确的操作。

因此,不再拖延,以下是设置步骤:

在本示例中,我们假设您有一个Customer实体和一个CustomerRepository(和相应的ORM)。

首先,您需要一个新的事件类型NewCustomerEvent:

// NewCustomerEvent.java
// Just a regular pojo for the event
public class NewCustomerEvent {

    public String email;

    // constructor, setters and getters omitted
}

然后您可以使用@TransactionalEventListener定义一个监听器。默认情况下,它会在成功提交后执行,但是可以使用phase参数进行更改:
// NewCustomerEventListener.java
@Component
public class NewCustomerEventListener {

    @TransactionalEventListener
    public void handleNewCustomerEvent(NewCustomerEvent newCustomerEvent) {
        // handle new customer event
    }
}

最后,您需要在交易服务中增加一个ApplicationEventPublisher,并在所有交易语句发送完成后调用publish。

// CustomerRespositoryService.java
@Service
public class CustomerRepositoryService {

    @Inject
    private ApplicationEventPublisher applicationEventPublisher;

    @Inject
    private CustomerRepository customerRepository;

    @Transactional
    public void createCustomer(String email) {
        Customer customer = new Customer(email);
        customerRespotory.save(customer);
        applicationEventPublisher.publish(new NewCustomerEvent(email));
    }

}

另请参阅:


2020年肯定是更干净的方法。 - Wecherowski
小细节提醒,监听器的注解应该是TransactionalEventListener而不是TransactionalListener。 - F. Geraerts
@F. Geraerts,发现得好,谢谢!现在应该已经被纠正了。 - Frank Wilson

1

因为某些原因,在我的一个项目中我也不得不使用PlatformTransactionManager。所以我被迫使用了org.springframework.transaction.support.TransactionTemplate

http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/transaction/support/TransactionTemplate.html

主要好处是,如果您正确实现了PlatformTransactionManager,就不需要手动提交/回滚。如果需要更具体的内容,至少TransactionTemplate的源代码可以帮助您。
使用起来非常简单:

config.xml

<bean name="transactionTemplate"
            class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="platformTransactionManager"/>
</bean>

MyServiceImpl.java

@Service public class MyServiceImpl implements MyService {
@Autowired private TransactionTemplate transactionTemplate;
public Entity getSomethingWithTx(final long id) {
return transactionTemplate.execute(new TransactionCallback() { @Override public Entity doInTransaction(TransactionStatus status) { //TODO 实现 } }); }
}

这确实有助于手动编码方面的工作,但是如何判断事务是否已成功回滚或提交呢?我可以看到status.iscompleted().. 但这可能意味着成功回滚或成功提交...? - user1555190
1
事务仅在两种情况下回滚:回调代码抛出异常离开 doInTransaction 或存在隐式回滚调用(强烈不建议,可能会生成 UnexpectedRollbackException)。因此,您可以安全地假设如果 getSomethingWithTx 没有抛出异常,则事务已成功提交。 - mrembisz
使用@Transactional的主要原因是为了从代码中削减冗余和过于冗长的部分,这些部分并不适用。 - Enerccio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接