Solution for reliably retrieving incrementing value
damianharvey.damianharvey.gmail.com Aug 28, 2008 9:35 PMI have an entity Booking
in my multi-tenant app that in addition to it's ID also requires a Booking Number that increments for each company. I have multiple companies that need this number to increment separately (and some use different increment steps).
I was just selecting the max value of this number from the database for the particular company, however it was possible (and not that difficult) for two users from the same company to save the Booking at the same time and end up with the same Booking number.
What is the best way to solve this?
My solution so far involved creating a UniqueNumber
table that holds increment counters for each company per entity. I then have an Application Scoped SFSB that reads from this table and increments the counter there and then before returning a value. This still doesn't work though. Both users still get the same number. The crazy thing is that I can see the Hibernate update calls and the second select should get the updated value but it doesn't.
I've been staring at this for too long and my brain is numb. Can anyone suggest a better approach or point me to where I've made the wrong move?
Here's my UniqueNumberGenerator:
@Synchronized @Scope(ScopeType.APPLICATION) @Stateful @AutoCreate @Name("uniqueKeyGenerator") public class UniqueKeyGenerator implements IUniqueKeyGenerator, Serializable { @In private EntityManager entityManager; @Logger private static Log log; //This can be reset to whatever via the components.xml private Long defaultUniqueKey = 1000L; /** Fetches the next unique key from the Unique Key table. It then increments the value in the table. * This is to prevent duplicate unique keys (eg. Booking Number) * * @param company * @param entityClass * @return */ public long getUniqueKey(Company company, String entityClass) { PersistenceContexts.instance().changeFlushMode(FlushModeType.MANUAL); UniqueKey uniqueKey = null; try { uniqueKey = (UniqueKey)entityManager.createQuery("select u from UniqueKey u"+ " WHERE u.company = :company"+ " AND u.entityClass = :entityClass") .setParameter("company", company) .setParameter("entityClass", entityClass) .getSingleResult(); } catch(NoResultException e) { } catch(NonUniqueResultException e) { } if(uniqueKey == null) { uniqueKey = new UniqueKey(company, entityClass, defaultUniqueKey, new Date()); entityManager.persist(uniqueKey); } long result = uniqueKey.getIdentifier(); //Now increment the value uniqueKey.setIdentifier(result + 1L); uniqueKey.setUpdatedDate(new Date()); entityManager.flush(); PersistenceContexts.instance().changeFlushMode(FlushModeType.COMMIT); return result; } @Remove @Destroy public void destroy() { } }
Many Thanks,
Damian.