A (slightly modified) patch from Charlie to use the magical statement

collecting Connection to automatically close all statements created during the
course of our operations.
This commit is contained in:
Michael Bayne
2009-09-04 17:37:57 +00:00
parent 52a9c2036f
commit 82d742ec09
11 changed files with 204 additions and 294 deletions
@@ -21,7 +21,6 @@
package com.samskivert.depot; package com.samskivert.depot;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
@@ -39,8 +38,6 @@ import com.samskivert.util.ArrayUtil;
import com.samskivert.jdbc.ConnectionProvider; import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.clause.FieldOverride; import com.samskivert.depot.clause.FieldOverride;
import com.samskivert.depot.clause.InsertClause; import com.samskivert.depot.clause.InsertClause;
import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.clause.QueryClause;
@@ -459,19 +456,13 @@ public abstract class DepotRepository
builder.newQuery(new InsertClause(pClass, _result, identityFields)); builder.newQuery(new InsertClause(pClass, _result, identityFields));
PreparedStatement stmt = builder.prepare(conn); int mods = builder.prepare(conn).executeUpdate();
try { // run any post-factum value generators and potentially generate our key
int mods = stmt.executeUpdate(); if (_key == null) {
// run any post-factum value generators and potentially generate our key marsh.generateFieldValues(conn, liaison, _result, true);
if (_key == null) { updateKey(marsh.getPrimaryKey(_result, false));
marsh.generateFieldValues(conn, liaison, _result, true);
updateKey(marsh.getPrimaryKey(_result, false));
}
return mods;
} finally {
JDBCUtil.close(stmt);
} }
return mods;
} }
}); });
} }
@@ -698,44 +689,36 @@ public abstract class DepotRepository
_ctx.invoke(new CachingModifier<T>(record, key, key) { _ctx.invoke(new CachingModifier<T>(record, key, key) {
@Override @Override
protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
PreparedStatement stmt = null; if (_key != null) {
try { // run the update
if (_key != null) { int mods = builder.prepare(conn).executeUpdate();
// run the update if (mods > 0) {
stmt = builder.prepare(conn); // if it succeeded, we're done
int mods = stmt.executeUpdate(); return mods;
if (mods > 0) {
// if it succeeded, we're done
return mods;
}
JDBCUtil.close(stmt);
} }
// if the update modified zero rows or the primary key was unset, insert
Set<String> identityFields = Collections.emptySet();
if (_key == null) {
// first, set any auto-generated column values
identityFields = marsh.generateFieldValues(conn, liaison, _result, false);
// update our modifier's key so that it can cache our results
updateKey(marsh.getPrimaryKey(_result, false));
}
builder.newQuery(new InsertClause(pClass, _result, identityFields));
stmt = builder.prepare(conn);
int mods = stmt.executeUpdate();
// run any post-factum value generators and potentially generate our key
if (_key == null) {
marsh.generateFieldValues(conn, liaison, _result, true);
updateKey(marsh.getPrimaryKey(_result, false));
}
created[0] = true;
return mods;
} finally {
JDBCUtil.close(stmt);
} }
// if the update modified zero rows or the primary key was unset, insert
Set<String> identityFields = Collections.emptySet();
if (_key == null) {
// first, set any auto-generated column values
identityFields = marsh.generateFieldValues(conn, liaison, _result, false);
// update our modifier's key so that it can cache our results
updateKey(marsh.getPrimaryKey(_result, false));
}
builder.newQuery(new InsertClause(pClass, _result, identityFields));
int mods = builder.prepare(conn).executeUpdate();
// run any post-factum value generators and potentially generate our key
if (_key == null) {
marsh.generateFieldValues(conn, liaison, _result, true);
updateKey(marsh.getPrimaryKey(_result, false));
}
created[0] = true;
return mods;
} }
}); });
return created[0]; return created[0];
@@ -821,12 +804,7 @@ public abstract class DepotRepository
return _ctx.invoke(new Modifier(invalidator) { return _ctx.invoke(new Modifier(invalidator) {
@Override @Override
protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
PreparedStatement stmt = builder.prepare(conn); return builder.prepare(conn).executeUpdate();
try {
return stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
} }
}); });
} }
@@ -855,12 +833,7 @@ public abstract class DepotRepository
return _ctx.invoke(new Modifier(invalidator) { return _ctx.invoke(new Modifier(invalidator) {
@Override @Override
protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
PreparedStatement stmt = builder.prepare(conn); return builder.prepare(conn).executeUpdate();
try {
return stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
} }
}); });
} }
@@ -27,6 +27,7 @@ import java.util.Set;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@@ -37,6 +38,7 @@ import com.samskivert.util.StringUtil;
import com.samskivert.jdbc.ConnectionProvider; import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.LiaisonRegistry; import com.samskivert.jdbc.LiaisonRegistry;
import com.samskivert.depot.CacheAdapter.CacheCategory; import com.samskivert.depot.CacheAdapter.CacheCategory;
@@ -539,6 +541,10 @@ public class PersistenceContext
throw new DatabaseException(pe.getMessage(), pe.getCause()); throw new DatabaseException(pe.getMessage(), pe.getCause());
} }
// wrap the connection in a proxy that will collect all opened statements
List<Statement> stmts = Lists.newArrayListWithCapacity(1);
conn = JDBCUtil.makeCollector(conn, stmts);
// TEMP: we synchronize on the connection to cooperate with SimpleRepository when used in // TEMP: we synchronize on the connection to cooperate with SimpleRepository when used in
// conjunction with a StaticConnectionProvider; at some point we'll switch to standard JDBC // conjunction with a StaticConnectionProvider; at some point we'll switch to standard JDBC
// connection pooling which will block in getConnection() instead of returning a connection // connection pooling which will block in getConnection() instead of returning a connection
@@ -547,7 +553,18 @@ public class PersistenceContext
long preInvoke = System.nanoTime(); long preInvoke = System.nanoTime();
try { try {
// invoke our database operation // invoke our database operation
T value = op.invoke(this, conn, _liaison); T value;
try {
value = op.invoke(this, conn, _liaison);
} finally {
// close all opened statements; if any close fails, abort the close process as
// the whole connection is now unusable and will be discarded
for (Statement stmt : stmts) {
if (!stmt.isClosed()) {
stmt.close();
}
}
}
// if auto-commit is off and this is a write operation, push it through // if auto-commit is off and this is a write operation, push it through
if (!isReadOnly && !conn.getAutoCommit()) { if (!isReadOnly && !conn.getAutoCommit()) {
conn.commit(); conn.commit();
@@ -44,8 +44,6 @@ import com.samskivert.util.Tuple;
import com.samskivert.jdbc.ColumnDefinition; import com.samskivert.jdbc.ColumnDefinition;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.DatabaseException; import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.Key; import com.samskivert.depot.Key;
import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistenceContext;
@@ -711,12 +709,7 @@ public class DepotMarshaller<T extends PersistentRecord>
throws SQLException throws SQLException
{ {
if (builder.newQuery(clause)) { if (builder.newQuery(clause)) {
PreparedStatement stmt = builder.prepare(conn); return builder.prepare(conn).executeUpdate();
try {
return stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
} }
return 0; return 0;
} }
@@ -28,8 +28,6 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.DatabaseException; import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.Key; import com.samskivert.depot.Key;
import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistenceContext;
@@ -76,19 +74,15 @@ public class FindAllKeysQuery<T extends PersistentRecord> extends Query<XList<Ke
{ {
XList<Key<T>> keys = new XArrayList<Key<T>>(); XList<Key<T>> keys = new XArrayList<Key<T>>();
PreparedStatement stmt = _builder.prepare(conn); PreparedStatement stmt = _builder.prepare(conn);
try { ResultSet rs = stmt.executeQuery();
ResultSet rs = stmt.executeQuery(); while (rs.next()) {
while (rs.next()) { keys.add(_marsh.makePrimaryKey(rs));
keys.add(_marsh.makePrimaryKey(rs));
}
// TODO: cache this result?
if (PersistenceContext.CACHE_DEBUG) {
log.info("Loaded " + _marsh.getTableName(), "count", keys.size());
}
return keys;
} finally {
JDBCUtil.close(stmt);
} }
// TODO: cache this result?
if (PersistenceContext.CACHE_DEBUG) {
log.info("Loaded " + _marsh.getTableName(), "count", keys.size());
}
return keys;
} }
// from Query // from Query
@@ -36,8 +36,6 @@ import com.google.common.collect.Sets;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.CacheAdapter.CacheCategory; import com.samskivert.depot.CacheAdapter.CacheCategory;
import com.samskivert.depot.DatabaseException; import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.DepotRepository.CacheStrategy; import com.samskivert.depot.DepotRepository.CacheStrategy;
@@ -132,13 +130,9 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<XLi
builder.newQuery(_select); builder.newQuery(_select);
PreparedStatement stmt = builder.prepare(conn); PreparedStatement stmt = builder.prepare(conn);
stmtString = stmt.toString(); // for debugging stmtString = stmt.toString(); // for debugging
try { ResultSet rs = stmt.executeQuery();
ResultSet rs = stmt.executeQuery(); while (rs.next()) {
while (rs.next()) { keys.add(_marsh.makePrimaryKey(rs));
keys.add(_marsh.makePrimaryKey(rs));
}
} finally {
JDBCUtil.close(stmt);
} }
_keys = KeySet.newKeySet(_type, keys); _keys = KeySet.newKeySet(_type, keys);
_uncachedQueries++; _uncachedQueries++;
@@ -239,14 +233,9 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<XLi
XList<T> result = new XArrayList<T>(); XList<T> result = new XArrayList<T>();
SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select));
builder.newQuery(_select); builder.newQuery(_select);
PreparedStatement stmt = builder.prepare(conn); ResultSet rs = builder.prepare(conn).executeQuery();
try { while (rs.next()) {
ResultSet rs = stmt.executeQuery(); result.add(_marsh.createObject(rs));
while (rs.next()) {
result.add(_marsh.createObject(rs));
}
} finally {
JDBCUtil.close(stmt);
} }
_explicitQueries++; _explicitQueries++;
if (PersistenceContext.CACHE_DEBUG) { if (PersistenceContext.CACHE_DEBUG) {
@@ -348,39 +337,33 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<XLi
_type, _marsh.getFieldNames(), KeySet.newKeySet(_type, keys)); _type, _marsh.getFieldNames(), KeySet.newKeySet(_type, keys));
SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select)); SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, select));
builder.newQuery(select); builder.newQuery(select);
PreparedStatement stmt = builder.prepare(conn); Set<Key<T>> got = Sets.newHashSet();
try { ResultSet rs = builder.prepare(conn).executeQuery();
Set<Key<T>> got = Sets.newHashSet(); int cnt = 0, dups = 0;
ResultSet rs = stmt.executeQuery(); while (rs.next()) {
int cnt = 0, dups = 0; T obj = _marsh.createObject(rs);
while (rs.next()) { Key<T> key = _marsh.getPrimaryKey(obj);
T obj = _marsh.createObject(rs); if (entities.put(key, obj) != null) {
Key<T> key = _marsh.getPrimaryKey(obj); dups++;
if (entities.put(key, obj) != null) {
dups++;
}
ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), obj.clone());
got.add(key);
cnt++;
} }
// if we get more results than we planned, or if we're doing a two-phase query and got ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), obj.clone());
// fewer, then complain got.add(key);
if (cnt > keys.size() || (origStmt != null && cnt < keys.size())) { cnt++;
log.warning("Row count mismatch in second pass", "origQuery", origStmt,
// we need toString() here or StringUtil will get smart and dump our
// KeySet using its iterator which results in verbosity
"wanted", KeySet.newKeySet(_type, keys).toString(),
"got", KeySet.newKeySet(_type, got).toString(),
"dups", dups, new Exception());
}
if (PersistenceContext.CACHE_DEBUG) {
log.info("Cached " + _marsh.getTableName(), "count", cnt);
}
} finally {
JDBCUtil.close(stmt);
} }
// if we get more results than we planned, or if we're doing a two-phase query and got
// fewer, then complain
if (cnt > keys.size() || (origStmt != null && cnt < keys.size())) {
log.warning("Row count mismatch in second pass", "origQuery", origStmt,
// we need toString() here or StringUtil will get smart and dump our
// KeySet using its iterator which results in verbosity
"wanted", KeySet.newKeySet(_type, keys).toString(), "got", KeySet.newKeySet(
_type, got).toString(), "dups", dups, new Exception());
}
if (PersistenceContext.CACHE_DEBUG) {
log.info("Cached " + _marsh.getTableName(), "count", cnt);
}
} }
protected String keysToString (Iterable<Key<T>> keySet) protected String keysToString (Iterable<Key<T>> keySet)
@@ -21,13 +21,10 @@
package com.samskivert.depot.impl; package com.samskivert.depot.impl;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.CacheKey; import com.samskivert.depot.CacheKey;
import com.samskivert.depot.DatabaseException; import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.DepotRepository; import com.samskivert.depot.DepotRepository;
@@ -82,40 +79,34 @@ public class FindOneQuery<T extends PersistentRecord> extends Query<T>
public T invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison) public T invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison)
throws SQLException throws SQLException
{ {
PreparedStatement stmt = _builder.prepare(conn); // load up the record in question
try { T result = null;
// load up the record in question ResultSet rs = _builder.prepare(conn).executeQuery();
T result = null; if (rs.next()) {
ResultSet rs = stmt.executeQuery(); result = _marsh.createObject(rs);
if (rs.next()) {
result = _marsh.createObject(rs);
}
// TODO: if (rs.next()) issue warning?
rs.close();
// potentially cache the result
CacheKey key = getCacheKey();
if (key == null) {
// no row-specific cache key was given, if we can, create a key from the record
if (result != null && _marsh.hasPrimaryKey()) {
key = new KeyCacheKey(_marsh.getPrimaryKey(result));
}
}
if (PersistenceContext.CACHE_DEBUG) {
log.info("Loaded " + (key != null ? key : _marsh.getTableName()));
}
if (key != null) {
ctx.cacheStore(CacheCategory.RECORD, key, (result != null) ? result.clone() : null);
if (PersistenceContext.CACHE_DEBUG) {
log.info("Cached " + key);
}
}
return result;
} finally {
JDBCUtil.close(stmt);
} }
// TODO: if (rs.next()) issue warning?
rs.close();
// potentially cache the result
CacheKey key = getCacheKey();
if (key == null) {
// no row-specific cache key was given, if we can, create a key from the record
if (result != null && _marsh.hasPrimaryKey()) {
key = new KeyCacheKey(_marsh.getPrimaryKey(result));
}
}
if (PersistenceContext.CACHE_DEBUG) {
log.info("Loaded " + (key != null ? key : _marsh.getTableName()));
}
if (key != null) {
ctx.cacheStore(CacheCategory.RECORD, key, (result != null) ? result.clone() : null);
if (PersistenceContext.CACHE_DEBUG) {
log.info("Cached " + key);
}
}
return result;
} }
// from Operation // from Operation
@@ -25,12 +25,10 @@ import java.sql.Clob;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Date; import java.sql.Date;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Set; import java.util.Set;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.util.Tuple; import com.samskivert.util.Tuple;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
@@ -206,13 +204,8 @@ public class MySQLBuilder
} }
update.append(")"); update.append(")");
Statement stmt = conn.createStatement(); log.info("Adding full-text search index: ftsIx_" + fts.name());
try { conn.createStatement().executeUpdate(update.toString());
log.info("Adding full-text search index: ftsIx_" + fts.name());
stmt.executeUpdate(update.toString());
} finally {
JDBCUtil.close(stmt);
}
return true; return true;
} }
@@ -22,6 +22,7 @@ package com.samskivert.depot.impl;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistenceContext;
import com.samskivert.depot.Stats; import com.samskivert.depot.Stats;
@@ -38,7 +39,9 @@ public interface Operation<T>
public boolean isReadOnly (); public boolean isReadOnly ();
/** /**
* Performs the actual JDBC interactions associated with this operation. * Performs the actual JDBC interactions associated with this operation. Any {@link Statement}
* instances created with the given connection will be closed automatically after this method
* returns. The operation need not close them itself.
*/ */
public T invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison) public T invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison)
throws SQLException; throws SQLException;
@@ -31,7 +31,6 @@ import java.sql.Timestamp;
import java.util.Set; import java.util.Set;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.LiaisonRegistry; import com.samskivert.jdbc.LiaisonRegistry;
import com.samskivert.util.ArrayUtil; import com.samskivert.util.ArrayUtil;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
@@ -185,18 +184,12 @@ public class PostgreSQLBuilder
append(liaison.columnSQL(column)).append(")"); append(liaison.columnSQL(column)).append(")");
Statement stmt = conn.createStatement(); Statement stmt = conn.createStatement();
try { log.info("Adding full-text search column, index and trigger: " + column + ", " +
log.info( index + ", " + trigger);
"Adding full-text search column, index and trigger: " + column + ", " + liaison.addColumn(conn, table, column, "TSVECTOR", true);
index + ", " + trigger); stmt.executeUpdate(initColumn.toString());
liaison.addColumn(conn, table, column, "TSVECTOR", true); stmt.executeUpdate(createIndex.toString());
stmt.executeUpdate(initColumn.toString()); stmt.executeUpdate(createTrigger.toString());
stmt.executeUpdate(createIndex.toString());
stmt.executeUpdate(createTrigger.toString());
} finally {
JDBCUtil.close(stmt);
}
return true; return true;
} }
@@ -30,7 +30,6 @@ import com.samskivert.depot.annotation.TableGenerator;
import com.samskivert.jdbc.ColumnDefinition; import com.samskivert.jdbc.ColumnDefinition;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
/** /**
* Generates primary keys using an external table . * Generates primary keys using an external table .
@@ -69,106 +68,83 @@ public class TableValueGenerator extends ValueGenerator
new String[] { _pkColumnName }); new String[] { _pkColumnName });
// and also that there's a row in it for us // and also that there's a row in it for us
PreparedStatement stmt = null; PreparedStatement stmt = conn.prepareStatement(
try { " SELECT * FROM " + liaison.tableSQL(_valueTable) +
stmt = conn.prepareStatement( " WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
" SELECT * FROM " + liaison.tableSQL(_valueTable) + stmt.setString(1, _pkColumnValue);
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?"); if (stmt.executeQuery().next()) {
stmt.setString(1, _pkColumnValue); return;
if (stmt.executeQuery().next()) {
return;
}
JDBCUtil.close(stmt);
stmt = null;
int initialValue = _initialValue;
if (_migrateIfExists) {
Integer max = getFieldMaximum(conn, liaison);
if (max != null) {
initialValue = 1 + max.intValue();
}
}
stmt = conn.prepareStatement(
" INSERT INTO " + liaison.tableSQL(_valueTable) + " (" +
liaison.columnSQL(_pkColumnName) + ", " + liaison.columnSQL(_valueColumnName) +
") VALUES (?, ?)");
stmt.setString(1, _pkColumnValue);
stmt.setInt(2, initialValue);
stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
} }
int initialValue = _initialValue;
if (_migrateIfExists) {
Integer max = getFieldMaximum(conn, liaison);
if (max != null) {
initialValue = 1 + max.intValue();
}
}
stmt = conn.prepareStatement(
" INSERT INTO " + liaison.tableSQL(_valueTable) + " (" +
liaison.columnSQL(_pkColumnName) + ", " + liaison.columnSQL(_valueColumnName) +
") VALUES (?, ?)");
stmt.setString(1, _pkColumnValue);
stmt.setInt(2, initialValue);
stmt.executeUpdate();
} }
@Override // from ValueGenerator @Override // from ValueGenerator
public void delete (Connection conn, DatabaseLiaison liaison) throws SQLException public void delete (Connection conn, DatabaseLiaison liaison) throws SQLException
{ {
PreparedStatement stmt = null; PreparedStatement stmt = conn.prepareStatement(
try { " DELETE FROM " + liaison.tableSQL(_valueTable) +
stmt = conn.prepareStatement( " WHERE " + liaison.columnSQL(_pkColumnName) + " = ?");
" DELETE FROM " + liaison.tableSQL(_valueTable) + stmt.setString(1, _pkColumnValue);
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ?"); stmt.executeUpdate();
stmt.setString(1, _pkColumnValue);
stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
} }
@Override // from ValueGenerator @Override // from ValueGenerator
public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison) public int nextGeneratedValue (Connection conn, DatabaseLiaison liaison)
throws SQLException throws SQLException
{ {
PreparedStatement readStatement = null; // TODO: Make this lockless!
PreparedStatement writeStatement = null; PreparedStatement readStatement = conn.prepareStatement(
try { " SELECT " + liaison.columnSQL(_valueColumnName) +
// TODO: Make this lockless! " FROM " + liaison.tableSQL(_valueTable) +
readStatement = conn.prepareStatement( " WHERE " + liaison.columnSQL(_pkColumnName) + " = ? ");
" SELECT " + liaison.columnSQL(_valueColumnName) + readStatement.setString(1, _pkColumnValue);
" FROM " + liaison.tableSQL(_valueTable) +
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ? ");
readStatement.setString(1, _pkColumnValue);
writeStatement = conn.prepareStatement( PreparedStatement writeStatement = conn.prepareStatement(
" UPDATE " + liaison.tableSQL(_valueTable) + " UPDATE " + liaison.tableSQL(_valueTable) +
" SET " + liaison.columnSQL(_valueColumnName) + " = ? " + " SET " + liaison.columnSQL(_valueColumnName) + " = ? " +
" WHERE " + liaison.columnSQL(_pkColumnName) + " = ? " + " WHERE " + liaison.columnSQL(_pkColumnName) + " = ? " +
" AND " + liaison.columnSQL(_valueColumnName) + " = ? "); " AND " + liaison.columnSQL(_valueColumnName) + " = ? ");
for (int tries = 0; tries < 10; tries ++) { for (int tries = 0; tries < 10; tries ++) {
// execute the query // execute the query
ResultSet rs = readStatement.executeQuery(); ResultSet rs = readStatement.executeQuery();
if (!rs.next()) { if (!rs.next()) {
throw new SQLException( throw new SQLException(
"Failed to find next primary key value [table=" + _valueTable + "Failed to find next primary key value [table=" + _valueTable +
", column=" + _valueColumnName + "]"); ", column=" + _valueColumnName + "]");
}
// fetch the next available value
int val = rs.getInt(1);
// claim this value locklessly
writeStatement.setInt(1, val + _allocationSize);
writeStatement.setString(2, _pkColumnValue);
writeStatement.setInt(3, val);
// if we modified a row, we know we and nobody else got this particular value!
if (writeStatement.executeUpdate() == 1) {
return val;
}
// else try again
} }
throw new SQLException( // fetch the next available value
"Failed to claim next primary key value in 10 attempts [table=" + _valueTable + int val = rs.getInt(1);
", column=" + _valueColumnName + "]");
} finally { // claim this value locklessly
JDBCUtil.close(readStatement); writeStatement.setInt(1, val + _allocationSize);
JDBCUtil.close(writeStatement); writeStatement.setString(2, _pkColumnValue);
writeStatement.setInt(3, val);
// if we modified a row, we know we and nobody else got this particular value!
if (writeStatement.executeUpdate() == 1) {
return val;
}
// else try again
} }
throw new SQLException(
"Failed to claim next primary key value in 10 attempts [table=" + _valueTable +
", column=" + _valueColumnName + "]");
} }
/** /**
@@ -26,7 +26,6 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.depot.annotation.GeneratedValue; import com.samskivert.depot.annotation.GeneratedValue;
import static com.samskivert.depot.Log.log; import static com.samskivert.depot.Log.log;
@@ -85,24 +84,19 @@ public abstract class ValueGenerator
String table = _dm.getTableName(); String table = _dm.getTableName();
Statement stmt = conn.createStatement(); Statement stmt = conn.createStatement();
try { ResultSet rs = stmt.executeQuery(
ResultSet rs = stmt.executeQuery( " SELECT COUNT(*), MAX(" + liaison.columnSQL(column) + ") " +
" SELECT COUNT(*), MAX(" + liaison.columnSQL(column) + ") " + " FROM " + liaison.tableSQL(table));
" FROM " + liaison.tableSQL(table)); if (!rs.next()) {
if (!rs.next()) { log.warning("Query on count()/max() bizarrely returned no rows.");
log.warning("Query on count()/max() bizarrely returned no rows.");
return null;
}
int cnt = rs.getInt(1);
if (cnt > 0) {
return Integer.valueOf(rs.getInt(2));
}
return null; return null;
} finally {
JDBCUtil.close(stmt);
} }
int cnt = rs.getInt(1);
if (cnt > 0) {
return Integer.valueOf(rs.getInt(2));
}
return null;
} }
public DepotMarshaller<?> getDepotMarshaller () public DepotMarshaller<?> getDepotMarshaller ()