Sending events at the end of a transaction

前端 未结 3 973
闹比i
闹比i 2021-02-08 06:50

I have an interface for a Service object that looks something like the following (simplified for brevity):

public interface ItemService {

  public Item getItemB         


        
3条回答
  •  北荒
    北荒 (楼主)
    2021-02-08 07:27

    Summarizing your question: You're looking for a transactionally-safe way to send messages.

    Option 1: JMS

    Transactionally safe messaging is exactly what JMS is for. There's also good JMS integration in Spring, See the JMS chapter in the Spring documentation.

    That will make sure that the messages are sent if and only if the transaction is committed. It also helps for dealing with errors in listeners for these events.

    A difference with your current setup is that these events will be handled asynchronously: your service will return before these events have been handled. (JMS will make sure that they are processed eventually, it can be configured to try multiple times and how to deal with errors,...). Depending on your needs, that may be a good or a bad thing.

    Option 2: Transaction synchronization

    Alternatively, if JMS is too heavy-weight for your case, you could use transaction synchronization: When sending an event, instead of sending it directly use Spring's TransactionSynchronizationManager.registerSynchronization, and send the message in the afterCommit() of your TransactionSynchronization. You could either add a new synchronization per event to be sent, or add one synchronization and keep track of which events to be sent by binding an object containing that list to the transaction using TransactionSynchronizationManager.bindResource.

    I would advise against trying to use your own ThreadLocal for this, because that would go wrong in some cases; for example if inside your transaction you would start a new transaction (RequiresNew).

    Differences with your current setup:

    • if an exception is thrown in the handling of the events in this case, your service will throw the exception, but the changes will already have been committed in the database.
    • if one of your listeners also writes into the database, it will have to do so in a new transaction.

    Alternatively, you can use beforeCommit instead of afterCommit, but then your events will be handled (mails sent,...) even if the actual commit to the database later fails.

    This is less robust (less transactional), than using JMS, but lighter and easier to set up, and usually good enough.

提交回复
热议问题