EJB3.x JPA: When to use rollback() and setRollbackOnly()

After JTA was introduced for more than a decade ago, then later with the introduction of Bean-Managed Transaction and Container-Managed Transaction in the J2EE 1.2 specs; still, there are many developers, especially those whom had just gotten their feets wet in the marvelous world of Java EE programming were confused when to and not to use the rollback() and the setRollbackOnly() method in the UserTransaction or the EJBContext class. Perhaps you may be coding fine grind transactional methods and have no idea why expections are all over the place and the rollback() or setRollbackOnly() just doesn’t seem to perform according to your expectation. You might think that both methods are the same (but its not). Is this the reason why you have landed on this page through google or through other web sources trying to shade some light?


I’ve been through these pains a decade ago and fear not, I’ll try to clarify in the shortest and the most effecttive manner (hopefully the most effective) through this short article. In order to see what are the best ways for these 2 methods to be used, I have splited this article into 2 section. The first section is the usage in Bean-Managed Transaction and the second, the Container-Managed Transaction approach.

rollback() and setRollbackOnly() in Bean-Managed Transaction

The control over fine grind transaction could be achieved in defining a Session Bean as Bean-Managed Transaction through marking a session bean as @TransactionManagement( TransactionManagementType.BEAN ) annotation. Transaction management is performed by calling the methods in the UserTransaction class.

When to use rollback() in Bean-Managed Transaction?

Taking the below example of a stateless session bean, the rollback() function is to be used only after begin(). Once a transaction begins and if there are any circumstances that data persisted needed a rollback, the rollback() method should be called. If things are fine, the commit() method commits all of the persisted data into the database and makes it final.

@Stateless
@TransactionManagement( TransactionManagementType.BEAN )
public class ExampleSessionFacade implements IExampleSessionFacadeLocal
{
	@Resource
	private EJBContext context;

	public void trxnMethod() throws Exception
	{
		UserTransaction utx = context.getUserTransaction();

		try
		{
			utx.begin();

			//Do something with the EntityManager such as persist(), merge() or remove()

			/**
			  * If any error happens before reaching commit, the rollback() method
			  * is waiting below at the catch{} clause, before hitting on the commit() method.
			 **/
			utx.commit();
		}
		catch(Throwable t)
		{
			//Invokes only if the above throws and Exception or instance of Throwable
			utx.rollback();

			throws new Exception(t.getMessage());
		}
	}
}

This is a very simple and straight forward example. The rollback() method can only be invoked after calling begin(). At the end of the transaction, EITHER you commit all the data by invoking commit() or roll them back through rollback() if circumstances arise that you wouldn’t want the data to be persisted. After begin(), there are two choices, either it is a commit() or a rollback() that concludes a transaction.

** Please take special note for MySQL database users: do make sure that your tables are InnoDB tables instead of MyISAM. If not, rollback will not occur.

QUICK REMINDER for UserTransaction's rollback() in Bean-Managed Transaction

:- Use UserTransaction.rollback() after UserTransaction.begin(). :- If UserTransaction.rollback() was invoked, UserTransction.commit() SHOULD NOT BE INVOKED!.
Vice versa, both the rollback() and commit() are mutually exclusive.

When to use setRollbackOnly() in Bean-Managed Transaction?

According to Oracle, the setRollbackOnly() method is NOT ALLOWED TO BE USED in Bean-Managed Transaction. You can read it here. If Oracle said so, please just refrain from using the setRollbackOnly() method in BMT. But that doesn’t stop us from experimenting. So, please do this with caution (the result might varry in different Application Server and I don’t suggest that you use this is any project).

You can rollback the transaction with setRollbackOnly() in BMT. The example below illustrates where to invoke begin(), commit() and setRollbackOnly():

@Stateless
@TransactionManagement( TransactionManagementType.BEAN )
public class ExampleSessionFacade implements IExampleSessionFacadeLocal
{
	@Resource
	private EJBContext context;

	public void trxnMethod() throws Exception
	{
		UserTransaction utx = context.getUserTransaction();

		try
		{
			utx.begin();

			/** 
			  * Do something with the EntityManager such as persist(), merge() or remove(). 
			  * If any error happens before reaching commit, the the catch{} clause will be invoked,
			  * before hitting on the finally{} clause method.
			 **/
		}
		catch(Throwable t)
		{
			//Invokes only if the above throws an Exception or instance of Throwable
			utx.setRollbackOnly();	//Invoking setRollbackOnly() will throw RollbackException
		}
		finally
		{
			//Even after utx.setTollbackOnly() is invoked, the transaction has to end with commit()
			utx.commit();
		}
	}
}

