blob: 8addd937a0c133156ce29d3d61922e2f6cc13b38 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.jdbc.kernel;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.concurrent.ConcurrentHashMap;
import javax.transaction.NotSupportedException;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl;
import org.apache.openjpa.jdbc.identifier.DBIdentifier;
import org.apache.openjpa.jdbc.identifier.DBIdentifier.DBIdentifierType;
import org.apache.openjpa.jdbc.identifier.Normalizer;
import org.apache.openjpa.jdbc.identifier.QualifiedDBIdentifier;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.schema.Column;
import org.apache.openjpa.jdbc.schema.PrimaryKey;
import org.apache.openjpa.jdbc.schema.Schema;
import org.apache.openjpa.jdbc.schema.SchemaGroup;
import org.apache.openjpa.jdbc.schema.SchemaTool;
import org.apache.openjpa.jdbc.schema.Schemas;
import org.apache.openjpa.jdbc.schema.Table;
import org.apache.openjpa.jdbc.schema.Unique;
import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.jdbc.sql.Row;
import org.apache.openjpa.jdbc.sql.SQLBuffer;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.conf.Configurations;
import org.apache.openjpa.lib.identifier.IdentifierUtil;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.util.Options;
import org.apache.openjpa.lib.util.StringUtil;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.UserException;
////////////////////////////////////////////////////////////
// NOTE: Do not change property names; see SequenceMetaData
// and SequenceMapping for standard property names.
////////////////////////////////////////////////////////////
/**
* {@link JDBCSeq} implementation that uses a database table
* for sequence number generation. This base implementation uses a single
* row for a global sequence number.
*
* @author Abe White
*/
public class TableJDBCSeq extends AbstractJDBCSeq implements Configurable {
public static final String ACTION_DROP = "drop";
public static final String ACTION_ADD = "add";
public static final String ACTION_GET = "get";
public static final String ACTION_SET = "set";
public static final String DEFAULT_TABLE = "OPENJPA_SEQUENCE_TABLE";
private static final Localizer _loc = Localizer.forPackage
(TableJDBCSeq.class);
private transient JDBCConfiguration _conf = null;
private transient Log _log = null;
private int _alloc = 50;
private int _intValue = 1;
private final ConcurrentHashMap<ClassMapping, Status> _stat = new ConcurrentHashMap<>();
private DBIdentifier _table = DBIdentifier.newTable(DEFAULT_TABLE);
private DBIdentifier _seqColumnName = DBIdentifier.newColumn("SEQUENCE_VALUE");
private DBIdentifier _pkColumnName = DBIdentifier.newColumn("ID");
private DBIdentifier[] _uniqueColumnNames;
private DBIdentifier _uniqueConstraintName = DBIdentifier.NULL;
private Column _seqColumn = null;
private Column _pkColumn = null;
/**
* The sequence table name. Defaults to <code>OPENJPA_SEQUENCE_TABLE</code>.
* By default, the table will be placed in the first schema listed in your
* <code>openjpa.jdbc.Schemas</code> property, or in the default schema if
* the property is not given. If you specify a table name in the form
* <code>&lt;schema&gt;.&lt;table&gt;</code>, then the given schema
* will be used.
*/
public String getTable() {
return _table.getName();
}
/**
* The sequence table name. Defaults to <code>OPENJPA_SEQUENCE_TABLE</code>.
* By default, the table will be placed in the first schema listed in your
* <code>openjpa.jdbc.Schemas</code> property, or in the default schema if
* the property is not given. If you specify a table name in the form
* <code>&lt;schema&gt;.&lt;table&gt;</code>, then the given schema
* will be used.
*/
public void setTable(String name) {
// Split the name into its individual parts
String[] names = Normalizer.splitName(name);
// Join the name back together. This will delimit as appropriate.
_table = DBIdentifier.newTable(Normalizer.joinNames(names));
}
/**
* @deprecated Use {@link #setTable}. Retained for
* backwards-compatibility with auto-configuration.
*/
@Deprecated
public void setTableName(String name) {
setTable(name);
}
/**
* The name of the column that holds the sequence value. Defaults
* to <code>SEQUENCE_VALUE</code>.
*/
public String getSequenceColumn() {
return _seqColumnName.getName();
}
/**
* The name of the column that holds the sequence value. Defaults
* to <code>SEQUENCE_VALUE</code>.
*/
public void setSequenceColumn(String sequenceColumn) {
_seqColumnName = DBIdentifier.newColumn(sequenceColumn);
}
/**
* The name of the table's primary key column. Defaults to
* <code>ID</code>.
*/
public String getPrimaryKeyColumn() {
return _pkColumnName.getName();
}
public DBIdentifier getPrimaryKeyColumnIdentifier() {
return _pkColumnName;
}
/**
* The name of the table's primary key column. Defaults to
* <code>ID</code>.
*/
public void setPrimaryKeyColumn(String primaryKeyColumn) {
_pkColumnName = DBIdentifier.newColumn(primaryKeyColumn);
}
/**
* Return the number of sequences to allocate for each update of the
* sequence table. Sequence numbers will be grabbed in blocks of this
* value to reduce the number of transactions that must be performed on
* the sequence table.
*/
public int getAllocate() {
return _alloc;
}
/**
* Return the number of sequences to allocate for each update of the
* sequence table. Sequence numbers will be grabbed in blocks of this
* value to reduce the number of transactions that must be performed on
* the sequence table.
*/
public void setAllocate(int alloc) {
_alloc = alloc;
}
/**
* Return the number as the initial number for the
* GeneratedValue.TABLE strategy to start with.
* @return an initial number
*/
public int getInitialValue() {
return _intValue;
}
/**
* Set the initial number in the table for the GeneratedValue.TABLE
* strategy to use as initial number.
* @param intValue. The initial number
*/
public void setInitialValue(int intValue) {
_intValue = intValue;
}
/**
* Sets the names of the columns on which a unique constraint is set.
* @param columnsNames are passed as a single String concatenated with
* a '|' character. This method parses it back to array of Strings.
*/
public void setUniqueColumns(String columnNames) {
_uniqueColumnNames = (StringUtil.isEmpty(columnNames))
? null : DBIdentifier.split(columnNames, DBIdentifierType.COLUMN, IdentifierUtil.BAR);
}
public String getUniqueColumns() {
return Normalizer.joinNames(DBIdentifier.toStringArray(_uniqueColumnNames), IdentifierUtil.BAR);
}
/**
* @deprecated Use {@link #setAllocate}. Retained for backwards
* compatibility of auto-configuration.
*/
@Deprecated
public void setIncrement(int inc) {
setAllocate(inc);
}
@Override
public JDBCConfiguration getConfiguration() {
return _conf;
}
@Override
public void setConfiguration(Configuration conf) {
_conf = (JDBCConfiguration) conf;
_log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
}
@Override
public void startConfiguration() {
}
@Override
public void endConfiguration() {
buildTable();
}
@Override
public void addSchema(ClassMapping mapping, SchemaGroup group) {
// Since the table is created by openjpa internally
// we can create the table for each schema within the PU
// in here.
Schema[] schemas = group.getSchemas();
for (int i = 0; i < schemas.length; i++) {
QualifiedDBIdentifier path = QualifiedDBIdentifier.getPath(_table);
DBIdentifier schemaName = path.getSchemaName();
if (DBIdentifier.isEmpty(schemaName)) {
schemaName = Schemas.getNewTableSchemaIdentifier(_conf);
}
if (DBIdentifier.isNull(schemaName)) {
schemaName = schemas[i].getIdentifier();
}
// create table in this group
Schema schema = group.getSchema(schemaName);
if (schema == null) {
schema = group.addSchema(schemaName);
}
Table copy = schema.importTable(_pkColumn.getTable());
// importTable() does not import unique constraints
Unique[] uniques = _pkColumn.getTable().getUniques();
for (Unique u : uniques) {
copy.importUnique(u);
}
// we need to reset the table name in the column with the
// fully qualified name for matching the table name from the
// Column.
_pkColumn.resetTableIdentifier(QualifiedDBIdentifier.newPath(schemaName, _pkColumn.getTableIdentifier()));
// some databases require to create an index for the sequence table
_conf.getDBDictionaryInstance().createIndexIfNecessary(schema,
_table, _pkColumn);
}
}
@Override
protected Object nextInternal(JDBCStore store, ClassMapping mapping) throws Exception {
// if needed, grab the next handful of ids
Status stat = getStatus(mapping);
if (stat == null)
throw new InvalidStateException(_loc.get("bad-seq-type",
getClass(), mapping));
while (true) {
synchronized (stat) {
// make sure seq is at least 1, since autoassigned ids of 0 can
// conflict with uninitialized values
stat.seq = Math.max(stat.seq, 1);
if (stat.seq < stat.max)
return stat.seq++;
allocateSequence(store, mapping, stat, _alloc, true);
}
}
}
@Override
protected Object currentInternal(JDBCStore store, ClassMapping mapping)
throws Exception {
if (current == null) {
CurrentSequenceRunnable runnable =
new CurrentSequenceRunnable(store, mapping);
try {
if (suspendInJTA()) {
// NotSupportedException is wrapped in a StoreException by
// the caller.
_conf.getManagedRuntimeInstance().doNonTransactionalWork(
runnable);
} else {
runnable.run();
}
} catch (RuntimeException re) {
throw (Exception) (re.getCause() == null ? re : re.getCause());
}
}
return super.currentInternal(store, mapping);
}
@Override
protected void allocateInternal(int count, JDBCStore store,
ClassMapping mapping)
throws SQLException {
Status stat = getStatus(mapping);
if (stat == null)
return;
while (true) {
int available;
synchronized (stat) {
available = (int) (stat.max - stat.seq);
if (available >= count)
return;
}
allocateSequence(store, mapping, stat, count - available, false);
}
}
/**
* Return the appropriate status object for the given class, or null
* if cannot handle the given class. The mapping may be null.
*/
protected Status getStatus(ClassMapping mapping) {
Status status = _stat.get(mapping);
if (status == null){
status = new Status();
Status tStatus = _stat.putIfAbsent(mapping, status);
// This can happen if another thread calls .put(..) sometime after our call to get. Return
// the value from the putIfAbsent call as that is truly in the map.
if (tStatus != null) {
return tStatus;
}
}
return status;
}
/**
* Add the primary key column to the given table and return it.
*/
protected Column addPrimaryKeyColumn(Table table) {
DBDictionary dict = _conf.getDBDictionaryInstance();
DBIdentifier delimitedColumnName = dict.fromDBName(getPrimaryKeyColumn(), DBIdentifier.DBIdentifierType.COLUMN);
Column pkColumn = table.addColumn(dict.getValidColumnName
(delimitedColumnName, table));
pkColumn.setType(dict.getPreferredType(Types.TINYINT));
pkColumn.setJavaType(JavaTypes.INT);
return pkColumn;
}
/**
* Return the primary key value for the sequence table for the given class.
*/
protected Object getPrimaryKey(ClassMapping mapping) {
return 0;
}
/**
* Creates the object-level representation of the sequence table.
*/
private void buildTable() {
DBIdentifier tableName = DBIdentifier.NULL;
DBIdentifier schemaName = DBIdentifier.NULL;
QualifiedDBIdentifier path = QualifiedDBIdentifier.getPath(_table);
if (!DBIdentifier.isEmpty(path.getSchemaName())) {
schemaName = path.getSchemaName();
tableName = path.getUnqualifiedName();
}
else {
tableName = _table;
}
if (DBIdentifier.isEmpty(schemaName)) {
schemaName = Schemas.getNewTableSchemaIdentifier(_conf);
}
SchemaGroup group = new SchemaGroup();
Schema schema = group.addSchema(schemaName);
Table table = schema.addTable(tableName);
_pkColumn = addPrimaryKeyColumn(table);
PrimaryKey pk = table.addPrimaryKey();
pk.addColumn(_pkColumn);
DBDictionary dict = _conf.getDBDictionaryInstance();
DBIdentifier _delimitedSeqColumnName = dict.delimitAll() ?
DBIdentifier.newColumn(this._seqColumnName.getName(), true) : this._seqColumnName;
_seqColumn = table.addColumn(dict.getValidColumnName
(_delimitedSeqColumnName, table));
_seqColumn.setType(dict.getPreferredType(Types.BIGINT));
_seqColumn.setJavaType(JavaTypes.LONG);
if (_uniqueColumnNames != null) {
DBIdentifier uniqueName = _uniqueConstraintName;
if (DBIdentifier.isEmpty(uniqueName)) {
uniqueName = dict.getValidUniqueName(DBIdentifier.newConstraint("UNQ"), table);
}
Unique u = table.addUnique(uniqueName);
for (DBIdentifier columnName : _uniqueColumnNames) {
if (!table.containsColumn(columnName, _conf.getDBDictionaryInstance()))
throw new UserException(_loc.get("unique-missing-column",
columnName, table.getIdentifier(),
table.getColumnNames()));
Column col = table.getColumn(columnName);
u.addColumn(col);
}
}
}
/**
* Updates the max available sequence value.
*/
private void allocateSequence(JDBCStore store, ClassMapping mapping,
Status stat, int alloc, boolean updateStatSeq) throws SQLException {
Runnable runnable =
new AllocateSequenceRunnable(
store, mapping, stat, alloc, updateStatSeq);
try {
if (suspendInJTA()) {
// NotSupportedException is wrapped in a StoreException by
// the caller.
try {
_conf.getManagedRuntimeInstance().doNonTransactionalWork(
runnable);
}
catch(NotSupportedException nse) {
SQLException sqlEx = new SQLException(
nse.getLocalizedMessage());
sqlEx.initCause(nse);
throw sqlEx;
}
} else {
runnable.run();
}
} catch (RuntimeException re) {
Throwable e = re.getCause();
if(e instanceof SQLException )
throw (SQLException) e;
else
throw re;
}
}
/**
* Inserts the initial sequence column into the database.
*
* @param mapping
* ClassMapping for the class whose sequence column will be
* updated
* @param conn
* Connection used issue SQL statements.
*/
private void insertSequence(ClassMapping mapping, Connection conn)
throws SQLException {
if (_log.isTraceEnabled())
_log.trace(_loc.get("insert-seq"));
Object pk = getPrimaryKey(mapping);
if (pk == null)
throw new InvalidStateException(_loc.get("bad-seq-type",
getClass(), mapping));
DBDictionary dict = _conf.getDBDictionaryInstance();
DBIdentifier tableName = resolveTableIdentifier(mapping, _pkColumn.getTable());
SQLBuffer insert = new SQLBuffer(dict).append("INSERT INTO ").
append(tableName).append(" (").
append(_pkColumn).append(", ").append(_seqColumn).
append(") VALUES (").
appendValue(pk, _pkColumn).append(", ").
appendValue(_intValue, _seqColumn).append(")");
boolean wasAuto = conn.getAutoCommit();
if (!wasAuto && !suspendInJTA())
conn.setAutoCommit(true);
PreparedStatement stmnt = null;
try {
stmnt = prepareStatement(conn, insert);
dict.setTimeouts(stmnt, _conf, true);
executeUpdate(_conf, conn, stmnt, insert, Row.ACTION_INSERT);
} finally {
if (stmnt != null)
try { stmnt.close(); } catch (SQLException se) {}
if (!wasAuto && !suspendInJTA())
conn.setAutoCommit(false);
}
}
/**
* Get the current sequence value.
*
* @param mapping
* ClassMapping of the entity whose sequence value will be
* obtained.
* @param conn
* Connection used issue SQL statements.
*
* @return The current sequence value, or <code>SEQUENCE_NOT_FOUND</code>
* if the sequence could not be found.
*/
protected long getSequence(ClassMapping mapping, Connection conn)
throws SQLException {
if (_log.isTraceEnabled())
_log.trace(_loc.get("get-seq"));
Object pk = getPrimaryKey(mapping);
if (pk == null)
return -1;
DBDictionary dict = _conf.getDBDictionaryInstance();
SQLBuffer sel = new SQLBuffer(dict).append(_seqColumn);
SQLBuffer where = new SQLBuffer(dict).append(_pkColumn).append(" = ").
appendValue(pk, _pkColumn);
DBIdentifier tableName = resolveTableIdentifier(mapping, _seqColumn.getTable());
SQLBuffer tables = new SQLBuffer(dict).append(tableName);
SQLBuffer select = dict.toSelect(sel, null, tables, where, null, null,
null, false, dict.supportsSelectForUpdate, 0, Long.MAX_VALUE,
false, true);
PreparedStatement stmnt = null;
ResultSet rs = null;
try {
stmnt = prepareStatement(conn, select);
dict.setTimeouts(stmnt, _conf, false);
rs = executeQuery(_conf, conn, stmnt, select);
return getSequence(rs, dict);
} finally {
if (rs != null)
try { rs.close(); } catch (SQLException se) {}
if (stmnt != null)
try { stmnt.close(); } catch (SQLException se) {}
}
}
/**
* Grabs the next handful of sequence numbers.
*
* @return true if the sequence was updated, false if no sequence
* row existed for this mapping
*/
protected boolean setSequence(ClassMapping mapping, Status stat, int inc,
boolean updateStatSeq, Connection conn)
throws SQLException {
if (_log.isTraceEnabled())
_log.trace(_loc.get("update-seq"));
Object pk = getPrimaryKey(mapping);
if (pk == null)
throw new InvalidStateException(_loc.get("bad-seq-type",
getClass(), mapping));
DBDictionary dict = _conf.getDBDictionaryInstance();
SQLBuffer where = new SQLBuffer(dict).append(_pkColumn).append(" = ").
appendValue(pk, _pkColumn);
// loop until we have a successful atomic select/update sequence
long cur = 0;
PreparedStatement stmnt;
ResultSet rs;
SQLBuffer upd;
for (int updates = 0; updates == 0;) {
stmnt = null;
rs = null;
try {
cur = getSequence(mapping, conn);
if (cur == -1)
return false;
// update the value
upd = new SQLBuffer(dict);
DBIdentifier tableName = resolveTableIdentifier(mapping,
_seqColumn.getTable());
upd.append("UPDATE ").append(tableName).
append(" SET ").append(_seqColumn).append(" = ").
appendValue(cur + inc, _seqColumn).
append(" WHERE ").append(where).append(" AND ").
append(_seqColumn).append(" = ").
appendValue(cur, _seqColumn);
stmnt = prepareStatement(conn, upd);
dict.setTimeouts(stmnt, _conf, true);
updates = executeUpdate(_conf, conn, stmnt, upd,
Row.ACTION_UPDATE);
} finally {
if (rs != null)
try { rs.close(); } catch (SQLException se) {}
if (stmnt != null)
try { stmnt.close(); } catch (SQLException se) {}
}
}
// setup new sequence range
synchronized (stat) {
if (updateStatSeq && stat.seq < cur)
stat.seq = cur;
if (stat.max < cur + inc)
stat.max = cur + inc;
}
return true;
}
/**
* Resolve a fully qualified table name
*
* @param class
* mapping to get the schema name
* @deprecated
*/
@Deprecated
public String resolveTableName(ClassMapping mapping, Table table) {
return resolveTableIdentifier(mapping, table).getName();
}
/**
* Resolve a fully qualified table name
*
* @param class
* mapping to get the schema name
*/
public DBIdentifier resolveTableIdentifier(ClassMapping mapping, Table table) {
DBIdentifier sName = mapping.getTable().getSchemaIdentifier();
DBIdentifier tableName = DBIdentifier.NULL;
//OPENJPA-2650: Don't use a schema name if the user has requested,
//via useSchemaName, to not use one.
if (!_conf.getDBDictionaryInstance().useSchemaName){
tableName = table.getIdentifier();
} else if (DBIdentifier.isNull(sName)) {
tableName = table.getFullIdentifier();
} else if (!DBIdentifier.isNull(table.getSchemaIdentifier())) {
tableName = table.getFullIdentifier();
} else {
tableName = QualifiedDBIdentifier.newPath(sName, table.getIdentifier());
}
return tableName;
}
/**
* Creates the sequence table in the DB.
*/
public void refreshTable()
throws SQLException {
if (_log.isInfoEnabled())
_log.info(_loc.get("make-seq-table"));
// create the table
SchemaTool tool = new SchemaTool(_conf);
tool.setIgnoreErrors(true);
tool.createTable(_pkColumn.getTable());
}
/**
* Drops the sequence table in the DB.
*/
public void dropTable()
throws SQLException {
if (_log.isInfoEnabled())
_log.info(_loc.get("drop-seq-table"));
// drop the table
SchemaTool tool = new SchemaTool(_conf);
tool.setIgnoreErrors(true);
tool.dropTable(_pkColumn.getTable());
}
/////////
// Main
/////////
/**
* Usage: java org.apache.openjpa.jdbc.schema.TableJDBCSequence [option]*
* -action/-a &lt;add | drop | get | set&gt; [value]
* Where the following options are recognized.
* <ul>
* <li><i>-properties/-p &lt;properties file or resource&gt;</i>: The
* path or resource name of a OpenJPA properties file containing
* information such as the license key and connection data as
* outlined in {@link JDBCConfiguration}. Optional.</li>
* <li><i>-&lt;property name&gt; &lt;property value&gt;</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>
* </ul>
* The various actions are as follows.
* <ul>
* <li><i>add</i>: Create the sequence table.</li>
* <li><i>drop</i>: Drop the sequence table.</li>
* <li><i>get</i>: Print the current sequence value.</li>
* <li><i>set</i>: Set the sequence value.</li>
* </ul>
*/
public static void main(String[] args)
throws Exception {
Options opts = new Options();
final String[] arguments = opts.setFromCmdLine(args);
boolean ret = Configurations.runAgainstAllAnchors(opts,
new Configurations.Runnable() {
@Override
public boolean run(Options opts) throws Exception {
JDBCConfiguration conf = new JDBCConfigurationImpl();
try {
return TableJDBCSeq.run(conf, arguments, opts);
} finally {
conf.close();
}
}
});
if (!ret) {
// START - ALLOW PRINT STATEMENTS
System.out.println(_loc.get("seq-usage"));
// STOP - ALLOW PRINT STATEMENTS
}
}
/**
* Run the tool. Returns false if invalid options were given.
*/
public static boolean run(JDBCConfiguration conf, String[] args,
Options opts)
throws Exception {
String action = opts.removeProperty("action", "a", null);
Configurations.populateConfiguration(conf, opts);
return run(conf, args, action);
}
/**
* Run the tool. Return false if an invalid option was given.
*/
public static boolean run(JDBCConfiguration conf, String[] args,
String action)
throws Exception {
if (args.length > 1 || (args.length != 0
&& !ACTION_SET.equals(action)))
return false;
TableJDBCSeq seq = new TableJDBCSeq();
String props = Configurations.getProperties(conf.getSequence());
Configurations.configureInstance(seq, conf, props);
if (ACTION_DROP.equals(action))
seq.dropTable();
else if (ACTION_ADD.equals(action))
seq.refreshTable();
else if (ACTION_GET.equals(action) || ACTION_SET.equals(action)) {
Connection conn = conf.getDataSource2(null).getConnection();
try {
long cur = seq.getSequence(null, conn);
if (ACTION_GET.equals(action)) {
// START - ALLOW PRINT STATEMENTS
System.out.println(cur);
// STOP - ALLOW PRINT STATEMENTS
} else {
long set;
if (args.length > 0)
set = Long.parseLong(args[0]);
else
set = cur + seq.getAllocate();
if (set < cur)
set = cur;
else {
Status stat = seq.getStatus(null);
seq.setSequence(null, stat, (int) (set - cur), true,
conn);
set = stat.seq;
}
// START - ALLOW PRINT STATEMENTS
System.err.println(set);
// STOP - ALLOW PRINT STATEMENTS
}
}
catch (NumberFormatException nfe) {
return false;
} finally {
try { conn.close(); } catch (SQLException se) {}
}
} else
return false;
return true;
}
/**
* Helper struct to hold status information.
*/
protected static class Status implements Serializable {
private static final long serialVersionUID = 1L;
public long seq = 1L;
public long max = 0L;
}
/**
* This method is to provide override for non-JDBC or JDBC-like
* implementation of preparing statement.
*/
protected PreparedStatement prepareStatement(Connection conn, SQLBuffer buf)
throws SQLException {
return buf.prepareStatement(conn);
}
/**
* This method is to provide override for non-JDBC or JDBC-like
* implementation of executing update.
*/
protected int executeUpdate(JDBCConfiguration conf, Connection conn,
PreparedStatement stmnt, SQLBuffer buf, int opcode) throws SQLException
{
return stmnt.executeUpdate();
}
/**
* This method is to provide override for non-JDBC or JDBC-like
* implementation of executing query.
*/
protected ResultSet executeQuery(JDBCConfiguration conf, Connection conn,
PreparedStatement stmnt, SQLBuffer buf) throws SQLException {
return stmnt.executeQuery();
}
/**
* This method is to provide override for non-JDBC or JDBC-like
* implementation of getting sequence from the result set.
*/
protected long getSequence(ResultSet rs, DBDictionary dict)
throws SQLException {
if (rs == null || !rs.next())
return -1;
return dict.getLong(rs, 1);
}
public void setUniqueConstraintName(String uniqueConstraintName) {
_uniqueConstraintName = DBIdentifier.newConstraint(uniqueConstraintName);
}
public void setUniqueConstraintName(DBIdentifier uniqueConstraintName) {
_uniqueConstraintName = uniqueConstraintName;
}
public String getUniqueConstraintName() {
return _uniqueConstraintName.getName();
}
public DBIdentifier getUniqueConstraintIdentifier() {
return _uniqueConstraintName;
}
/**
* AllocateSequenceRunnable is a runnable wrapper that will inserts the
* initial sequence value into the database.
*/
protected class AllocateSequenceRunnable implements Runnable {
JDBCStore store = null;
ClassMapping mapping = null;
Status stat = null;
int alloc;
boolean updateStatSeq;
AllocateSequenceRunnable(JDBCStore store, ClassMapping mapping,
Status stat, int alloc, boolean updateStatSeq) {
this.store = store;
this.mapping = mapping;
this.stat = stat;
this.alloc = alloc;
this.updateStatSeq = updateStatSeq;
}
/**
* This method actually obtains the current sequence value.
*
* @throws RuntimeException
* any SQLExceptions that occur when obtaining the sequence
* value are wrapped in a runtime exception to avoid
* breaking the Runnable method signature. The caller can
* obtain the "real" exception by calling getCause().
*/
@Override
public void run() throws RuntimeException {
Connection conn = null;
SQLException err = null;
try {
// Try to use the store's connection.
conn = getConnection(store);
boolean sequenceSet =
setSequence(mapping, stat, alloc, updateStatSeq, conn);
closeConnection(conn);
if (!sequenceSet) {
// insert a new sequence column. Prefer connection2 / non-jta-data-source when inserting a
// sequence column regardless of Seq.type.
conn = _conf.getDataSource2(store.getContext()).getConnection();
try {
insertSequence(mapping, conn);
} catch (SQLException e) {
// it is possible another thread already got in and inserted this sequence. Try to keep going
if (_log.isTraceEnabled()) {
_log.trace(
"Caught an exception while trying to insert sequence. Will try to reselect the " +
"seqence. ", e);
}
}
conn.close();
// now we should be able to update using the connection per
// on the seq type.
conn = getConnection(store);
if (!setSequence(mapping, stat, alloc, updateStatSeq, conn))
{
throw (err != null) ? err : new SQLException(_loc.get(
"no-seq-row", mapping, _table).getMessage());
}
closeConnection(conn);
}
} catch (SQLException e) {
if (conn != null) {
closeConnection(conn);
}
RuntimeException re = new RuntimeException(e.getMessage());
re.initCause(e);
throw re;
}
}
}
/**
* CurentSequenceRunnable is a runnable wrapper which obtains the current
* sequence value from the database.
*/
protected class CurrentSequenceRunnable implements Runnable {
private JDBCStore _store;
private ClassMapping _mapping;
CurrentSequenceRunnable(JDBCStore store, ClassMapping mapping) {
_store = store;
_mapping = mapping;
}
/**
* This method actually obtains the current sequence value.
*
* @throws RuntimeException
* any SQLExceptions that occur when obtaining the sequence
* value are wrapped in a runtime exception to avoid
* breaking the Runnable method signature. The caller can
* obtain the "real" exception by calling getCause().
*/
@Override
public void run() throws RuntimeException {
Connection conn = null;
try {
conn = getConnection(_store);
long cur = getSequence(_mapping, conn);
if (cur != -1 ) // USE the constant
current = cur;
} catch (SQLException sqle) {
RuntimeException re = new RuntimeException(sqle.getMessage());
re.initCause(sqle);
throw re;
} finally {
if (conn != null) {
closeConnection(conn);
}
}
}
}
}