diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java
index b1beb7eee..fae8d80f9 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java
@@ -55,6 +55,8 @@ import serp.util.Strings;
/**
* {@link JDBCSeq} implementation that uses a database sequences
* to generate numbers.
+ * Supports allocation (caching). In order for allocation to work properly, the database sequence must be defined
+ * with INCREMENT BY value equal to allocate * increment.
*
* @see JDBCSeq
* @see AbstractJDBCSeq
@@ -73,9 +75,11 @@ public class NativeJDBCSeq
private DBIdentifier _seqName = DBIdentifier.newSequence("OPENJPA_SEQUENCE");
private int _increment = 1;
private int _initial = 1;
- private int _allocate = 0;
+ private int _allocate = 50;
private Sequence _seq = null;
private String _select = null;
+ private long _nextValue = 0;
+ private long _maxValue = -1;
// for deprecated auto-configuration support
private String _format = null;
@@ -203,29 +207,76 @@ public class NativeJDBCSeq
public void endConfiguration() {
buildSequence();
- DBDictionary dict = _conf.getDBDictionaryInstance();
- if (_format == null) {
- _format = dict.nextSequenceQuery;
- if (_format == null)
- throw new MetaDataException(_loc.get("no-seq-sql", _seqName));
- }
if (DBIdentifier.isNull(_tableName))
_tableName = DBIdentifier.newTable("DUAL");
+ DBDictionary dict = _conf.getDBDictionaryInstance();
String name = dict.getFullName(_seq);
- Object[] subs = (_subTable) ? new Object[]{ name, _tableName }
+
+ if (dict.useNativeSequenceCache){
+ if (_format == null) {
+ _format = dict.nextSequenceQuery;
+ if (_format == null)
+ throw new MetaDataException(_loc.get("no-seq-sql", _seqName));
+ }
+
+ Object[] subs = (_subTable) ? new Object[]{ name, _tableName }
: new Object[]{ name };
- _select = MessageFormat.format(_format, subs);
+ _select = MessageFormat.format(_format, subs);
+ }
+ else {
+ String format = dict.nextSequenceQuery;
+ if (format == null) {
+ throw new MetaDataException(_loc.get("no-seq-sql", _seqName));
+ }
+
+ // Increment step is needed for Firebird which uses non-standard sequence fetch syntax.
+ // Use String.valueOf to get rid of possible locale-specific number formatting.
+ _select = MessageFormat.format(format, new Object[]{name,
+ String.valueOf(_allocate * _increment)});
+ }
type = dict.nativeSequenceType;
}
@Override
- protected Object nextInternal(JDBCStore store, ClassMapping mapping)
+ protected synchronized Object nextInternal(JDBCStore store, ClassMapping mapping)
throws SQLException {
+ DBDictionary dict = _conf.getDBDictionaryInstance();
+
+ //To maintain existing behavior call allocateInternal to get the next
+ //sequence value, which it stores in _nextValue, and simply return the value.
+ if (dict.useNativeSequenceCache){
+ allocateInternal(0, store, mapping);
+ return _nextValue;
+ }
+
+ if (_nextValue < _maxValue) {
+ long result = _nextValue;
+ _nextValue += _increment;
+ return result;
+ }
+
+ allocateInternal(0, store, mapping);
+ long result = _nextValue;
+ _nextValue += _increment;
+ return result;
+ }
+
+ /**
+ * Allocate additional sequence values.
+ * @param additional ignored - the allocation size is fixed and determined by allocate and increment properties.
+ * @param store used to obtain connection
+ * @param mapping ignored
+ */
+ @Override
+ protected synchronized void allocateInternal(int additional, JDBCStore store, ClassMapping mapping)
+ throws SQLException {
+
Connection conn = getConnection(store);
try {
- return getSequence(conn);
+ _nextValue = getSequence(conn);
+ _maxValue = _nextValue + _allocate * _increment;
} finally {
closeConnection(conn);
}
@@ -300,9 +351,7 @@ public class NativeJDBCSeq
try {
stmnt = conn.prepareStatement(_select);
dict.setTimeouts(stmnt, _conf, false);
- synchronized(this) {
- rs = stmnt.executeQuery();
- }
+ rs = stmnt.executeQuery();
if (rs.next())
return rs.getLong(1);
@@ -327,13 +376,12 @@ public class NativeJDBCSeq
* Where the following options are recognized.
*
* - -properties/-p <properties file or resource>: The
- * path or resource name of a OpenJPA properties file containing
- * information such as the license key and connection data as
+ * path or resource name of an OpenJPA properties file containing
+ * information such as connection data as
* outlined in {@link JDBCConfiguration}. Optional.
* - -<property name> <property value>: All bean
* properties of the OpenJPA {@link JDBCConfiguration} can be set by
- * using their names and supplying a value. For example:
- *
-licenseKey adslfja83r3lkadf
+ * using their names and supplying a value.
*
* The various actions are as follows.
*
@@ -373,7 +421,7 @@ public class NativeJDBCSeq
}
/**
- * Run the tool. Return false if an invalid option was given.
+ * Run the tool. Returns false if an invalid option was given.
*/
public static boolean run(JDBCConfiguration conf, String[] args,
String action)
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java
index 62252d54c..a52d8cfaf 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java
@@ -225,12 +225,15 @@ public class DB2Dictionary
}
}
- public String[] getCreateSequenceSQL(Sequence seq) {
- String[] sql = super.getCreateSequenceSQL(seq);
- if (seq.getAllocate() > 1)
- sql[0] += " CACHE " + seq.getAllocate();
- return sql;
- }
+ public String[] getCreateSequenceSQL(Sequence seq) {
+ String[] sql = super.getCreateSequenceSQL(seq);
+
+ if (seq.getAllocate() > 1 && useNativeSequenceCache){
+ sql[0] += " CACHE " + seq.getAllocate();
+ }
+
+ return sql;
+ }
@Override
protected String getSequencesSQL(String schemaName, String sequenceName) {
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
index a5bf393a5..6665bb7b8 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
@@ -351,6 +351,7 @@ public class DBDictionary
public String sequenceNameSQL = null;
// most native sequences can be run inside the business transaction
public int nativeSequenceType= Seq.TYPE_CONTIGUOUS;
+ public boolean useNativeSequenceCache = true;
protected JDBCConfiguration conf = null;
protected Log log = null;
@@ -3402,6 +3403,14 @@ public class DBDictionary
buf.append(seqName);
if (seq.getInitialValue() != 0)
buf.append(" START WITH ").append(seq.getInitialValue());
+
+ if (seq.getIncrement() > 1 && useNativeSequenceCache){
+ buf.append(" INCREMENT BY ").append(seq.getIncrement());
+ }
+ else if ((seq.getIncrement() > 1) || (seq.getAllocate() > 1)){
+ buf.append(" INCREMENT BY ").append(seq.getIncrement() * seq.getAllocate());
+ }
+
if (seq.getIncrement() > 1)
buf.append(" INCREMENT BY ").append(seq.getIncrement());
return new String[]{ buf.toString() };
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
index 57c718a47..0388863d3 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
@@ -997,12 +997,15 @@ public class OracleDictionary
}
@Override
- public String[] getCreateSequenceSQL(Sequence seq) {
- String[] sql = super.getCreateSequenceSQL(seq);
- if (seq.getAllocate() > 1)
- sql[0] += " CACHE " + seq.getAllocate();
- return sql;
- }
+ public String[] getCreateSequenceSQL(Sequence seq) {
+ String[] sql = super.getCreateSequenceSQL(seq);
+
+ if (seq.getAllocate() > 1 && useNativeSequenceCache){
+ sql[0] += " CACHE " + seq.getAllocate();
+ }
+
+ return sql;
+ }
/**
* Return the preferred {@link Types} constant for the given
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
index 8563fa5cf..989c9ba10 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java
@@ -365,8 +365,11 @@ public class PostgresDictionary
@Override
public String[] getCreateSequenceSQL(Sequence seq) {
String[] sql = super.getCreateSequenceSQL(seq);
- if (seq.getAllocate() > 1)
+
+ if (seq.getAllocate() > 1 && useNativeSequenceCache){
sql[0] += " CACHE " + seq.getAllocate();
+ }
+
return sql;
}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestNativeSeqGenerator.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestNativeSeqGenerator.java
index 349a681b4..64e41cfab 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestNativeSeqGenerator.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/generationtype/TestNativeSeqGenerator.java
@@ -62,4 +62,34 @@ public class TestNativeSeqGenerator extends SQLListenerTestCase {
assertTrue("Next value should depend on previous genid", nextId >= genId + 1);
em.close();
}
+
+ /**
+ * Asserts native sequence generator allocates values in memory
+ * and requests sequence values from database only when necessary.
+ */
+ public void testAllocationSize() {
+ //Run this test only if the user has elected to not use the Native Sequence Cache.
+ if (supportsNativeSequence && !dict.useNativeSequenceCache){
+ // Turn off statement batching for easier INSERT counting.
+ dict.setBatchLimit(0);
+ em.getTransaction().begin();
+ resetSQL();
+ for (int i = 0; i < 51; i++) {
+ createEntityE2();
+ em.persist(entityE2);
+ }
+ em.getTransaction().commit();
+
+ // Since allocationSize has a default of 50, we expect 2 sequence fetches and 51 INSERTs.
+ assertEquals("53 statements should be executed.", 53, getSQLCount());
+ String[] statements = new String[53];
+ statements[0] = ".*";
+ statements[1] = ".*";
+ for (int i = 2; i < 53; i++) {
+ statements[i] = "INSERT .*";
+ }
+ assertAllExactSQLInOrder(statements);
+ em.close();
+ }
+ }
}
diff --git a/openjpa-project/src/doc/manual/jpa_overview_mapping.xml b/openjpa-project/src/doc/manual/jpa_overview_mapping.xml
index b59dee2e5..9b6914410 100644
--- a/openjpa-project/src/doc/manual/jpa_overview_mapping.xml
+++ b/openjpa-project/src/doc/manual/jpa_overview_mapping.xml
@@ -967,10 +967,11 @@ default.
allocationSize property
-int allocationSize: Some databases can pre-allocate groups
-of sequence values. This allows the database to service sequence requests from
-cache, rather than physically incrementing the sequence with every request. This
-allocation size defaults to 50.
+int allocationSize: The number of values to allocate in
+memory for each trip to the database. Allocating values in memory allows the JPA
+runtime to avoid accessing the database for every sequence request.
+This number also specifies the amount that the sequence value is incremented
+each time the sequence is accessed. Defaults to 50.
diff --git a/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml b/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
index 80479c736..d98c996c4 100644
--- a/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
+++ b/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
@@ -1914,8 +1914,9 @@ create a generated name. The default value is the underscore "_"
NextSequenceQuery: A SQL string for obtaining a native
sequence value. May use a placeholder of {0} for the variable
-sequence name. Defaults to a database-appropriate value. For example,
-"SELECT {0}.NEXTVAL FROM DUAL" for Oracle.
+sequence name and {1} for sequence increment.
+Defaults to a database-appropriate value. For example,
+"SELECT {0}.NEXTVAL FROM DUAL" for Oracle database.
diff --git a/openjpa-project/src/doc/manual/ref_guide_optimization.xml b/openjpa-project/src/doc/manual/ref_guide_optimization.xml
index 9d772f497..88f1afba2 100644
--- a/openjpa-project/src/doc/manual/ref_guide_optimization.xml
+++ b/openjpa-project/src/doc/manual/ref_guide_optimization.xml
@@ -301,8 +301,7 @@ it can become a factor.
For applications that perform large bulk inserts, the retrieval of sequence
-numbers can be a bottleneck. Increasing sequence increments and using
-table-based rather than native database sequences can reduce or eliminate
+numbers can be a bottleneck. Increasing sequence allocation sizes can reduce or eliminate
this bottleneck. In some cases, implementing your own sequence factory can
further optimize sequence number retrieval.
diff --git a/openjpa-project/src/doc/manual/ref_guide_runtime.xml b/openjpa-project/src/doc/manual/ref_guide_runtime.xml
index 166d1728b..bb62c268b 100644
--- a/openjpa-project/src/doc/manual/ref_guide_runtime.xml
+++ b/openjpa-project/src/doc/manual/ref_guide_runtime.xml
@@ -1661,8 +1661,11 @@ properties:
-Allocate: Some database can allocate values in-memory to
-service subsequent sequence requests faster.
+Allocate: The number of values to allocate on each database
+trip. Defaults to 50, meaning the class will set aside the next 50 numbers each
+time it accesses the sequence, which in turn means it only has to make a
+database trip to get new sequence numbers once every 50 sequence number
+requests.