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.
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.