views:

187

answers:

2

Hi there.

I have the following class that generates sequencial Card Numbers. I'm trying to recover from OptimisticLockException, by calling recursively the same method. however, i'm getting TransactionRequiredException. Dows anyone knows how to recover from OptimisticLockException in my case?

Thanks a lot in advance

@Name("simpleAutoIncrementGenerator")
public class SimpleAutoIncrementGenerator extends CardNumberGenerator{
private static final long serialVersionUID = 2869548248468809665L;

private int numberOfRetries = 0;

@Override
public String generateNextNumber(CardInstance cardInstance, EntityManager entityManager) {

    try{ 
        EntityCard card = (EntityCard)entityManager.find(EntityCard.class, cardInstance.getId());

        if(card != null){

            String nextNumber = "";

            String currentNumber = card.getCurrentCardNumber();

            if(currentNumber != null && !currentNumber.isEmpty()){

                Long numberToInc =  Long.parseLong(currentNumber);
                numberToInc ++;
                nextNumber = String.valueOf(numberToInc);
                card.setCurrentCardNumber(nextNumber);

                                    // this is just to cause a OptimisticLock Exception
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                entityManager.persist(card);
                entityManager.flush();

                return nextNumber;
            }
        }

    }catch (OptimisticLockException oLE) {

        System.out.println("\n\n\n\n OptimisticLockException \n\n\n\n");
        if(numberOfRetries < CentralizedConfig.CARD_NUMBER_GENERATOR_MAX_TRIES){
            numberOfRetries ++;
            return generateNextNumber(cardInstance,entityManager);
        }

    }catch (TransactionRequiredException trE) {
        System.out.println("\n\n\n\n TransactionRequiredException \n\n\n\n");
        if(numberOfRetries < CentralizedConfig.CARD_NUMBER_GENERATOR_MAX_TRIES){
            numberOfRetries ++;
            return generateNextNumber(cardInstance,entityManager);
        }
    }catch (StaleObjectStateException e) {
        System.out.println("\n\n\n\n StaleObjectStateException \n\n\n\n");
        if(numberOfRetries < CentralizedConfig.CARD_NUMBER_GENERATOR_MAX_TRIES){
            numberOfRetries ++;
            return generateNextNumber(cardInstance,entityManager);
        }
    }

    return null;
}
}
A: 

Well ... where do I start?

If you just want to generate sequential numbers, what about having the database do that? For instance using a sequence, or using an update statement of the form

update Card
set cardnumber = cardnumber + 1 where id = ?

Or if only a single process generates the numbers, what about sychronizing the generator in Java? After all, incrementing a counter hardly takes long enough to qualify as a concurrency bottleneck.

Next, your generator is not thread safe, because you keep mutable state in an instance variable (number of retries) without any sychronization.

I don't know Seam, but if hibernate throws an exception, the hibernate session should no longer be used, and a typical response is to roll back the current transaction. Your recursive call does not start a new transaction, and obviously the entity manager needs one. Look into how your framework does transaction demarcation.

meriton
A: 

From the javadoc of 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.

So the current JTA transaction is indeed marked for rollback. In other words, if you want to "try again", you'll have to start a new transaction (and if you are using container managed transaction, this means calling a method for which the container will start a new transaction).

I'm not a Seam expert but, still, what I don't understand here is:

  • Why are you passing the EntityManager as parameter? Isn't the EntityManager injected (using the @In annotation in a POJO if I'm not wrong). Re-using it in the recursive call sounds wrong.
  • Where is the transactional stuff? Shouldn't you use @Transactional(REQUIRED) or something equivalent (maybe it's automatic though)?

IMHO, the first point is a big part of your problem.

Pascal Thivent