mirror of https://github.com/apache/openjpa.git
OPENJPA-1376: Backported trunk changes to 2.0.x where possible, made code gated by a system property.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.0.x@1153950 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
75a512da70
commit
d12334e6f9
|
@ -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.
|
||||
* <ul>
|
||||
* <li><i>-properties/-p <properties file or resource></i>: 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.</li>
|
||||
* <li><i>-<property name> <property value></i>: All bean
|
||||
* properties of the OpenJPA {@link JDBCConfiguration} can be set by
|
||||
* using their names and supplying a value. For example:
|
||||
* <code>-licenseKey adslfja83r3lkadf</code></li>
|
||||
* using their names and supplying a value.</li>
|
||||
* </ul>
|
||||
* The various actions are as follows.
|
||||
* <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,
|
||||
String action)
|
||||
|
|
|
@ -227,12 +227,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) {
|
||||
|
|
|
@ -350,6 +350,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;
|
||||
|
@ -3385,6 +3386,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() };
|
||||
|
|
|
@ -1036,12 +1036,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
|
||||
|
|
|
@ -345,8 +345,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,38 @@ public class TestNativeSeqGenerator extends SQLListenerTestCase {
|
|||
em.getTransaction().commit();
|
||||
int genId = entityE2.getId();
|
||||
int nextId = (int)((Long)em.getIdGenerator(EntityE2.class).next()).longValue();
|
||||
assertTrue("Next value should depend on previous genid", nextId == genId + 1);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -967,10 +967,11 @@ default.
|
|||
allocationSize property
|
||||
</secondary>
|
||||
</indexterm>
|
||||
<literal>int allocationSize</literal>: 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.
|
||||
<literal>int allocationSize</literal>: 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.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
|
|
|
@ -1824,8 +1824,9 @@ create a generated name. The default value is the underscore <literal>"_"</liter
|
|||
</indexterm>
|
||||
<literal>NextSequenceQuery</literal>: A SQL string for obtaining a native
|
||||
sequence value. May use a placeholder of <literal>{0}</literal> for the variable
|
||||
sequence name. Defaults to a database-appropriate value. For example,
|
||||
<literal>"SELECT {0}.NEXTVAL FROM DUAL"</literal> for Oracle.
|
||||
sequence name and <literal>{1}</literal> for sequence increment.
|
||||
Defaults to a database-appropriate value. For example,
|
||||
<literal>"SELECT {0}.NEXTVAL FROM DUAL"</literal> for Oracle database.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem id="DBDictionary.NullTypeName">
|
||||
|
|
|
@ -295,8 +295,7 @@ it can become a factor.
|
|||
</entry>
|
||||
<entry colname="desc">
|
||||
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.
|
||||
</entry>
|
||||
|
|
|
@ -1661,8 +1661,11 @@ properties:
|
|||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<literal>Allocate</literal>: Some database can allocate values in-memory to
|
||||
service subsequent sequence requests faster.
|
||||
<literal>Allocate</literal>: 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.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
|
Loading…
Reference in New Issue