Execute before or after a spring transactional method

Spring provides a mechanism through which you can plugin some code which gets executed after the transaction is complete. We need to provide an implementation of the org.springframework.transaction.support.TransactionSynchronization interface which can be registered using TransactionSynchronizationManager. This could be useful in scenario where your code does not have a control over when the transaction starts and ends. For e.g. Your service code always joins an existing transaction started by the calling method (using REQUIRED PROPAGATION LEVEL).

Lets have a look at some sample code. Below sample captures one of the bulk report upload use case where each record in a database has a corresponding report (physical file). When the existing records are replaced with new records their corresponding reports would have to be deleted after the transaction successfully commits.


public class ServiceBean {

    @Transactional
    public void copyNewRecords(Collection<RecordInfo> recordInfo) {
        //read old file names
        //register the TransactionSynchronization object
        TransactionSynchronizationManager.registerSynchronization(new FileDeletionHandler(oldFileLists);
        //copy new records and their corresponding files
    }
}

public class FileDeletionHandler implements TransactionSynchronization {
    private List<File> filesToBeDeleted;

    public FileDeletionHandler(List<File> filesToBeDeleted) {
        this.filesToBeDeleted = filesToBeDeleted;
    }

    @Override
    public void suspend() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void flush() {

    }

    @Override
    public void beforeCommit(boolean readOnly) {
    }

    @Override
    public void beforeCompletion() {
    }

    @Override
    public void afterCommit() {
        for(File file : filesToBeDeleted) {
            file.delete();
        }
    }

    @Override
    public void afterCompletion(int status) {
        //status could be either TransactionSynchronization.STATUS_COMMITTED or TransactionSynchronization.STATUS_ROLLED_BACK
        //or TransactionSynchronization.STATUS_UNKNOWN
    }
}

If you want to execute some code when the transaction rolls back, you could implement that in the afterCompletion() method and have a conditional check on the passed status parameter.

If you have a look at the TransactionSynchronizationManager implementation, you will notice that a List<TransactionSynchronization> objects are maintained in a ThreadLocal variable. This variable is initialized just after the transaction starts (using initSynchronization() method) and cleared when the transaction is suspended or ends.

Advertisements

Posted on October 6, 2011, in spring and tagged , . Bookmark the permalink. 2 Comments.

  1. Hi Amit, very well explained!

  2. Hi, really nice work it seeminglessly works. Thank you.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: