views:

250

answers:

3

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.

A: 

Hi,

I think that you can create two separated transactions, the first for the TransferRecord(1) (doing a commit once everything is fine) and then starting other TX for all the TransferRecord(i+1).

Another approach is using savepoints, being able to rollback and discard everything past that savepoint (but I like prefer the first approach).

Regards.

ATorras
+2  A: 

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.

Pascal Thivent
Thanks Pascal.By the way, how do you rollback the transaction? Do you propagate a RuntimeException or do you call setRollbackOnly()? I'm just curious.We throw RuntimeException only.We are trying to avoid User transactions on a whole. But your code is promising.Thanks again.
Vineyard
I wasn't sure with the pseudo code but I don't have anything to add then :)
Pascal Thivent
+1  A: 

The only option you have here is either to use user transaction instead of container managed transaction of loop outside the bean so that everytime you enter the bean you get fresh entity manager with associated transaction and connection (basically session)

scienty
You can use CMT and loop inside the bean.
Pascal Thivent
Pascal,You mean:use: @TransationAttribute(TransactionAttributeType.REQUIRES_NEW) public void process(TransferRecord transferRecord) {kind of method and loop through all TransferRecords in another method.?Thank you.
Vineyard