DuplicateKeyExecption and primary key generation
tomerbd2 Jun 10, 2005 6:20 AMHi
I have 2 Jboss3.2.5 applications servers working in a cluster.
I'm using for primary key generation The pattern "sequence blocks" taken from the book ejb design patterns.
However when i have something like 5 threads that are trying to create new entities then i get DuplikateKeyException on the primary key. (with only 1 jboss server not in a cluster everything is fine).
I was wondering if anybody could help me pin out the problem, following is my code:
I estimated it might be an Isolation level problem, however i have updated the isolation level to be serializable in that way in mysql ds and the problem still happend...
<user-name>root</user-name> <password></password> <TransactionIsolation>TRANSACTION_SERIALIZABLE</TransactionIsolation>
Sequence entity bean
package messaging_as.domain.util; import javax.ejb.*; import messaging_as.util.jmx.ConfigContainer; abstract public class SequenceBean implements EntityBean { EntityContext entityContext; public java.lang.String ejbCreate(java.lang.String name) throws CreateException { setName(name); return null; } public void ejbPostCreate(java.lang.String name) throws CreateException { setSIndex(ConfigContainer.getJMXPropertiesFacade().getSequenceSessionBeanStartIndex()); } public void ejbRemove() throws RemoveException { } public abstract void setName(java.lang.String name); public abstract void setSIndex(int sIndex); public abstract java.lang.String getName(); public abstract int getSIndex(); public void ejbLoad() { } public void ejbStore() { } public void ejbActivate() { } public void ejbPassivate() { } public void unsetEntityContext() { this.entityContext = null; } public void setEntityContext(EntityContext entityContext) { this.entityContext = entityContext; } public int getValueAfterIncrementingBy(int blockSize) { this.setSIndex(this.getSIndex()+ blockSize); return this.getSIndex(); } }
sequence session bean
package messaging_as.domain.util; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.ejb.CreateException; import javax.ejb.FinderException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.naming.Context; import javax.naming.NamingException; import messaging_as.domain.AlbumElementHome; import messaging_as.domain.AlbumElementShareHome; import messaging_as.domain.UserHome; import messaging_as.service.exceptions.InternalException; import messaging_as.util.jmx.JMXPropertiesFacade; import messaging_as.util.sql.MASConnectionManager; import org.apache.log4j.Category; public class SequenceSessionBean implements SessionBean { SessionContext sessionContext; private Category _log; private class Entry { Sequence sequence; int last; }; private java.util.HashMap _entries = new java.util.HashMap(); private int _blockSize; private int _retryCount; private SequenceHome _sequenceHome; public void ejbCreate() throws CreateException { _log = Category.getInstance(getClass()); } public void ejbRemove() { } public void ejbActivate() { } public void ejbPassivate() { } public void setSessionContext(SessionContext sessionContext) { this.sessionContext = sessionContext; Context namingContext = null; try { namingContext = new javax.naming.InitialContext(); _blockSize = JMXPropertiesFacade.getInstance().getSequenceSessionBeanBlockSize(); _retryCount = JMXPropertiesFacade.getInstance().getSequenceSessionBeanRetryCount(); _sequenceHome = (SequenceHome) namingContext.lookup("SequenceLocalHome"); } catch (NamingException e) { _log.error(e.getMessage(), e); } } public void synch() throws InternalException { Connection connection = null; ResultSet resultSet = null; Statement statement = null; int maxObjectID = 0, maxUID = 0, maxShareID = 0; try { connection = MASConnectionManager.getInstance().createConnection(); String sql = "select max(" + AlbumElementHome.FIELD_NAME_OBJECT_ID + ") maxObjectID from " + AlbumElementHome.TABLE_NAME; statement = connection.createStatement(); _log.debug("SQL : " + sql); statement.execute(sql); resultSet = statement.getResultSet(); if (resultSet.next()) { maxObjectID = resultSet.getInt("maxObjectID"); _log.debug(String.valueOf(maxObjectID)); } resultSet.close(); statement.close(); ((Sequence) _sequenceHome.findByPrimaryKey(AlbumElementHome.JNDI_NAME)).setSIndex(maxObjectID + 1); sql = "select max(" + UserHome.FIELD_UID + ") maxUID from " + UserHome.TABLE_NAME; statement = connection.createStatement(); _log.debug("SQL : " + sql); statement.execute(sql); resultSet = statement.getResultSet(); if (resultSet.next()) { maxUID = resultSet.getInt("maxUID"); _log.debug(String.valueOf(maxUID)); } resultSet.close(); statement.close(); ((Sequence) _sequenceHome.findByPrimaryKey(UserHome.JNDI_NAME)).setSIndex(maxUID + 1); sql = "select max(" + AlbumElementShareHome.FIELD_SHARE_ID + ") maxShareID from " + AlbumElementShareHome.TABLE_NAME; statement = connection.createStatement(); _log.debug("SQL : " + sql); statement.execute(sql); resultSet = statement.getResultSet(); if (resultSet.next()) { maxShareID = resultSet.getInt("maxShareID"); _log.debug(String.valueOf(maxShareID)); } resultSet.close(); statement.close(); ((Sequence) _sequenceHome.findByPrimaryKey(AlbumElementShareHome.JNDI_NAME)).setSIndex(maxShareID + 1); } catch (SQLException e1) { _log.error(e1.getMessage(), e1); } catch (FinderException e) { throw new InternalException(e); } finally { try { if (connection != null && !connection.isClosed()) connection.close(); } catch (SQLException e) { _log.error(e.getMessage(), e); } } } public int getNextSequenceNumber(String name) { try { Entry entry = (Entry) _entries.get(name); if (entry == null) { // add an entry to the sequence table entry = new Entry(); try { entry.sequence = _sequenceHome.findByPrimaryKey(name); } catch (javax.ejb.FinderException e) { _log.info("can't find sequence: " + name + " trying to create it"); // if we couldn't find it, then create it... entry.sequence = _sequenceHome.create(name); _log.info("sequence: " + name + " created"); } _entries.put(name, entry); } if (entry.last % _blockSize == 0) { for (int retry = 0; true; retry++) { try { entry.last = entry.sequence.getValueAfterIncrementingBy(_blockSize); break; } catch (javax.ejb.TransactionRolledbackLocalException e) { if (retry < _retryCount) { // we hit a concurrency exception, so try again... _log.info("RETRYING"); continue; } else { // we tried too many times, so fail... throw new javax.ejb.EJBException(e); } } } } return entry.last++; } catch (javax.ejb.CreateException e) { throw new javax.ejb.EJBException(e); } } }
from ejb-jar.xml
<container-transaction> <method> <description /> <ejb-name>SequenceSession</ejb-name> <method-name>getNextSequenceNumber</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> <trans-attribute>RequiresNew</trans-attribute> </container-transaction>
jboss.xml
<container-configurations> <container-configuration> <container-name>Standard CMP 2.x EntityBean with cache invalidation</container-name> <commit-option>A</commit-option> <cache-invalidation>True</cache-invalidation> </container-configuration> </container-configurations> . . . <entity> <ejb-name>Sequence</ejb-name> <local-jndi-name>SequenceLocalHome</local-jndi-name> <!-- no cache for this entity bean --> </entity>
anybody can help with what the problem might be?
did i update the isolation level correctly?