mirror of https://github.com/apache/openjpa.git
OPENJPA-1376: Backported trunk changes to 2.1.x where possible, made code gated by a system property.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.1.x@1153990 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
dddb5a0f67
commit
1dbc7edccb
|
@ -55,6 +55,8 @@ import serp.util.Strings;
|
||||||
/**
|
/**
|
||||||
* {@link JDBCSeq} implementation that uses a database sequences
|
* {@link JDBCSeq} implementation that uses a database sequences
|
||||||
* to generate numbers.
|
* 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 JDBCSeq
|
||||||
* @see AbstractJDBCSeq
|
* @see AbstractJDBCSeq
|
||||||
|
@ -73,9 +75,11 @@ public class NativeJDBCSeq
|
||||||
private DBIdentifier _seqName = DBIdentifier.newSequence("OPENJPA_SEQUENCE");
|
private DBIdentifier _seqName = DBIdentifier.newSequence("OPENJPA_SEQUENCE");
|
||||||
private int _increment = 1;
|
private int _increment = 1;
|
||||||
private int _initial = 1;
|
private int _initial = 1;
|
||||||
private int _allocate = 0;
|
private int _allocate = 50;
|
||||||
private Sequence _seq = null;
|
private Sequence _seq = null;
|
||||||
private String _select = null;
|
private String _select = null;
|
||||||
|
private long _nextValue = 0;
|
||||||
|
private long _maxValue = -1;
|
||||||
|
|
||||||
// for deprecated auto-configuration support
|
// for deprecated auto-configuration support
|
||||||
private String _format = null;
|
private String _format = null;
|
||||||
|
@ -203,29 +207,76 @@ public class NativeJDBCSeq
|
||||||
public void endConfiguration() {
|
public void endConfiguration() {
|
||||||
buildSequence();
|
buildSequence();
|
||||||
|
|
||||||
|
if (DBIdentifier.isNull(_tableName))
|
||||||
|
_tableName = DBIdentifier.newTable("DUAL");
|
||||||
|
|
||||||
DBDictionary dict = _conf.getDBDictionaryInstance();
|
DBDictionary dict = _conf.getDBDictionaryInstance();
|
||||||
|
String name = dict.getFullName(_seq);
|
||||||
|
|
||||||
|
if (dict.useNativeSequenceCache){
|
||||||
if (_format == null) {
|
if (_format == null) {
|
||||||
_format = dict.nextSequenceQuery;
|
_format = dict.nextSequenceQuery;
|
||||||
if (_format == null)
|
if (_format == null)
|
||||||
throw new MetaDataException(_loc.get("no-seq-sql", _seqName));
|
throw new MetaDataException(_loc.get("no-seq-sql", _seqName));
|
||||||
}
|
}
|
||||||
if (DBIdentifier.isNull(_tableName))
|
|
||||||
_tableName = DBIdentifier.newTable("DUAL");
|
|
||||||
|
|
||||||
String name = dict.getFullName(_seq);
|
|
||||||
Object[] subs = (_subTable) ? new Object[]{ name, _tableName }
|
Object[] subs = (_subTable) ? new Object[]{ name, _tableName }
|
||||||
: new Object[]{ name };
|
: 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;
|
type = dict.nativeSequenceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object nextInternal(JDBCStore store, ClassMapping mapping)
|
protected synchronized Object nextInternal(JDBCStore store, ClassMapping mapping)
|
||||||
throws SQLException {
|
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);
|
Connection conn = getConnection(store);
|
||||||
try {
|
try {
|
||||||
return getSequence(conn);
|
_nextValue = getSequence(conn);
|
||||||
|
_maxValue = _nextValue + _allocate * _increment;
|
||||||
} finally {
|
} finally {
|
||||||
closeConnection(conn);
|
closeConnection(conn);
|
||||||
}
|
}
|
||||||
|
@ -300,9 +351,7 @@ public class NativeJDBCSeq
|
||||||
try {
|
try {
|
||||||
stmnt = conn.prepareStatement(_select);
|
stmnt = conn.prepareStatement(_select);
|
||||||
dict.setTimeouts(stmnt, _conf, false);
|
dict.setTimeouts(stmnt, _conf, false);
|
||||||
synchronized(this) {
|
|
||||||
rs = stmnt.executeQuery();
|
rs = stmnt.executeQuery();
|
||||||
}
|
|
||||||
if (rs.next())
|
if (rs.next())
|
||||||
return rs.getLong(1);
|
return rs.getLong(1);
|
||||||
|
|
||||||
|
@ -327,13 +376,12 @@ public class NativeJDBCSeq
|
||||||
* Where the following options are recognized.
|
* Where the following options are recognized.
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><i>-properties/-p <properties file or resource></i>: The
|
* <li><i>-properties/-p <properties file or resource></i>: The
|
||||||
* path or resource name of a OpenJPA properties file containing
|
* path or resource name of an OpenJPA properties file containing
|
||||||
* information such as the license key and connection data as
|
* information such as connection data as
|
||||||
* outlined in {@link JDBCConfiguration}. Optional.</li>
|
* outlined in {@link JDBCConfiguration}. Optional.</li>
|
||||||
* <li><i>-<property name> <property value></i>: All bean
|
* <li><i>-<property name> <property value></i>: All bean
|
||||||
* properties of the OpenJPA {@link JDBCConfiguration} can be set by
|
* properties of the OpenJPA {@link JDBCConfiguration} can be set by
|
||||||
* using their names and supplying a value. For example:
|
* using their names and supplying a value.</li>
|
||||||
* <code>-licenseKey adslfja83r3lkadf</code></li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
* The various actions are as follows.
|
* The various actions are as follows.
|
||||||
* <ul>
|
* <ul>
|
||||||
|
@ -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,
|
public static boolean run(JDBCConfiguration conf, String[] args,
|
||||||
String action)
|
String action)
|
||||||
|
|
|
@ -227,8 +227,11 @@ public class DB2Dictionary
|
||||||
|
|
||||||
public String[] getCreateSequenceSQL(Sequence seq) {
|
public String[] getCreateSequenceSQL(Sequence seq) {
|
||||||
String[] sql = super.getCreateSequenceSQL(seq);
|
String[] sql = super.getCreateSequenceSQL(seq);
|
||||||
if (seq.getAllocate() > 1)
|
|
||||||
|
if (seq.getAllocate() > 1 && useNativeSequenceCache){
|
||||||
sql[0] += " CACHE " + seq.getAllocate();
|
sql[0] += " CACHE " + seq.getAllocate();
|
||||||
|
}
|
||||||
|
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -351,6 +351,7 @@ public class DBDictionary
|
||||||
public String sequenceNameSQL = null;
|
public String sequenceNameSQL = null;
|
||||||
// most native sequences can be run inside the business transaction
|
// most native sequences can be run inside the business transaction
|
||||||
public int nativeSequenceType= Seq.TYPE_CONTIGUOUS;
|
public int nativeSequenceType= Seq.TYPE_CONTIGUOUS;
|
||||||
|
public boolean useNativeSequenceCache = true;
|
||||||
|
|
||||||
protected JDBCConfiguration conf = null;
|
protected JDBCConfiguration conf = null;
|
||||||
protected Log log = null;
|
protected Log log = null;
|
||||||
|
@ -3402,6 +3403,14 @@ public class DBDictionary
|
||||||
buf.append(seqName);
|
buf.append(seqName);
|
||||||
if (seq.getInitialValue() != 0)
|
if (seq.getInitialValue() != 0)
|
||||||
buf.append(" START WITH ").append(seq.getInitialValue());
|
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)
|
if (seq.getIncrement() > 1)
|
||||||
buf.append(" INCREMENT BY ").append(seq.getIncrement());
|
buf.append(" INCREMENT BY ").append(seq.getIncrement());
|
||||||
return new String[]{ buf.toString() };
|
return new String[]{ buf.toString() };
|
||||||
|
|
|
@ -999,8 +999,11 @@ public class OracleDictionary
|
||||||
@Override
|
@Override
|
||||||
public String[] getCreateSequenceSQL(Sequence seq) {
|
public String[] getCreateSequenceSQL(Sequence seq) {
|
||||||
String[] sql = super.getCreateSequenceSQL(seq);
|
String[] sql = super.getCreateSequenceSQL(seq);
|
||||||
if (seq.getAllocate() > 1)
|
|
||||||
|
if (seq.getAllocate() > 1 && useNativeSequenceCache){
|
||||||
sql[0] += " CACHE " + seq.getAllocate();
|
sql[0] += " CACHE " + seq.getAllocate();
|
||||||
|
}
|
||||||
|
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -365,8 +365,11 @@ public class PostgresDictionary
|
||||||
@Override
|
@Override
|
||||||
public String[] getCreateSequenceSQL(Sequence seq) {
|
public String[] getCreateSequenceSQL(Sequence seq) {
|
||||||
String[] sql = super.getCreateSequenceSQL(seq);
|
String[] sql = super.getCreateSequenceSQL(seq);
|
||||||
if (seq.getAllocate() > 1)
|
|
||||||
|
if (seq.getAllocate() > 1 && useNativeSequenceCache){
|
||||||
sql[0] += " CACHE " + seq.getAllocate();
|
sql[0] += " CACHE " + seq.getAllocate();
|
||||||
|
}
|
||||||
|
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,4 +62,34 @@ public class TestNativeSeqGenerator extends SQLListenerTestCase {
|
||||||
assertTrue("Next value should depend on previous genid", nextId >= genId + 1);
|
assertTrue("Next value should depend on previous genid", nextId >= genId + 1);
|
||||||
em.close();
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -967,10 +967,11 @@ default.
|
||||||
allocationSize property
|
allocationSize property
|
||||||
</secondary>
|
</secondary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
<literal>int allocationSize</literal>: Some databases can pre-allocate groups
|
<literal>int allocationSize</literal>: The number of values to allocate in
|
||||||
of sequence values. This allows the database to service sequence requests from
|
memory for each trip to the database. Allocating values in memory allows the JPA
|
||||||
cache, rather than physically incrementing the sequence with every request. This
|
runtime to avoid accessing the database for every sequence request.
|
||||||
allocation size defaults to 50.
|
This number also specifies the amount that the sequence value is incremented
|
||||||
|
each time the sequence is accessed. Defaults to 50.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
|
@ -1914,8 +1914,9 @@ create a generated name. The default value is the underscore <literal>"_"</liter
|
||||||
</indexterm>
|
</indexterm>
|
||||||
<literal>NextSequenceQuery</literal>: A SQL string for obtaining a native
|
<literal>NextSequenceQuery</literal>: A SQL string for obtaining a native
|
||||||
sequence value. May use a placeholder of <literal>{0}</literal> for the variable
|
sequence value. May use a placeholder of <literal>{0}</literal> for the variable
|
||||||
sequence name. Defaults to a database-appropriate value. For example,
|
sequence name and <literal>{1}</literal> for sequence increment.
|
||||||
<literal>"SELECT {0}.NEXTVAL FROM DUAL"</literal> for Oracle.
|
Defaults to a database-appropriate value. For example,
|
||||||
|
<literal>"SELECT {0}.NEXTVAL FROM DUAL"</literal> for Oracle database.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem id="DBDictionary.NullTypeName">
|
<listitem id="DBDictionary.NullTypeName">
|
||||||
|
|
|
@ -301,8 +301,7 @@ it can become a factor.
|
||||||
</entry>
|
</entry>
|
||||||
<entry colname="desc">
|
<entry colname="desc">
|
||||||
For applications that perform large bulk inserts, the retrieval of sequence
|
For applications that perform large bulk inserts, the retrieval of sequence
|
||||||
numbers can be a bottleneck. Increasing sequence increments and using
|
numbers can be a bottleneck. Increasing sequence allocation sizes can reduce or eliminate
|
||||||
table-based rather than native database sequences can reduce or eliminate
|
|
||||||
this bottleneck. In some cases, implementing your own sequence factory can
|
this bottleneck. In some cases, implementing your own sequence factory can
|
||||||
further optimize sequence number retrieval.
|
further optimize sequence number retrieval.
|
||||||
</entry>
|
</entry>
|
||||||
|
|
|
@ -1661,8 +1661,11 @@ properties:
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
<literal>Allocate</literal>: Some database can allocate values in-memory to
|
<literal>Allocate</literal>: The number of values to allocate on each database
|
||||||
service subsequent sequence requests faster.
|
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.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
Loading…
Reference in New Issue