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?