0

I have EJB 2.0 legacy code. It has a session bean:

/**
 * @ejb.bean
 *      name="Sample"
 *      type="Stateless"
 *      view-type="both"
 * @ejb.transaction
 *      type="Required"
 * @ejb.util
 *      generate="physical"
 */
public abstract class SampleEJB
    implements SessionBean {

  public void delete(Long id) {
    EJBLocalObject local_o = getEjbLocalObject(id);

    invokeDelete(local_o);
  }

  private void invokeDelete(EJBLocalObject local_o)
      throws Exception
  {
    try
    {
      ...
      EJBLocalObject local_another_o = getEjbLocalObject(local_o.getAnotherId());
      local_another_o.remove();
      ...
    }
    catch (Exception e)
    {
      // log exception
      // throw new exception
    }

    try
    {
      ...
      local_o.remove();
      ...
    }
    catch (Exception e)
    {
      // log exception
      // throw new exception
    }

  }

Sometimes due to issues in database, first remove call is successful. But second remove call fails and throws the exception.

This creates inconsistencies in database. Requirement is, all the remove calls should be successful. If one of the call fails, it should rollback the previous removes. How to handle such scenarios?

In case of BMT we can demarcate transactions for start, commit and rollback. But its CMT, so I am not sure how to handle this situation in EJB2.0. Please let me know what can be done.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
user613114
  • 2,731
  • 11
  • 47
  • 73

1 Answers1

1

@ejb.transaction * type="Required"

Assuming that this means the ejb transaction attribute configured is Requiered, the Container enforces that every call to delete() business method executes within a transaction. Therefore, to demarcate the transaction boundary is not a problem, you can be sure that both delete operations execute in the same transaction.

What you need is to mark the transaction for rollback if one delete operation fails.

The easier way to do this is that your business method throws a System exception.

} catch (Exception e) {
   //log exception
   throw new EJBException();
}

When the Container detects that a System exception (in ejb2.x this is exclusively an exception that extends from RuntimeException class) was thrown, it automatically marks the transaction for rollback. However, when an Application Exception (an exception that extends from Exception) is thrown, the Container doesn't change the transaction state.

In your case, it seems to be that delete() throws an Application Exception.

Other alternative is to explicitly marks the transaction for rollback using the SessionContext.setRollbackOnly() method.

//bean atribute
private SessionContext context;

//bean method call by the Container
public void setSessionContext(SessionContet ctx) {
context = ctx;
}

//your first delete code
try {
      ...
      EJBLocalObject local_another_o = getEjbLocalObject(local_o.getAnotherId());
      local_another_o.remove();
      ...
} catch (Exception e) {
   context.setRollbackOnly();
   //log exception
   //throw new Exception
}
//idem for your second delete  
Gabriel Aramburu
  • 2,951
  • 2
  • 16
  • 20
  • Perfect :) I was going through many blogs and found above information scattered at many places. Thank you very much for compiling everything in well manner and explaining it with easy language. Approach suggested by you above works very well. In my case application exception was being thrown. Thus no rollback was happening. – user613114 Jan 24 '14 at 07:30