0
votes

I got session bean which is managed by containter. Recently I run into problems, where exception is thrown:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

it is because some other process has updated row (and version field has changed). Now when it is thrown, I catch OptimisticLockException and want to re-run failed operation (and I want to put WRITE lock this time to be sure it won't fail again), I do it this way:

T ctj = new T();
C ca = entityManager.find(C.class, id);     
Double newBalance = Operations.add(ca.getAccountBalance(), amount);
ca.setAccountBalance(newBalance);
entityManager.persist(ca);
ctj.setBalanceAfterTransaction(newBalance);    
entityManager.persist(ctj);
try {
    flushRegisterTransactionUpdateAccountBalance();
} catch(OptimisticLockException ex) {
    retryBalanceUpdate(ca, ctj, amount);
}    

and the methods I call in above:

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void retryBalanceUpdate(C ca, T ctj, Double amount) {
    entityManager.refresh(ca);
entityManager.lock(ca, LockModeType.WRITE);
    Double newBalance = Operations.add(ca.getAccountBalance(), amount);
    ca.setAccountBalance(newBalance);
    entityManager.persist(ca);      
    ctj.setBalanceAfterTransaction(newBalance);
    entityManager.persist(ctj);
    flushRegisterTransactionUpdateAccountBalance();    
}

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void flushRegisterTransactionUpdateAccountBalance() {
    entityManager.flush();
}

Those 2 methods I have crated, because I was hoping the whole (parent) transaction will not fail because of exception thrown by flushRegisterTransactionUpdateAccountBalance().

Unfortunately it fails, when I call in catch block method retryBalanceUpdate, the first line of it's body (entityManager.refresh(ca)) throws:

[TxPolicy] javax.ejb.EJBTransactionRolledbackException: EntityManager must be access within a transaction [MyBean] No transaction javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction

Does anybode know how I could achieve what I explained? I am using EJB 3.0, entityManager object is initiated by class-level annotation:

@PersistenceContext(unitName="MyPersistenceUnit") private EntityManager entityManager;

The class it self is stateless session bean with transaction attribute SUPPORTS

1

1 Answers

0
votes

OptimisticLockException :

Thrown by the persistence provider when an optimistic locking conflict occurs. This exception may be thrown as part of an API call, a flush or at commit time. The current transaction, if one is active, will be marked for rollback.

You can create a custom exception with annotation @ApplicationException(rollback=false). In flushRegisterTransactionUpdateAccountBalance you have to catch OptimisticLockException & re-throw custom exception.

Can refer the below the sample code.

try {
    flushRegisterTransactionUpdateAccountBalance();
} catch(XApplicationException ex) {
    retryBalanceUpdate(ca, ctj, amount);
} 

In below code, handling the exception & then re-throwing custom exception which is marked as rollback = false.

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void flushRegisterTransactionUpdateAccountBalance() throws XApplicationException{
    try {
    entityManager.flush();
    } catch(OptimisticLockException ex) {
     throw new XApplicationException(ex.getMessage());
} 
}