You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2003/04/01 16:09:41 UTC
cvs commit: db-ojb/src/java/org/apache/ojb/broker/util/sequence SequenceManagerStoredProcedureImpl.java SequenceManagerHighLowImpl.java HighLowSequence.java AbstractSequenceManager.java SequenceManagerHelper.java SequenceManagerInMemoryImpl.java SequenceManagerMySQLImpl.java SequenceManagerNextValImpl.java SequenceManagerSeqHiLoImpl.java
arminw 2003/04/01 06:09:40
Modified: src/java/org/apache/ojb/broker/util/sequence
SequenceManagerHighLowImpl.java
HighLowSequence.java AbstractSequenceManager.java
SequenceManagerHelper.java
SequenceManagerInMemoryImpl.java
SequenceManagerMySQLImpl.java
SequenceManagerNextValImpl.java
SequenceManagerSeqHiLoImpl.java
Added: src/java/org/apache/ojb/broker/util/sequence
SequenceManagerStoredProcedureImpl.java
Log:
- update sequence package
- add new SequenceManager implmentation
use stored procedure to simulate an
'oracle-like' sequence generation (Thanks Ryan)
- sequence manager implementation
now use long instead of int ids
- SequenceManagerHighLowImpl now use optimistic
locking
Revision Changes Path
1.16 +185 -48 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerHighLowImpl.java
Index: SequenceManagerHighLowImpl.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerHighLowImpl.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- SequenceManagerHighLowImpl.java 8 Mar 2003 19:22:56 -0000 1.15
+++ SequenceManagerHighLowImpl.java 1 Apr 2003 14:09:38 -0000 1.16
@@ -56,7 +56,14 @@
import org.apache.commons.lang.SystemUtils;
import org.apache.ojb.broker.PersistenceBroker;
+import org.apache.ojb.broker.PersistenceBrokerFactory;
+import org.apache.ojb.broker.PersistenceBrokerException;
+import org.apache.ojb.broker.OptimisticLockException;
+import org.apache.ojb.broker.TransactionNotInProgressException;
import org.apache.ojb.broker.metadata.FieldDescriptor;
+import org.apache.ojb.broker.query.Criteria;
+import org.apache.ojb.broker.query.Query;
+import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
@@ -66,12 +73,13 @@
/**
* High/Low sequence manager implementation generates unique and continuous
* id's (during runtime) by using sequences to avoid database access.
+ * <br/>
*
* <p>
* Implementation configuration properties:
* </p>
*
- * <table align="left" cellspacing="2" cellpadding="2" border="1" frame="box" rules="all">
+ * <table cellspacing="2" cellpadding="2" border="3" frame="box">
* <tr>
* <td><strong>Property Key</strong></td>
* <td><strong>Property Values</strong></td>
@@ -89,17 +97,33 @@
* <td>globalSequenceId</td>
* <td>
* If set 'true' implementation use global unique
- * id's for all fields. This modus is NOT extent aware!
- * Default was 'false'.
+ * id's for all fields. Default was 'false'.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>globalSequenceStart</td>
+ * <td>
+ * Set the start index of used global id
+ * generation (e.g. set 100000, id generation starts with 100001)
* </td>
* </tr>
* </table>
*
* <br/>
- * <br/>
+ * <p>
+ * <b>Limitations:</b>
+ * <ul>
+ * <li>Do NOT use this implementation in j2ee environment or
+ * any comparable system where any connection was associated
+ * with the running tx.</li>
+ * </ul>
+ * </p>
+ *
+ *
* <br/>
* <br/>
*
+ *
* @see org.apache.ojb.broker.util.sequence.SequenceManager
* @see org.apache.ojb.broker.util.sequence.SequenceManagerFactory
* @see org.apache.ojb.broker.util.sequence.SequenceGenerator
@@ -110,14 +134,21 @@
*/
public class SequenceManagerHighLowImpl extends AbstractSequenceManager
{
- private Logger log = LoggerFactory.getLogger(SequenceManagerHighLowImpl.class);
-
+ private static Logger log = LoggerFactory.getLogger(SequenceManagerHighLowImpl.class);
+ /**
+ * sequence name used for global id generation.
+ */
+ private static final String GLOBAL_SEQUENCE_NAME = "global - default sequence name";
public static final String PROPERTY_GRAB_SIZE = "grabSize";
public static final String PROPERTY_GLOBAL_SEQUENCE_ID = "globalSequenceId";
+ public static final String PROPERTY_GLOBAL_SEQUENCE_START = "globalSequenceStart";
private static Map sequencesMap = new HashMap();
+
protected boolean useGlobalSequenceIdentities;
protected int grabSize;
+ protected long globalSequenceStart;
+ private int attempts;
public SequenceManagerHighLowImpl(PersistenceBroker broker)
{
@@ -125,19 +156,17 @@
grabSize = new Integer(getConfigurationProperty(PROPERTY_GRAB_SIZE, "20")).intValue();
useGlobalSequenceIdentities =
new Boolean(getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_ID, "false")).booleanValue();
+ globalSequenceStart = new Long(
+ getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_START, "10000")).longValue();
}
- /**
- * Gets a new uniqueId for the given Class and fieldname
- */
- protected int getUniqueId(FieldDescriptor field)
- throws SequenceManagerException
+ protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
{
HighLowSequence seq;
String seqName;
if (useGlobalSequenceIdentities)
{
- seqName = SequenceManagerHelper.buildSequenceNameGlobal(getBrokerForClass(), field);
+ seqName = GLOBAL_SEQUENCE_NAME;
}
else
{
@@ -154,59 +183,167 @@
}
synchronized (sequencesMap)
{
- //try to found sequence in map
+ // try to found sequence in map
seq = (HighLowSequence) sequencesMap.get(seqName);
if (seq == null)
{
- // not found, create a new one and save it
- // get max id for class and extents from DB
- int lastId = SequenceManagerHelper.getMaxForExtent(getBrokerForClass(), field);
- //look up sequence
- seq = new HighLowSequence();
- seq.setTableName(seqName);
- seq.setFieldName(field.getColumnName());
- seq.setGrabSize(grabSize);
- seq.setMaxKey(lastId);
-
- /* @todo we need a callback mechanism to notify
- sequence manager if a rollback was done, because
- the obtained sequence entry in DB was rollback too and we
- have to remove current HighLowSequence*/
-
- seq = SequenceGenerator.getNextSequence(
- getBrokerForClass(), seq);
+ // not found, get sequence from database or create new
+ seq = getSequence(getBrokerForClass(), field, seqName);
sequencesMap.put(seqName, seq);
}
- //now we have a sequence
- int id = seq.getNextId();
+ // now we have a sequence
+ long id = seq.getNextId();
+ // seq does not have reserved IDs => catch new block of keys
if (id == 0)
{
- /* @todo we need a callback mechanism to notify
- sequence manager if a rollback was done, because
- the obtained sequence entry in DB was rollback too and we
- have to remove current HighLowSequence*/
-
- //seq does not have reserved IDs => reload seq
- seq = SequenceGenerator.getNextSequence(
- getBrokerForClass(), seq);
+ seq = getSequence(getBrokerForClass(), field, seqName);
// replace old sequence!!
sequencesMap.put(seqName, seq);
id = seq.getNextId();
+ if (id == 0)
+ {
+ // something going wrong
+ sequencesMap.remove(seqName);
+ throw new SequenceManagerException("Sequence generation failed: " +
+ SystemUtils.LINE_SEPARATOR + "Sequence: " + seq +
+ ". Unable to build new ID, id was always 0." +
+ SystemUtils.LINE_SEPARATOR + "Thread: " + Thread.currentThread() +
+ SystemUtils.LINE_SEPARATOR + "PB: " + getBrokerForClass());
+ }
}
- if (id == 0)
+
+ return id;
+ }
+ }
+
+ private HighLowSequence getSequence(PersistenceBroker brokerForSequence,
+ FieldDescriptor field,
+ String sequenceName)
+ {
+ HighLowSequence newSequence = null;
+ PersistenceBroker internBroker = null;
+ try
+ {
+ Criteria c = new Criteria();
+ c.addLike("tableName", sequenceName);
+ if (useGlobalSequenceIdentities)
{
- throw new SequenceManagerException("Sequence generation failed: " +
- SystemUtils.LINE_SEPARATOR + "Sequence: " + seq +
- ". Unable to build new ID, id was always 0." +
- SystemUtils.LINE_SEPARATOR + "Thread: " + Thread.currentThread() +
- SystemUtils.LINE_SEPARATOR + "PB: " + getBrokerForClass());
+ c.addLike("fieldName", PROPERTY_GLOBAL_SEQUENCE_ID);
}
else
{
- return id;
+ c.addLike("fieldName", field.getColumnName());
+ }
+ Query q = new QueryByCriteria(HighLowSequence.class, c);
+
+ /*
+ arminw:
+ we use a new internBroker instance, because we run into problems
+ when current internBroker was rollback, then we have new sequence
+ in memory, but not in database and a concurrent thread will
+ get the same sequence.
+ Thus we use a new internBroker instance (with new connection) to
+ avoid this problem.
+ */
+ internBroker = PersistenceBrokerFactory.createPersistenceBroker(brokerForSequence.getPBKey());
+ internBroker.beginTransaction();
+ // first we lookup sequence object in database
+ newSequence = (HighLowSequence) internBroker.getObjectByQuery(q);
+
+ //not in db --> we have to store a new sequence
+ if (newSequence == null)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("sequence for field " + field + " not found in db, store new HighLowSequence");
+ }
+ /*
+ here we lookup the max key for the given field in system
+ */
+ long maxKey = getMaxKeyForSequence(brokerForSequence, field);
+ newSequence = newSequenceObject(sequenceName, field);
+ newSequence.setMaxKey(maxKey);
+ }
+ //set current grab size
+ newSequence.setGrabSize(grabSize);
+ //use copy to avoid sync problems!!
+ // newSequence = newSequence.getCopy();
+
+ //grab the next key scope
+ newSequence.grabNextKeySet();
+
+ //store the sequence to db
+ try
+ {
+ /*
+ arminw:
+ remove object from cache to avoid problems when same
+ objects where used with different databases. Currently
+ (rc1) Identity does not differnce between databases, thus
+ we could found an object in cache although it is not
+ in the second database, it was used in the first database.
+ If we not remove it PB try to update instead of insert.
+ */
+ internBroker.removeFromCache(newSequence);
+ internBroker.store(newSequence);
+ }
+ catch (OptimisticLockException e)
+ {
+ // we try five times to get a new sequence
+ if(attempts < 5)
+ {
+ log.info("OptimisticLockException was thrown, will try again to store sequence. Sequence was "+newSequence);
+ attempts++;
+ getSequence(brokerForSequence, field, sequenceName);
+ }
+ else throw e;
}
+ internBroker.commitTransaction();
+
+ if (log.isDebugEnabled()) log.debug("new sequence was " + newSequence);
+ }
+ finally
+ {
+ attempts = 0;
+ if (internBroker != null) internBroker.close();
+ }
+ return newSequence;
+ }
+
+ private HighLowSequence newSequenceObject(String sequenceName,
+ FieldDescriptor field)
+ {
+ HighLowSequence seq = new HighLowSequence();
+ seq.setTableName(sequenceName);
+ if (useGlobalSequenceIdentities)
+ {
+ seq.setFieldName(PROPERTY_GLOBAL_SEQUENCE_ID);
+ }
+ else
+ {
+ seq.setFieldName(field.getColumnName());
+ }
+ seq.setGrabSize(grabSize);
+ return seq;
+ }
+
+ private long getMaxKeyForSequence(PersistenceBroker broker,
+ FieldDescriptor field)
+ {
+ long maxKey = 0;
+ if (useGlobalSequenceIdentities)
+ {
+ maxKey = globalSequenceStart;
+ }
+ else
+ {
+ /*
+ here we lookup the max key for the given field in system
+ */
+ maxKey = SequenceManagerHelper.getMaxForExtent(broker, field);
}
+ return maxKey;
}
}
1.4 +36 -26 db-ojb/src/java/org/apache/ojb/broker/util/sequence/HighLowSequence.java
Index: HighLowSequence.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/HighLowSequence.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- HighLowSequence.java 9 Aug 2002 10:49:20 -0000 1.3
+++ HighLowSequence.java 1 Apr 2003 14:09:38 -0000 1.4
@@ -1,5 +1,10 @@
package org.apache.ojb.broker.util.sequence;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+import java.io.Serializable;
+
/* ====================================================================
* The Apache Software License, Version 1.1
*
@@ -56,23 +61,22 @@
/**
- * The HighLowSequence is the persistent part of the HighLowSequenceGenerator.
- * It makes the maximum reserved key persistently availabe.
+ * The HighLowSequence is the persistent part of the {@link SequenceManagerHighLowImpl}.
+ * It makes the maximum reserved key persistently available.
*
* @author R Bischof
* @created 26. Oktober 2001
+ * @version $Id$
*/
-public class HighLowSequence
+public class HighLowSequence implements Serializable
{
private String tableName;
private String fieldName;
- private int maxKey;
+ private long maxKey;
private int grabSize;
- /**
- * This is not stored in the DB
- */
- private int curVal = 0;
-
+ private int version;
+ // This is not stored in the DB
+ private long curVal = 0;
/**
* Default constructor for the HighLowSequence object
@@ -81,16 +85,15 @@
{
}
-
/**
* Constructor for the HighLowSequence object
*
- * @param tableName The classname of this sequence
+ * @param tableName The name of the sequence
* @param fieldName The fieldname of this sequence
* @param maxKey The maximum key currently in use
* @param grabSize The grab size, i.e. the number of keys to reserve
*/
- public HighLowSequence(String tableName, String fieldName, int maxKey, int grabSize)
+ public HighLowSequence(String tableName, String fieldName, long maxKey, int grabSize)
{
this.tableName = tableName;
this.fieldName = fieldName;
@@ -105,8 +108,24 @@
public String toString()
{
- return "[sequence: tableName=" + tableName + " fieldName=" + fieldName + " currentKey=" +
- curVal + " maxKey=" + maxKey + " grabSize=" + grabSize + " " + super.toString() + "]";
+ ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE);
+ buf.append("tableName", tableName).
+ append("fieldName", fieldName).
+ append("grabSize", grabSize).
+ append("version", version).
+ append("maxKey", maxKey).
+ append("currentKey", curVal);
+ return buf.toString();
+ }
+
+ public int getVersion()
+ {
+ return version;
+ }
+
+ public void setVersion(int version)
+ {
+ this.version = version;
}
/**
@@ -119,7 +138,6 @@
this.tableName = tableName;
}
-
/**
* Sets the grab size attribute of the HighLowSequence object
*
@@ -130,7 +148,6 @@
this.grabSize = grabSize;
}
-
/**
* Sets the fieldName attribute of the HighLowSequence object
*
@@ -141,18 +158,16 @@
this.fieldName = fieldName;
}
-
/**
* Sets the maxKey attribute of the HighLowSequence object
*
* @param maxKey The new maxKey value
*/
- public void setMaxKey(int maxKey)
+ public void setMaxKey(long maxKey)
{
this.maxKey = maxKey;
}
-
/**
* Gets the className attribute of the HighLowSequence object
*
@@ -163,7 +178,6 @@
return this.tableName;
}
-
/**
* Gets the grabSize attribute of the HighLowSequence object
*
@@ -174,7 +188,6 @@
return this.grabSize;
}
-
/**
* Gets the fieldName attribute of the HighLowSequence object
*
@@ -185,13 +198,12 @@
return this.fieldName;
}
-
/**
* Gets the next key from this sequence
*
* @return The next key or 0 if sequence needs to grab new keyset
*/
- public int getNextId()
+ public long getNextId()
{
if (curVal == maxKey)
{
@@ -205,17 +217,15 @@
}
}
-
/**
* Gets the maxKey attribute of the HighLowSequence object
*
* @return The maxKey value
*/
- public int getMaxKey()
+ public long getMaxKey()
{
return this.maxKey;
}
-
/**
* Grabs the next key set, the sequence must be saved afterwards!!
1.7 +150 -150 db-ojb/src/java/org/apache/ojb/broker/util/sequence/AbstractSequenceManager.java
Index: AbstractSequenceManager.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/AbstractSequenceManager.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- AbstractSequenceManager.java 8 Mar 2003 19:22:56 -0000 1.6
+++ AbstractSequenceManager.java 1 Apr 2003 14:09:39 -0000 1.7
@@ -1,12 +1,5 @@
package org.apache.ojb.broker.util.sequence;
-import java.math.BigDecimal;
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.sql.Types;
-import java.util.Properties;
-
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.accesslayer.JdbcAccess;
import org.apache.ojb.broker.metadata.ClassDescriptor;
@@ -16,6 +9,13 @@
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Properties;
+
/**
* A base class for sequence manager implementations.
@@ -58,6 +58,13 @@
}
}
+ /**
+ * returns a unique long value for field.
+ * the returned number is unique accross all tables in the extent of clazz.
+ */
+ abstract protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException;
+
+
public Platform getPlatform()
{
return platform;
@@ -92,7 +99,7 @@
//****************************************************************
// method implementations of SequenceManager interface
- //****************************************************************
+ //****************************************************************
/**
* Returns a unique object for the given field attribute.
* The returned value takes in account the jdbc-type
@@ -102,161 +109,138 @@
*/
public Object getUniqueValue(FieldDescriptor field) throws SequenceManagerException
{
- Object result = null;
- switch (field.getColumnJdbcType())
- {
- case Types.ARRAY:
- {
- Object[] arr = {getUniqueString(field)};
- result = arr;
- break;
- }
- case Types.BIGINT:
- {
- result = new Long(getUniqueLong(field));
- break;
- }
- case Types.BINARY:
- {
- result = (byte[]) getUniqueString(field).getBytes();
- break;
- }
- case Types.CHAR:
- {
- result = getUniqueString(field);
- break;
- }
- case Types.DATE:
- {
- result = new Date(getUniqueLong(field));
- break;
- }
- case Types.DECIMAL:
- {
- result = new BigDecimal(getUniqueLong(field));
- break;
- }
- case Types.DOUBLE:
- {
- result = new Double(getUniqueLong(field));
- break;
- }
- case Types.FLOAT:
- {
- result = new Double(getUniqueLong(field));
- break;
- }
- case Types.INTEGER:
- {
- result = new Integer(getUniqueId(field));
- break;
- }
- case Types.JAVA_OBJECT:
- {
- result = getUniqueObject(field);
- break;
- }
- case Types.LONGVARBINARY:
- {
- result = (byte[]) getUniqueString(field).getBytes();
- break;
- }
- case Types.LONGVARCHAR:
- {
- result = getUniqueString(field);
- break;
- }
- case Types.NUMERIC:
- {
- result = new BigDecimal(getUniqueLong(field));
- break;
- }
- case Types.REAL:
- {
- result = new Float(getUniqueLong(field));
- break;
- }
- case Types.SMALLINT:
- {
- result = new Short((short)getUniqueId(field));
- break;
- }
- case Types.TIME:
- {
- result = new Time(getUniqueLong(field));
- break;
- }
- case Types.TIMESTAMP:
- {
- result = new Timestamp(getUniqueLong(field));
- break;
- }
- case Types.TINYINT:
- {
- result = new Byte((byte)getUniqueId(field));
- break;
- }
- case Types.VARBINARY:
- {
- result = (byte[]) getUniqueString(field).getBytes();
- break;
- }
- case Types.VARCHAR:
- {
- result = getUniqueString(field);
- break;
- }
- default:
- {
- result = getUniqueString(field);
- break;
- }
- }
- // perform a sql to java conversion here, so that clients do
- // not see any db specific values
- result = field.getFieldConversion().sqlToJava(result);
+ Object result = null;
+ switch (field.getColumnJdbcType())
+ {
+ case Types.ARRAY:
+ {
+ Object[] arr = {getUniqueString(field)};
+ result = arr;
+ break;
+ }
+ case Types.BIGINT:
+ {
+ result = new Long(getUniqueLong(field));
+ break;
+ }
+ case Types.BINARY:
+ {
+ result = (byte[]) getUniqueString(field).getBytes();
+ break;
+ }
+ case Types.CHAR:
+ {
+ result = getUniqueString(field);
+ break;
+ }
+ case Types.DATE:
+ {
+ result = new Date(getUniqueLong(field));
+ break;
+ }
+ case Types.DECIMAL:
+ {
+ result = new BigDecimal(getUniqueLong(field));
+ break;
+ }
+ case Types.DOUBLE:
+ {
+ result = new Double(getUniqueLong(field));
+ break;
+ }
+ case Types.FLOAT:
+ {
+ result = new Double(getUniqueLong(field));
+ break;
+ }
+ case Types.INTEGER:
+ {
+ result = new Integer(getUniqueId(field));
+ break;
+ }
+ case Types.JAVA_OBJECT:
+ {
+ result = getUniqueObject(field);
+ break;
+ }
+ case Types.LONGVARBINARY:
+ {
+ result = (byte[]) getUniqueString(field).getBytes();
+ break;
+ }
+ case Types.LONGVARCHAR:
+ {
+ result = getUniqueString(field);
+ break;
+ }
+ case Types.NUMERIC:
+ {
+ result = new BigDecimal(getUniqueLong(field));
+ break;
+ }
+ case Types.REAL:
+ {
+ result = new Float(getUniqueLong(field));
+ break;
+ }
+ case Types.SMALLINT:
+ {
+ result = new Short((short) getUniqueId(field));
+ break;
+ }
+ case Types.TIME:
+ {
+ result = new Time(getUniqueLong(field));
+ break;
+ }
+ case Types.TIMESTAMP:
+ {
+ result = new Timestamp(getUniqueLong(field));
+ break;
+ }
+ case Types.TINYINT:
+ {
+ result = new Byte((byte) getUniqueId(field));
+ break;
+ }
+ case Types.VARBINARY:
+ {
+ result = (byte[]) getUniqueString(field).getBytes();
+ break;
+ }
+ case Types.VARCHAR:
+ {
+ result = getUniqueString(field);
+ break;
+ }
+ default:
+ {
+ result = getUniqueString(field);
+ break;
+ }
+ }
+ // perform a sql to java conversion here, so that clients do
+ // not see any db specific values
+ result = field.getFieldConversion().sqlToJava(result);
return result;
- }
-
- /**
- * noop
- */
- public void afterStore(JdbcAccess dbAccess, ClassDescriptor cld, Object obj)
- throws SequenceManagerException
- {
}
/**
- * noop
- */
- public void setReferenceFKs(Object obj, ClassDescriptor cld)
- throws SequenceManagerException
- {
- }
-
- /**
- * returns a new unique int for the given Class and fieldname
- */
- abstract protected int getUniqueId(FieldDescriptor field)
- throws SequenceManagerException;
-
- /**
* returns a unique String for class clazz and field fieldName.
* the returned uid is unique accross all tables in the extent of clazz.
*
*/
protected String getUniqueString(FieldDescriptor field) throws SequenceManagerException
{
- return Integer.toString(getUniqueId(field));
+ return Long.toString(getUniqueLong(field));
}
/**
- * returns a unique long value for class clazz and field fieldName.
- * the returned number is unique accross all tables in the extent of clazz.
+ * Returns a new unique int for the given Class and fieldname.
*/
- protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
+ protected int getUniqueId(FieldDescriptor field) throws SequenceManagerException
{
- return (long) getUniqueId(field);
+ return (int) getUniqueLong(field);
}
/**
@@ -267,5 +251,21 @@
{
return getUniqueString(field);
}
-
+
+ /**
+ * noop
+ */
+ public void afterStore(JdbcAccess dbAccess, ClassDescriptor cld, Object obj)
+ throws SequenceManagerException
+ {
+ }
+
+ /**
+ * noop
+ */
+ public void setReferenceFKs(Object obj, ClassDescriptor cld)
+ throws SequenceManagerException
+ {
+ }
}
+
1.9 +27 -19 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerHelper.java
Index: SequenceManagerHelper.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerHelper.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- SequenceManagerHelper.java 12 Jan 2003 10:03:32 -0000 1.8
+++ SequenceManagerHelper.java 1 Apr 2003 14:09:39 -0000 1.9
@@ -30,7 +30,7 @@
/**
* Prefix for global sequence names.
*/
- private static final String GLOBAL_SEQUENCE_NAME = "global - ";
+ //private static final String GLOBAL_SEQUENCE_NAME = "global - default sequence name";
/**
* Returns a unique sequence name (unique across all extents).
@@ -75,6 +75,14 @@
*/
if (cldTopLevel.isExtent())
{
+ /*
+ TODO
+ arminw:
+ this is a little critical, because we do not know if the extent classes
+ will change by and by and the first found extent class may change, thus the
+ returned table name could change!
+ But I don't know a way to resolve this problem. I put a comment to the docs
+ */
seqName = brokerForClass.getClassDescriptor(((Class) cldTopLevel.getExtentClasses().
get(0))).getFullTableName();
}
@@ -86,23 +94,23 @@
return SEQ_PREFIX + seqName;
}
- /**
- * Build a global sequence name unique across all tables.
- */
- public static String buildSequenceNameGlobal(PersistenceBroker brokerForClass, FieldDescriptor field)
- {
- return GLOBAL_SEQUENCE_NAME + (field != null ? field.getColumnName() : "defaultField");
- }
+// /**
+// * Build a global sequence name unique across all tables.
+// */
+// public static String buildSequenceNameGlobal()
+// {
+// return GLOBAL_SEQUENCE_NAME;
+// }
/**
* Lookup all tables associated with given class (search all extent classes)
* to find the current maximum value for the given field.
- * <br><b>Note:</b> Only works for integer single autoincrement fields.
+ * <br><b>Note:</b> Only works for <code>long</code> autoincrement fields.
* @param brokerForClass persistence broker instance match the database of the
- * given class
- * @param clazz the target class
+ * given field/class
+ * @param field the target field
*/
- public static int getMaxForExtent(PersistenceBroker brokerForClass, FieldDescriptor field) throws PersistenceBrokerException
+ public static long getMaxForExtent(PersistenceBroker brokerForClass, FieldDescriptor field) throws PersistenceBrokerException
{
if (field == null)
{
@@ -119,10 +127,10 @@
* Search down all extent classes and return max of all found
* PK values.
*/
- private static int getMaxId(PersistenceBroker brokerForClass, Class topLevel, FieldDescriptor original) throws PersistenceBrokerException
+ private static long getMaxId(PersistenceBroker brokerForClass, Class topLevel, FieldDescriptor original) throws PersistenceBrokerException
{
- int max = 0;
- int tmp = 0;
+ long max = 0;
+ long tmp = 0;
ClassDescriptor cld = brokerForClass.getClassDescriptor(topLevel);
// if class is no Interface we have to search its directly mapped table
@@ -167,7 +175,7 @@
* lookup current maximum value for a single field in
* table the given class descriptor was associated.
*/
- private static int getMaxIdForClass(
+ private static long getMaxIdForClass(
PersistenceBroker brokerForClass, ClassDescriptor cldForOriginalOrExtent, FieldDescriptor original)
throws PersistenceBrokerException
{
@@ -193,7 +201,7 @@
}
String column = field.getColumnName();
- int result = 0;
+ long result = 0;
ResultSet rs = null;
Statement stmt = null;
String table = cldForOriginalOrExtent.getFullTableName();
@@ -205,7 +213,7 @@
stmt = brokerForClass.serviceStatementManager().getGenericStatement(cldForOriginalOrExtent, Query.NOT_SCROLLABLE);
rs = stmt.executeQuery(sql);
rs.next();
- result = rs.getInt(1);
+ result = rs.getLong(1);
}
catch (Exception e)
{
1.9 +14 -45 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerInMemoryImpl.java
Index: SequenceManagerInMemoryImpl.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerInMemoryImpl.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- SequenceManagerInMemoryImpl.java 8 Mar 2003 19:22:56 -0000 1.8
+++ SequenceManagerInMemoryImpl.java 1 Apr 2003 14:09:39 -0000 1.9
@@ -68,35 +68,25 @@
* </p>
* <b>Limitations:</b>
* <ul type="disc">
- * <li>do not use in client/server mode</li>
* <li>do not use in clustered environments</li>
- * <li>do not use if other entities generate id's for objects</li>
+ * <li>do not use if other applications generate id's for objects</li>
* </ul>
*
+ *
+ *
* <p>
* Implementation configuration properties:
* </p>
*
- * <table align="left" cellspacing="2" cellpadding="2" border="1" frame="box" rules="all">
+ * <table cellspacing="2" cellpadding="2" border="3" frame="box">
* <tr>
* <td><strong>Property Key</strong></td>
* <td><strong>Property Values</strong></td>
* </tr>
* <tr>
- * <td>grabSize</td>
+ * <td>none</td>
* <td>
- * Integer entry determines the
- * number of IDs allocated within the
- * H/L sequence manager implementation.
- * Default was '20'.
- * </td>
- * </tr>
- * <tr>
- * <td>globalSequenceId</td>
- * <td>
- * If set 'true' implementation use global unique
- * id's for all fields. This modus is NOT extent aware!
- * Default was 'false'.
+ *
* </td>
* </tr>
* </table>
@@ -104,62 +94,41 @@
* <br/>
* <br/>
* <br/>
- * <br/>
- * <br/>
- * <br/>
- * <br/>
*
* @see org.apache.ojb.broker.util.sequence.SequenceManager
* @see org.apache.ojb.broker.util.sequence.SequenceManagerFactory
* @see org.apache.ojb.broker.util.sequence.SequenceGenerator
* @see org.apache.ojb.broker.util.sequence.SequenceManagerHelper
- *
* @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
* @version $Id$
*/
public class SequenceManagerInMemoryImpl extends AbstractSequenceManager
{
- public static final String PROPERTY_GLOBAL_SEQUENCE_ID = "globalSequenceId";
private static HashMap sequenceNameKeyMap = new HashMap();
- protected boolean useGlobalSequenceIdentities;
public SequenceManagerInMemoryImpl(PersistenceBroker broker)
{
super(broker);
- useGlobalSequenceIdentities =
- new Boolean(getConfigurationProperty(PROPERTY_GLOBAL_SEQUENCE_ID, "false")).booleanValue();
}
- protected int getUniqueId(FieldDescriptor field) throws SequenceManagerException
+ protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
{
- String seqName;
- int retId = 0;
- if (useGlobalSequenceIdentities)
- {
- seqName = SequenceManagerHelper.buildSequenceNameGlobal(getBrokerForClass(), field);
- }
- else
- {
- seqName = SequenceManagerHelper.buildSequenceName(getBrokerForClass(), field);
- }
-
+ String seqName = SequenceManagerHelper.buildSequenceName(getBrokerForClass(), field);
+ // we have to be threadsafe
synchronized (sequenceNameKeyMap)
{
// get id for given seq name
- Integer currentId = (Integer) sequenceNameKeyMap.get(seqName);
+ Long currentId = (Long) sequenceNameKeyMap.get(seqName);
// check - first time we search for sequence name
if (currentId == null)
{
- currentId = new Integer(
+ currentId = new Long(
SequenceManagerHelper.getMaxForExtent(getBrokerForClass(), field));
- // check for start index
- currentId = new Integer(currentId.intValue());
}
- retId = currentId.intValue() + 1;
- currentId = new Integer(retId);
+ currentId = new Long(currentId.longValue() + 1);
// put new id back to map
sequenceNameKeyMap.put(seqName, currentId);
+ return currentId.intValue();
}
- return retId;
}
}
1.11 +103 -43 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerMySQLImpl.java
Index: SequenceManagerMySQLImpl.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerMySQLImpl.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- SequenceManagerMySQLImpl.java 8 Mar 2003 19:22:56 -0000 1.10
+++ SequenceManagerMySQLImpl.java 1 Apr 2003 14:09:39 -0000 1.11
@@ -1,17 +1,57 @@
-/**
+package org.apache.ojb.broker.util.sequence;
+/* ====================================================================
+ * The Apache Software License, Version 1.1
*
- * MySQL SequenceManager for use with auto_increment columns.
- * <p>
- * WARNING: Not thoroughly tested, use at own risk. ;-)
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
*
- * @author Travis Reeder - travis@spaceprogram.com
- * @version $Id$
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
*
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache ObjectRelationalBridge" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache ObjectRelationalBridge", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
*/
-package org.apache.ojb.broker.util.sequence;
-
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.SQLException;
@@ -21,8 +61,6 @@
import java.util.Iterator;
import java.util.Vector;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.accesslayer.JdbcAccess;
@@ -33,34 +71,58 @@
import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.util.ProxyHelper;
+import org.apache.ojb.broker.util.logging.Logger;
+import org.apache.ojb.broker.util.logging.LoggerFactory;
-
+/**
+ * MySQL SequenceManager for use with auto_increment columns.
+ * <p>
+ * <b>WARNING:</b> Not thoroughly tested, use at own risk. ;-)
+ * <br/>
+ * Native key generation is not extent aware if the extent persistent
+ * objects using different database tables.
+ *
+ * <p>
+ * Implementation configuration properties:
+ * </p>
+ *
+ * <table cellspacing="2" cellpadding="2" border="3" frame="box">
+ * <tr>
+ * <td><strong>Property Key</strong></td>
+ * <td><strong>Property Values</strong></td>
+ * </tr>
+ * <tr>
+ * <td>none</td>
+ * <td>
+ *
+ * </td>
+ * </tr>
+ * </table>
+ * <br/>
+ * <br/>
+ *
+ * @author Travis Reeder - travis@spaceprogram.com
+ * @version $Id$
+ *
+ */
public class SequenceManagerMySQLImpl implements SequenceManager
{
-
- private Log log = LogFactory.getLog(this.getClass());
+ private Logger log = LoggerFactory.getLogger(SequenceManagerMySQLImpl.class);
/**
* reference to the PersistenceBroker
*/
protected PersistenceBroker broker;
-
/**
* singleton instance of the SequenceManager
*/
private static SequenceManagerMySQLImpl _instance;
- /**
- *
- * Public constructor
- *
- */
public SequenceManagerMySQLImpl(PersistenceBroker broker)
{
this.broker = broker;
}
-
/**
* Returns next Id get from "sequence sequence.NextVal from Dual".
* Mount sequence name as:
@@ -214,97 +276,97 @@
Object[] arr = {getUniqueString(field)};
result = arr;
break;
- }
+ }
case Types.BIGINT:
{
result = new Long(getUniqueLong(field));
break;
- }
+ }
case Types.BINARY:
{
result = (byte[]) getUniqueString(field).getBytes();
break;
- }
+ }
case Types.CHAR:
{
result = getUniqueString(field);
break;
- }
+ }
case Types.DATE:
{
result = new Date(getUniqueLong(field));
break;
- }
+ }
case Types.DECIMAL:
{
result = new BigDecimal(getUniqueLong(field));
break;
- }
+ }
case Types.DOUBLE:
{
result = new Double(getUniqueLong(field));
break;
- }
+ }
case Types.FLOAT:
{
result = new Double(getUniqueLong(field));
break;
- }
+ }
case Types.INTEGER:
{
result = new Integer(getUniqueId(field));
break;
- }
+ }
case Types.JAVA_OBJECT:
{
result = getUniqueObject(field);
break;
- }
+ }
case Types.LONGVARBINARY:
{
result = (byte[]) getUniqueString(field).getBytes();
break;
- }
+ }
case Types.LONGVARCHAR:
{
result = getUniqueString(field);
break;
- }
+ }
case Types.NUMERIC:
{
result = new BigDecimal(getUniqueLong(field));
break;
- }
+ }
case Types.REAL:
{
result = new Float(getUniqueLong(field));
break;
- }
+ }
case Types.SMALLINT:
{
result = new Short((short)getUniqueId(field));
break;
- }
+ }
case Types.TIME:
{
result = new Time(getUniqueLong(field));
break;
- }
+ }
case Types.TIMESTAMP:
{
result = new Timestamp(getUniqueLong(field));
break;
- }
+ }
case Types.TINYINT:
{
result = new Byte((byte)getUniqueId(field));
break;
- }
+ }
case Types.VARBINARY:
{
result = (byte[]) getUniqueString(field).getBytes();
break;
- }
+ }
case Types.VARCHAR:
{
result = getUniqueString(field);
@@ -313,12 +375,10 @@
default:
{
result = getUniqueString(field);
- break;
+ break;
}
}
result = field.getFieldConversion().sqlToJava(result);
return result;
- }
-
-
+ }
}
1.8 +24 -2 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerNextValImpl.java
Index: SequenceManagerNextValImpl.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerNextValImpl.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- SequenceManagerNextValImpl.java 8 Mar 2003 19:22:56 -0000 1.7
+++ SequenceManagerNextValImpl.java 1 Apr 2003 14:09:39 -0000 1.8
@@ -86,7 +86,29 @@
* <p>
* Implementation configuration properties:
* </p>
- * None.
+ *
+ * <table cellspacing="2" cellpadding="2" border="3" frame="box">
+ * <tr>
+ * <td><strong>Property Key</strong></td>
+ * <td><strong>Property Values</strong></td>
+ * </tr>
+ * <tr>
+ * <td>none</td>
+ * <td>
+ *
+ * </td>
+ * </tr>
+ * </table>
+ *
+ *
+ * <br/>
+ * <p>
+ * <b>Limitations:</b>
+ * <ul>
+ * <li>none</li>
+ * </ul>
+ * </p>
+ * <br/>
* <br/>
*
* @author Edson Carlos Ericksson Richter
1.4 +51 -18 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerSeqHiLoImpl.java
Index: SequenceManagerSeqHiLoImpl.java
===================================================================
RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerSeqHiLoImpl.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- SequenceManagerSeqHiLoImpl.java 8 Mar 2003 19:22:56 -0000 1.3
+++ SequenceManagerSeqHiLoImpl.java 1 Apr 2003 14:09:39 -0000 1.4
@@ -57,6 +57,8 @@
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.metadata.FieldDescriptor;
+import java.util.HashMap;
+
/**
* <p>
* A High/Low database sequence based implementation.
@@ -64,16 +66,11 @@
* for more information.
* </p>
*
- * <b>Limitations:</b>
- * <ul type="disc">
- * <li>do not use when other applications use the database sequence ditto</li>
- * </ul>
- *
* <p>
* Implementation configuration properties:
* </p>
*
- * <table align="left" cellspacing="2" cellpadding="2" border="1" frame="box" rules="all">
+ * <table cellspacing="2" cellpadding="2" border="3" frame="box">
* <tr>
* <td><strong>Property Key</strong></td>
* <td><strong>Property Values</strong></td>
@@ -88,9 +85,13 @@
* </td>
* </tr>
* </table>
- *
- * <br/>
* <br/>
+ * <p>
+ * <b>Limitations:</b>
+ * <ul>
+ * <li>do not use when other applications use the database based sequence ditto</li>
+ * </ul>
+ * </p>
* <br/>
* <br/>
*
@@ -100,24 +101,56 @@
public class SequenceManagerSeqHiLoImpl extends SequenceManagerNextValImpl
{
public static final String PROPERTY_GRAB_SIZE = SequenceManagerHighLowImpl.PROPERTY_GRAB_SIZE;
- protected int grabSize;
+ private static HashMap hiLoMap = new HashMap();
- private int counter;
- private int maxVal;
+ protected int grabSize;
public SequenceManagerSeqHiLoImpl(PersistenceBroker broker)
{
super(broker);
- grabSize = counter = new Integer(getConfigurationProperty(PROPERTY_GRAB_SIZE, "20")).intValue();
+ grabSize = new Integer(getConfigurationProperty(PROPERTY_GRAB_SIZE, "20")).intValue();
}
- protected int getUniqueId(FieldDescriptor field) throws SequenceManagerException
+ protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
{
- if (counter == grabSize)
+ String sequenceName = SequenceManagerHelper.buildSequenceName(getBrokerForClass(), field);
+ // we have to be threadsafe
+ synchronized (hiLoMap)
+ {
+ HiLoEntry entry = (HiLoEntry) hiLoMap.get(sequenceName);
+ if (entry == null)
+ {
+ entry = new HiLoEntry(grabSize, grabSize);
+ hiLoMap.put(sequenceName, entry);
+ }
+ if (entry.needNewSequence())
+ {
+ entry.maxVal = grabSize * (super.getUniqueLong(field) + 1);
+ entry.counter = 0;
+ }
+ return entry.nextVal();
+ }
+ }
+
+ class HiLoEntry
+ {
+ long maxVal;
+ long counter;
+
+ public HiLoEntry(long maxVal, long counter)
+ {
+ this.maxVal = maxVal;
+ this.counter = counter;
+ }
+
+ boolean needNewSequence()
+ {
+ return counter == grabSize;
+ }
+
+ long nextVal()
{
- maxVal = grabSize * (super.getUniqueId(field) + 1);
- counter = 0;
+ return maxVal + counter++;
}
- return maxVal + counter++;
}
}
1.1 db-ojb/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerStoredProcedureImpl.java
Index: SequenceManagerStoredProcedureImpl.java
===================================================================
package org.apache.ojb.broker.util.sequence;
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache ObjectRelationalBridge" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache ObjectRelationalBridge", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
import org.apache.commons.lang.SystemUtils;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.accesslayer.LookupException;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
/**
* This solution will give those seeking an oracle-style
* sequence generator a final answer (Identity columns really suck).
* <br/>
* The <code>SequenceManagerStoredProcedureImpl</code> implementation enabled database
* sequence key generation for all databases (e.g. MSSQL, MySQL, DB2, ...)
* with a <b>JDBC 2.0</b> compliant driver.
* <br/>
* First add a new table <code>OJB_NEXTVAL</code> to
* your database.
* <pre>
* CREATE TABLE OJB_NEXTVAL
* (
* SEQ_NAME VARCHAR(150) NOT NULL,
* MAX_KEY BIGINT,
* CONSTRAINT SYS_PK_OJB_NEXTVAL PRIMARY KEY(SEQ_NAME)
* )
* </pre>
* You will also need the stored procedure OJB_NEXTVAL
* will will take care of giving you a guaranteed unique
* sequence number, in multi server environments.
* <br/>
* <pre>
* CREATE PROCEDURE ojb_nextval @SEQ_NAME varchar(100)
* AS
* declare @MAX_KEY BIGINT
* -- return an error if sequence does not exist
* -- so we will know if someone truncates the table
* set @MAX_KEY = 0
*
* UPDATE OJB_NEXTVAL
* SET @MAX_KEY = MAX_KEY = MAX_KEY + 1
* WHERE TABLENAME = @SEQ_NAME
*
* if @MAX_KEY = 0
* select 1/0
* else
* select @MAX_KEY
*
* RETURN @MAX_KEY
* </pre>
* <br/>
* It is possible to define a <code>sequence-name</code>
* field-descriptor attribute in the repository file. If
* such an attribute was not found, the implementation build
* an extent aware sequence name by its own.
* <br/>
* Keep in mind when define a sequence name, that you are responsible
* to be aware of extents, that is: if you ask for an uid for an
* interface with several
* implementor classes, or a baseclass with several subclasses the returned
* uid have to be unique accross all tables representing objects of the
* extent in question. Thus you have to use the same <code>sequence-name</code>
* for all extents.
*
* <p>
* Implementation configuration properties:
* </p>
*
* <table cellspacing="2" cellpadding="2" border="3" frame="box">
* <tr>
* <td><strong>Property Key</strong></td>
* <td><strong>Property Values</strong></td>
* </tr>
* <tr>
* <td>none</td>
* <td>
*
* </td>
* </tr>
* </table>
*
* <p>
* <b>Limitations:</b>
* <ul>
* <li>do not use when other application use the native key generation ditto</li>
* </ul>
* </p>
* <br/>
* <br/>
*
* @author Ryan Vanderwerf
* @author Edson Carlos Ericksson Richter
* @author Rajeev Kaul
* @author Thomas Mahler
* @author Armin Waibel
* @version $Id: SequenceManagerStoredProcedureImpl.java,v 1.1 2003/04/01 14:09:39 arminw Exp $
*/
public class SequenceManagerStoredProcedureImpl extends AbstractSequenceManager
{
private Logger log = LoggerFactory.getLogger(SequenceManagerStoredProcedureImpl.class);
protected static final String PROCEDURE_NAME = "ojb_nextval";
protected static final String SEQ_NAME_STRING = "SEQ_NAME";
protected static final String SEQ_ID_STRING = "MAX_KEY";
protected static final String SEQ_TABLE_NAME = "OJB_NEXTVAL";
/**
*
*/
public SequenceManagerStoredProcedureImpl(PersistenceBroker broker)
{
super(broker);
}
protected String sp_createSequenceQuery(String sequenceName, long maxKey)
{
String result = "insert into " + SEQ_TABLE_NAME + " ("
+ SEQ_NAME_STRING + "," + SEQ_ID_STRING +
") values ('" + sequenceName + "'," + maxKey + ")";
return result;
}
protected String sp_buildProcedureString(String sequenceName)
{
return "{ call " + PROCEDURE_NAME + " (?, ?)" + "}";
}
protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
{
long result = 0;
/*
arminw:
we obtain a new PB instance to make
*/
PersistenceBroker targetBroker = getBrokerForClass();
// lookup sequence name
String sequenceName = SequenceManagerHelper.buildSequenceName(getBrokerForClass(), field);
try
{
result = buildNextSequence(targetBroker, field.getClassDescriptor(), sequenceName);
/*
if 0 was returned we assume that the stored procedure
did not work properly.
*/
if (result == 0)
{
throw new SequenceManagerException("No incremented value retrieved");
}
}
catch (Exception e)
{
// maybe the sequence was not created
log.info("Could not grap next key, message was " + e.getMessage() +
" - try to write a new sequence entry to database");
try
{
// on create, make sure to get the max key for the table first
long maxKey = SequenceManagerHelper.getMaxForExtent(getBrokerForClass(), field);
createSequence(targetBroker, field, sequenceName, maxKey);
}
catch (Exception e1)
{
String eol = SystemUtils.LINE_SEPARATOR;
throw new SequenceManagerException(eol + "Could not grab next id, failed with " + eol +
e.getMessage() + eol + "Creation of new sequence failed with " +
eol + e1.getMessage() + eol, e1);
}
try
{
result = buildNextSequence(targetBroker, field.getClassDescriptor(), sequenceName);
}
catch (Exception e1)
{
throw new SequenceManagerException("Could not grab next id, sequence seems to exist", e);
}
}
return result;
}
protected long buildNextSequence(PersistenceBroker broker, ClassDescriptor cld, String sequenceName)
throws LookupException, SQLException
{
CallableStatement cs = null;
try
{
String sp = "{ call " + PROCEDURE_NAME + " (?, ?)}";
cs = broker.serviceConnectionManager().
getConnection().prepareCall(sp);
cs.setString(1, sequenceName);
cs.registerOutParameter(2, Types.INTEGER);
cs.executeUpdate();
// cs.getLong(2);
return cs.getLong(2);
}
finally
{
try
{
if (cs != null)
cs.close();
}
catch (SQLException ignore)
{
}
}
}
protected void createSequence(PersistenceBroker broker, FieldDescriptor field,
String sequenceName, long maxKey) throws Exception
{
Statement stmt = null;
try
{
stmt = getBrokerForClass().serviceStatementManager().getGenericStatement(field.getClassDescriptor(), Query.NOT_SCROLLABLE);
stmt.execute(sp_createSequenceQuery(sequenceName, maxKey));
}
catch (Exception e)
{
e.printStackTrace();
throw new SequenceManagerException("Could create new row in OJB_HL_SEQ table - TABLENAME=" +
sequenceName + " field=" + field.getColumnName(), e);
}
finally
{
try
{
if (stmt != null) stmt.close();
}
catch (SQLException ignore)
{
}
}
}
}