Search code examples
javahibernateormjpajta

ApplicationException - Java - Hibernate - rollback related


My question is related to Transactions and Exceptions

Requirements:

I have 10 records to insert into database table. And after inserting every record, I insert data into another table. So if inserting to second table fails, I want to rollback that record.

Ex. Say handle cash transfer (from one account to account) for 10 persons at a time.

pseudo code: ------------- Start of EJB method

for(int i = 0; i < TransferRecords.length; i++)
{
    try
    {
          //Deduct cash from TransferRecord.accountFrom --- Includes use of Hibernate Session
          //Add cash in TransferRecord.accountTo -- Includes use of Hibernate Session
     } catch(AppException exception)
     {
            //Rollback the transaction only for this particular transfer (i)
            // But here when I go for next record it says session is closed
     }
}

---------End of EJB method

Here AppException is created with @ApplicaitonException(rollback=true) annotion.

The functionality we want is: Even if the transaction fails for TransferRecord (say 2), I want the data to be committed for record 0, record 1, record 3, record 4 (etc... and but not for record 2)

But the issue here is: when TransferRecord 2 fails and when I move to TransferRecord 3, I get "Session Closed" error.

My doubts are: 1. Is this a right approach? or should I run the for loop(for each TransferRecord) outside of the EJB 2. How can I make sure that session is not closed but transaction is rolled back (only for that for particular failed transaction)

Thank you in advance.

I am using EJB3, Hibernate 3.x, Jboss 4.2.x and I am using Container Managed Transaction.


Solution

  • Is this a right approach?

    No, with CMT, you method is your transactional unit. So here, all your TransferRecord and handled in a same and unique transaction.

    By the way, how do you rollback the transaction? Do you propagate a RuntimeException or do you call setRollbackOnly()? I'm just curious.

    Or should I run the for loop (for each TransferRecord) outside of the EJB?

    Why outside? Nothing forces you to do that. If you want to process each TransferRecord in its own transaction, you should pass them to another EJB method (the code below is inspired by this answer):

    // supposing processRecords is defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
    @Stateless
    @TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
        @EJB
        private MyStatelessLocal1 myBean;
    
        public void processRecords(List<TransferRecord> objs) {
            // No transactional stuff so no need for a transaction here
            for(Object obj : objs) {
                this.myBean.process(obj);
            }
        }
    
        @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
        public void process(TransferRecord transferRecord) {
            // Transactional stuff performed in its own transaction
            // ...
        }
    }
    

    How can I make sure that session is not closed but transaction is rolled back (only for that for particular failed transaction)

    I think I covered that part.