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

10959 ワード

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 effective 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 to split 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
			
            //Invoking setRollbackOnly() will throw RollbackException
            utx.setRollbackOnly();
        }
        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'ssetRollbackOnly() in Container-ManagedTransaction
  • For Container-Managed Transaction, you can perform the rollbackonly through EJBContext's setRollbackOnly() method (not throughUserTransaction's setRollbackOnly() method).
  • The setRollbackOnly() method need not only be invoked only whenerror 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 ofthe 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.
    - See more at: http://www.developerscrappad.com/547/java/java-ee/ejb3-x-jpa-when-to-use-rollback-and-setrollbackonly/#sthash.egAJpyBT.dpuf