A Word on Spring @Transactional and Exceptions

DZone 's Guide to

A Word on Spring @Transactional and Exceptions

Spring's @Transactional is very popular among developers. Yet, many of them are unaware of its weird exception handling convention, which can lead to inconsistent data.

· Java Zone ·
Free Resource

A unit of work transaction either succeeds or fails and rolls back. So suppose we are dealing with transferring money from one account to another. Now, say money from one account is already debited but when it gets credited to another account, there was an exception. Obviously, the ideal scenario would be for the debited action to roll back. Otherwise, we're dealing with an inconsistent state (and some very angry account owners).

So if we use transactions in our business logic, we can ensure the logic under the transaction works as a unit and that it will roll back if anything is found to be wrong.

Either the unit of work completely succeeds or completely fails. There is no intermediate state. Now the question comes, "How we can handle a transaction?"

There are two ways to handle it:

  • BMT: Bean Managed Transaction

  • CMT: Container Managed Transaction


If we need a finer control over business logic or want to introduce savepoints, this type of technique should be adopted, where a bean provider has a responsibility to start, commit, and roll back the transaction.


If we want to delegate the responsibility to a container, we use this instead. Sometimes we call it a declarative transaction. We all know in Spring that, using the @Transactional annotation, we can adopt a declarative transaction technique.

A Weird Case

We're not going to go into configuration in this article. Instead, I'm going to warn you about some precautions you want to take while using @Transactional. Study the pseudo code below:

public void transferMoney(Account to, Account from, int amount) throws Exception {
    debiFromAccount(from, amount)
    creditToAccount(to, amount);

public debitFromAccount(Account from, int amount) {
    //do staff and debited money from data base

public creditToAccount(Account to, int amount) throws Exception {
    //do straff
    throw new Exception("Error during credit");

Now do a dry run:

  1. If Account from's initial balance is 1000

  2. Account to is 500

  3. Transfer amount is 100

  4. Afterward, an error occurred in creditToAccount(Account to,int amount)

What will be the output you expect?

To: 500

From: 1000

After all, I use @Transactional. If there's an exception, it should be rolled back, right?

But unfortunately, it left an inconsistent state.

Our output is:

To: 500

From: 900

What's going on here?

Let me clear this up for you. @Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database, making the system inconsistent.

The Spring documentation says:

While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.

Pay attention to the last line: “it is often useful to customize this.” So how can we customize it?

It's very simple, just use the following with @Transactional:

@Transactional(rollbackFor = Exception.class)

So if you throw an Exception or a subclass of it, always use the above with the @Transactional annotation to tell Spring to roll back transactions if a checked exception occurs.

exceptions, java, spring, transactional, tutorial

Published at DZone with permission of Shamik Mitra , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}