What the setRollbackOnly() method does is to MARK the transaction as rollback only transaction. Meaning, even after calling commit(), all of the data will not be persisted because the transaction is already marked as “rollback only”, but the commit() method would still need to be called in order to conclude the transaction. Unlike rollback() in Bean-Managed Transaction where after calling begin(), it is either the rollback() method or the commit() method that concludes the transaction. Here, the commit() method would still need to be called even after setRollbackOnly() method is present (that’s the reason why I’ve placed the commit() method in the finally{} clause. But do be mindful though, invoking the UserTransaction’s setRollbackOnly() will cause it to throw javax.transaction.RollbackException. In Glassfish 3.1.2, I’ll get an exception message of: “Transaction marked for rollback”. But even if this method throws an exception when invoked, the commit() method would still need to be invoked because the transaction doesn’t just ends there.

QUICK REMINDER for UserTransaction's setRollbackOnly() in Bean-Managed Transaction

:- As suggested by Oracle, please do not use the UserTransaction's setRollbackOnly() method in any
Bean-Managed Transaction Session Beans. :- But if you insists, always remember to end your transaction with commit() even after invoking
setRollbackOnly() after begin(). :- It is good practice to have the commit() method called in the finally{} clause.

setRollbackOnly() in Container-Managed Transaction

If you’ve defined the session bean’s transaction to be managed by the container (which is by default in Java EE 5 and 6). The container will prevent you from getting the UserTransaction and thus, begin(), rollback() and commit() is not the option because you can’t get the UserTransaction’s instance.

For Container-Managed Transaction, any data manipulation methods from EntityManager will be persisted automatically without the need to explicitly call on the UserTransaction’s begin() and commit() methods. The question is if situations occur when rollback is necessary, how are you suppose to do perform rollback? The answer is EJBContext.setRollbackOnly().

When to use EJBContext’s setRollbackOnly() in Container-Managed Transaction?

Since you can’t get the UserTransaction instance in Container-Managed Transaction session beans, you can still call the setRollbackOnly() method (yes, only the setRollbackOnly() method and not others) through the EJBContext instance. This is the only “transactional” method that could be used for rollback in CMT.

An example of Container-Managed Transaction Session Bean with setRollbackOnly():

@Stateless
public class ExampleSessionFacade implements IExampleSessionFacadeLocal
{
	@Resource
	private EJBContext context;

	public void someTrxnMethod() throws Exception
	{
		try
		{
			//Do something with the EntityManager such as persist(), merge() or remove()
		}
		catch(Throwable t)
		{
			/*
                         * What error occurs in the try{} clause, the data will not be persisted
                         * if context.setRollbackOnly() is invoked here.
                         **/
			context.setRollbackOnly();
		}
	}
}

The moment when context.setRollbackOnly() is invoked, the transaction will just be marked as rollback only and nothing will be persisted when the method ends and returns. This concludes the transaction.

QUICK REMINDER for EJBContext's setRollbackOnly() in Container-Managed Transaction

:- For Container-Managed Transaction, you can perform the rollback only through EJBContext's
setRollbackOnly() method (not through UserTransaction's setRollbackOnly() method). :- The setRollbackOnly() method need not only be invoked only when error occurs in the midst of the
method. Sometimes, it is used to "test" or "pre-qualify" a transaction. There are many creative ways to use
EJBContext's setRollbackOnly() depending the requirement of the application, so use it wisely.

Summary

I truly hope that this article clears your doubt in distinguishing both rollback() method and setRollbackOnly() method in both Bean-Managed Transaction and Container-Managed Transaction. When I started EJB JTA programming back then, it took me quite a while to understand the principles behind transactions, mixing up the rollback() and the setRollbackOnly() is quite normal and thus resulted in erratic application behavior. I just hope that by reading this article, not only that you won’t fall into the same trap as I did, but at the same time, you can speed things up by getting the job done in a technically accurate and correct manner.

Hope you’ve enjoyed.

Related Articles

Max Lam

Born and currently resides in Malaysia, a seasoned Java developer whom had held positions as Senior Developer, Consultant and Technical Architect in various enterprise software development companies.

Facebook Twitter LinkedIn Google+ 

Previous Posts

Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 2) – Where to put your source codes and pom.xml (EJB, MDB, Web & Enterprise Application Client)
Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 1) – Project Directory Structure & Module Generation Through archetype:generate

Building and Deploying Java EE EAR with Maven to Java EE Application Server (Part 1) - Project Directory Structure & Module Generation Through archetype:generate

December 1st, 2012

Maven wasn’t very popular and wide used when it was first released, but after version 2, it had ga[...]

Java LDAP/JNDI: 2 Ways Of Decoding And Using The objectGUID From Windows Active Directory

Java LDAP/JNDI: 2 Ways Of Decoding And Using The objectGUID From Windows Active Directory

October 13th, 2012

Windows Active Directory is a good way for many corporations to be used as a means of user managemen[...]

Quick Note: Unable To Perform LDAP Wildcard “*” Search On Windows Active Directory

Quick Note: Unable To Perform LDAP Wildcard "*" Search On Windows Active Directory

October 9th, 2012

In case you are searching high and low for a solution or an answer to why Windows Active Directory d[...]

Java JNDI/LDAP: Windows Active Directory Authentication, Organizational Unit, Group & Other Information Access

Java JNDI/LDAP: Windows Active Directory Authentication, Organizational Unit, Group & Other Information Access

October 4th, 2012

In today's IT environment, most mid-size corporation and above will have some form of centralized em[...]

MySQL Cluster NDB 7.2 on Solaris 10 Part 3 – Testing The Cluster

MySQL Cluster NDB 7.2 on Solaris 10 Part 3 - Testing The Cluster

September 22nd, 2012

We are back again to have fun with our cluster that we've setup written in our previous articles on [...]

MySQL Cluster NDB 7.2 on Solaris 10 Part 2 – Starting, Distributed Synchronized Users Management And Stopping The Cluster

MySQL Cluster NDB 7.2 on Solaris 10 Part 2 - Starting, Distributed Synchronized Users Management And Stopping The Cluster

September 18th, 2012

This is the continuation from the previous part of the tutorial MySQL Cluster NDB 7.2 on Solaris 10 [...]

MySQL Cluster NDB 7.2 on Solaris 10 Part 1 – How To Install, Setup and Configure

MySQL Cluster NDB 7.2 on Solaris 10 Part 1 - How To Install, Setup and Configure

September 18th, 2012

If you have landed on this page, we believe you might either had a bumpy ride in getting the MySQL c[...]

Quick Fix: How to Solve “Unable to read the logging configuration” on Netbeans7 with JBoss6

Quick Fix: How to Solve "Unable to read the logging configuration" on Netbeans7 with JBoss6

September 8th, 2012

This is just a quick fix post for those whom are having this problem when running JBoss 6.x with Net[...]