Phase one of the database dialect supports for Depot from Zell. This is all

enhancements to non-Depot JDBC utilities (and dialect agnosticizing
TransitionRepository though we could accomplish that by Depot-izing it as
well).


git-svn-id: https://samskivert.googlecode.com/svn/trunk@2134 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2007-07-23 21:32:22 +00:00
parent 3cd3397ad3
commit 868ebd38b4
11 changed files with 803 additions and 286 deletions
@@ -0,0 +1,233 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2006-2007 Michael Bayne, Pär Winzell
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.samskivert.Log;
import com.samskivert.util.StringUtil;
/**
* A superclass to help with the shrinking subset of SQL our supported dialects can agree on,
* or when there is disagreement, implement the most standard-compliant version and let the
* dialectal sub-classes override.
*/
public abstract class BaseLiaison implements DatabaseLiaison
{
public BaseLiaison ()
{
super();
}
// from DatabaseLiaison
public boolean tableExists (Connection conn, String name) throws SQLException
{
ResultSet rs = conn.getMetaData().getTables("", "", name, null);
while (rs.next()) {
String tname = rs.getString("TABLE_NAME");
if (name.equals(tname)) {
return true;
}
}
return false;
}
// from DatabaseLiaison
public boolean tableContainsColumn (Connection conn, String table, String column)
throws SQLException
{
ResultSet rs = conn.getMetaData().getColumns("", "", table, column);
while (rs.next()) {
String tname = rs.getString("TABLE_NAME");
String cname = rs.getString("COLUMN_NAME");
if (tname.equals(table) && cname.equals(column)) {
return true;
}
}
return false;
}
// from DatabaseLiaison
public boolean tableContainsIndex (Connection conn, String table, String index)
throws SQLException
{
ResultSet rs = conn.getMetaData().getIndexInfo("", "", table, false, true);
while (rs.next()) {
String tname = rs.getString("TABLE_NAME");
String iname = rs.getString("INDEX_NAME");
if (tname.equals(table) && index.equals(iname)) {
return true;
}
}
return false;
}
// from DatabaseLiaison
public boolean addIndexToTable (Connection conn, String table, String[] columns, String ixName)
throws SQLException
{
if (tableContainsIndex(conn, table, ixName)) {
return false;
}
ixName = (ixName != null ? ixName : StringUtil.join(columns, "_"));
StringBuilder update = new StringBuilder("CREATE INDEX ").append(indexSQL(ixName)).
append(" on ").append(tableSQL(table)).append(" (");
for (int ii = 0; ii < columns.length; ii ++) {
if (ii > 0) {
update.append(", ");
}
update.append(columnSQL(columns[ii]));
}
update.append(")");
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(update.toString());
stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
Log.info("Database index '" + ixName + "' added to table '" + table + "'");
return true;
}
// from DatabaseLiaison
public boolean changeColumn (Connection conn, String table, String column, String definition)
throws SQLException
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(
"ALTER TABLE " + tableSQL(table) + " CHANGE " + columnSQL(column) +
" " + column + " " + definition);
stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
Log.info("Database column '" + column + "' of table '" + table +
"' modified to have definition '" + definition + "'.");
return true;
}
// from DatabaseLiaison
public boolean renameColumn (Connection conn, String table, String from, String to)
throws SQLException
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(
"ALTER TABLE " + tableSQL(table) + " RENAME COLUMN " + columnSQL(from) +
" TO " + columnSQL(to));
if (stmt.executeUpdate() == 1) {
Log.info("Renamed column '" + from + "' on table '" + table + "' to '" + to + "'");
}
} finally {
JDBCUtil.close(stmt);
}
return true;
}
// from DatabaseLiaison
public boolean dropColumn (Connection conn, String table, String column) throws SQLException
{
if (!tableContainsColumn(conn, table, column)) {
return false;
}
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(
"ALTER TABLE " + tableSQL(table) + " DROP COLUMN " + columnSQL(column));
if (stmt.executeUpdate() == 1) {
Log.info("Database index '" + column + "' removed from table '" + table + "'.");
}
} finally {
JDBCUtil.close(stmt);
}
return true;
}
// from DatabaseLiaison
public boolean createTableIfMissing (
Connection conn, String table, String[] columns, String[] definitions,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns)
throws SQLException
{
if (tableExists(conn, table)) {
return false;
}
if (columns.length != definitions.length) {
throw new IllegalArgumentException("Column name and definition number mismatch");
}
StringBuilder builder =
new StringBuilder("CREATE TABLE ").append(tableSQL(table)).append(" (");
for (int ii = 0; ii < columns.length; ii ++) {
if (ii > 0) {
builder.append(", ");
}
builder.append(columnSQL(columns[ii])).append(" ").append(definitions[ii]);
}
if (uniqueConstraintColumns != null && uniqueConstraintColumns.length > 0) {
for (String[] uCols : uniqueConstraintColumns) {
builder.append(", UNIQUE (");
for (int ii = 0; ii < uCols.length; ii ++) {
if (ii > 0) {
builder.append(", ");
}
builder.append(columnSQL(uCols[ii]));
}
builder.append(")");
}
}
if (primaryKeyColumns != null && primaryKeyColumns.length > 0) {
builder.append(", PRIMARY KEY (");
for (int ii = 0; ii < primaryKeyColumns.length; ii ++) {
if (ii > 0) {
builder.append(", ");
}
builder.append(columnSQL(primaryKeyColumns[ii]));
}
builder.append(")");
}
builder.append(")");
Statement stmt = conn.createStatement();
try {
stmt.executeUpdate(builder.toString());
} finally {
JDBCUtil.close(stmt);
}
Log.info("Database table '" + table + "' created.");
return true;
}
}
@@ -25,69 +25,65 @@ import java.sql.*;
import com.samskivert.io.PersistenceException;
/**
* As the repository aims to interface with whatever database pooling
* services a project cares to use, it obtains all of its database
* connections through a connection provider. The connection provider
* provides connections based on a database identifier (a string) which
* identifies a particular connection to a particular database. A user of
* these services would then coordinate the database identifier (or
* identifiers) used to invoke a database operation with the database
* connections that are returned by the connection provider that they
* provided to the repository at construct time.
* As the repository aims to interface with whatever database pooling services a project cares to
* use, it obtains all of its database connections through a connection provider. The connection
* provider provides connections based on a database identifier (a string) which identifies a
* particular connection to a particular database. A user of these services would then coordinate
* the database identifier (or identifiers) used to invoke a database operation with the database
* connections that are returned by the connection provider that they provided to the repository at
* construct time.
*/
public interface ConnectionProvider
{
/**
* Obtains a database connection based on the supplied database
* identifier. The repository expects to have exclusive use of this
* connection instance until it releases it. This connection will be
* released subsequently with a call to {@link #releaseConnection} or
* {@link #connectionFailed} depending on the circumstances of the
* release. <code>close()</code> <em>will not</em> be called on the
* connection. It is up to the connection provider to close the
* connection when it is released if appropriate.
* Obtains a database connection based on the supplied database identifier. The repository
* expects to have exclusive use of this connection instance until it releases it. This
* connection will be released subsequently with a call to {@link #releaseConnection} or {@link
* #connectionFailed} depending on the circumstances of the release. <code>close()</code>
* <em>will not</em> be called on the connection. It is up to the connection provider to close
* the connection when it is released if appropriate.
*
* @param ident the database connection identifier.
* @param readOnly whether or not the connection may be to a read-only
* mirror of the repository.
* @param readOnly whether or not the connection may be to a read-only mirror of the
* repository.
*
* @return an active JDBC connection (which may have come from a
* connection pool).
* @return an active JDBC connection (which may have come from a connection pool).
*
* @exception PersistenceException thrown if a problem occurs trying
* to open the requested connection.
* @exception PersistenceException thrown if a problem occurs trying to open the requested
* connection.
*/
public Connection getConnection (String ident, boolean readOnly)
throws PersistenceException;
/**
* Releases a database connection when it is no longer needed by the
* repository.
* Releases a database connection when it is no longer needed by the repository.
*
* @param ident the database identifier used when obtaining this
* @param ident the database identifier used when obtaining this connection.
* @param readOnly the same value that was passed to {@link #getConnection} to obtain this
* connection.
* @param readOnly the same value that was passed to {@link #getConnection}
* to obtain this connection.
* @param conn the connection to release (back into the pool or to be
* closed if pooling is not going on).
* @param conn the connection to release (back into the pool or to be closed if pooling is not
* going on).
*/
public void releaseConnection (String ident, boolean readOnly,
Connection conn);
public void releaseConnection (String ident, boolean readOnly, Connection conn);
/**
* Called by the repository if a failure occurred on the connection.
* It is expected that the connection will be disposed of and
* subsequent calls to <code>getConnection</code> will return a
* freshly established connection to the database.
* Called by the repository if a failure occurred on the connection. It is expected that the
* connection will be disposed of and subsequent calls to <code>getConnection</code> will
* return a freshly established connection to the database.
*
* @param ident the database identifier used when obtaining this
* @param ident the database identifier used when obtaining this connection.
* @param readOnly the same value that was passed to {@link #getConnection} to obtain this
* connection.
* @param readOnly the same value that was passed to {@link #getConnection}
* to obtain this connection.
* @param conn the connection that failed.
* @param error the error thrown by the connection (which may be used
* to determine if the connection should be closed or can be reused).
* @param error the error thrown by the connection (which may be used to determine if the
* connection should be closed or can be reused).
*/
public void connectionFailed (String ident, boolean readOnly,
Connection conn, SQLException error);
public void connectionFailed (String ident, boolean readOnly, Connection conn,
SQLException error);
/**
* Returns the URL associated with this database identifier. This should be the same value that
* would be used if {@link #getConnection} were called.
*/
public String getURL (String ident);
}
+120 -28
View File
@@ -3,7 +3,7 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2007 Michael Bayne
//
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
@@ -24,52 +24,144 @@ import java.sql.Connection;
import java.sql.SQLException;
/**
* Despite good intentions, JDBC and SQL do not provide a unified
* interface to all databases. There remain idiosyncrasies that must be
* worked around when making code interact with different database
* servers. The database liaison encapsulates the code needed to
* straighten out the curves and curve out the straights.
* Despite good intentions, JDBC and SQL do not provide a unified interface to all databases. There
* remain idiosyncrasies that must be worked around when making code interact with different
* database servers. The database liaison encapsulates the code needed to straighten out the curves
* and curve out the straights.
*/
public interface DatabaseLiaison
{
/**
* Indicates whether or not this database liaison is the proper
* liaison for the specified database URL.
* Indicates whether or not this database liaison is the proper liaison for the specified
* database URL.
*
* @return true if we should use this liaison for connections created
* with the supplied URL, false if we should not.
* @return true if we should use this liaison for connections created with the supplied URL,
* false if we should not.
*/
public boolean matchesURL (String url);
/**
* Determines whether or not the supplied SQL exception was caused by
* a duplicate row being inserted into a table with a unique key.
* Determines whether or not the supplied SQL exception was caused by a duplicate row being
* inserted into a table with a unique key.
*
* @return true if the exception was caused by the insertion of a
* duplicate row, false if not.
* @return true if the exception was caused by the insertion of a duplicate row, false if not.
*/
public boolean isDuplicateRowException (SQLException sqe);
/**
* Determines whether or not the supplied SQL exception is a transient
* failure, meaning one that is not related to the SQL being executed,
* but instead to the environment at the time of execution, like the
* connection to the database having been lost.
* Determines whether or not the supplied SQL exception is a transient failure, meaning one
* that is not related to the SQL being executed, but instead to the environment at the time of
* execution, like the connection to the database having been lost.
*
* @return true if the exception was thrown due to a transient
* failure, false if not.
* @return true if the exception was thrown due to a transient failure, false if not.
*/
public boolean isTransientException (SQLException sqe);
/**
* Returns the value of an <code>AUTO_INCREMENT</code> column for the
* last row insertion. This is MySQL specific, but exists here for now
* until we sort out a general purpose mechanism for assigning unique
* ids.
* Returns the value of an <code>AUTO_INCREMENT</code> column for the last row insertion. This
* is MySQL specific, but exists here for now until we sort out a general purpose mechanism for
* assigning unique ids.
*
* @return the most recent ID generated by an insert into an
* <code>AUTO_INCREMENT</code> table or -1 if it could not be
* obtained.
* Note: More general purpose: {@link #lastInsertedId(Connection, String, String)
*
* @return the most recent ID generated by an insert into an <code>AUTO_INCREMENT</code> table
* or -1 if it could not be obtained.
*/
public int lastInsertedId (Connection conn) throws SQLException;
@Deprecated
public int lastInsertedId (Connection conn)
throws SQLException;
/**
* This method initializes the column value auto-generator described in {@link
* #lastInsertedId(Connection, String, String)}.
*/
public void initializeGenerator (
Connection conn, String table, String column, int first, int step)
throws SQLException;
/**
* This method attempts as dialect-agnostic an interface as possible to the ability of certain
* databases to auto-generated numerical values for i.e. key columns; there is MySQL's
* AUTO_INCREMENT and PostgreSQL's DEFAULT nextval(sequence), for example.
*/
public int lastInsertedId (Connection conn, String table, String column)
throws SQLException;
/**
* Drops the given column from the given table. Returns true or false if the database did or
* did not report a schema modification.
*/
public boolean dropColumn (Connection conn, String table, String column)
throws SQLException;
/**
* Adds a named index to a table on the given columns. Returns true or false if the database
* did or did not report a schema modification.
*/
public boolean addIndexToTable (Connection conn, String table, String[] columns, String index)
throws SQLException;
/**
* Alter the name, but not the definition, of a given column on a given table. Returns true or
* false if the database did or did not report a schema modification.
*/
public boolean renameColumn (Connection conn, String table, String oldColumn, String newColumn)
throws SQLException;
/**
* Alter the definition, but not the name, of a given column on a given table. Returns true or
* false if the database did or did not report a schema modification.
*
* TODO: We may wish to further split column definitions into type, nullability, etc.
*/
public boolean changeColumn (
Connection conn, String table, String column, String definition)
throws SQLException;
/**
* Created a new table of the given name with the given column names and column definitions;
* the given set of unique constraints (or null) and the given primary key columns (or null).
* Returns true if the table was successfully created, false if it already existed.
*/
public boolean createTableIfMissing (
Connection conn, String table, String[] columns, String[] definitions,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns)
throws SQLException;
/**
* Returns true if the specified table exists and contains an index of the specified name;
* false if either conditions does not hold true. <em>Note:</em> the names are case sensitive.
*/
public boolean tableContainsIndex (Connection conn, String table, String index)
throws SQLException;
/**
* Returns true if the specified table exists and contains a column with the specified name;
* false if either condition does not hold true. <em>Note:</em> the names are case sensitive.
*/
public boolean tableContainsColumn (Connection conn, String table, String column)
throws SQLException;
/**
* Returns true if the table with the specified name exists, false if it does not.
* <em>Note:</em> the table name is case sensitive.
*/
public boolean tableExists (Connection conn, String name) throws SQLException;
/**
* Returns the proper SQL to identify a table. Some databases require table names to be quoted.
*/
public String tableSQL (String table);
/**
* Returns the proper SQL to identify a column. Some databases require columns names to be
* quoted.
*/
public String columnSQL (String column);
/**
* Returns the proper SQL to identify an index. Some databases require index names to be
* quoted.
*/
public String indexSQL (String index);
}
@@ -24,33 +24,65 @@ import java.sql.Connection;
import java.sql.SQLException;
/**
* The default liaison is used if no other liaison could be matched for a
* particular database connection. It isn't very smart or useful but we
* need something.
* The default liaison is used if no other liaison could be matched for a particular database
* connection. It isn't very smart or useful but we need something.
*/
public class DefaultLiaison implements DatabaseLiaison
public class DefaultLiaison extends BaseLiaison
{
// documentation inherited
// from DatabaseLiaison
public boolean matchesURL (String url)
{
return true;
}
// documentation inherited
// from DatabaseLiaison
public boolean isDuplicateRowException (SQLException sqe)
{
return false;
}
// documentation inherited
// from DatabaseLiaison
public boolean isTransientException (SQLException sqe)
{
return false;
}
// documentation inherited
// from DatabaseLiaison
public int lastInsertedId (Connection conn) throws SQLException
{
return -1;
}
// from DatabaseLiaison
public void initializeGenerator (
Connection conn, String table, String column, int value, int size)
throws SQLException
{
// nothing doing
}
// from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column)
throws SQLException
{
return -1;
}
// from DatabaseLiaison
public String columnSQL (String column)
{
return column;
}
// from DatabaseLiaison
public String tableSQL (String table)
{
return table;
}
// from DatabaseLiaison
public String indexSQL (String index)
{
return index;
}
}
@@ -20,7 +20,6 @@
package com.samskivert.jdbc;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Iterator;
@@ -3,7 +3,7 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2007 Michael Bayne
//
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
@@ -23,30 +23,24 @@ package com.samskivert.jdbc;
import java.sql.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import com.samskivert.Log;
/**
* The liaison registry provides access to the appropriate database
* liaison implementation for a particular database connection.
* The liaison registry provides access to the appropriate database liaison implementation for a
* particular database connection.
*/
public class LiaisonRegistry
{
/**
* Fetch the appropriate database liaison for the supplied database
* connection.
* Fetch the appropriate database liaison for the supplied URL, which should be the same string
* that would be used to configure a connection to the database.
*/
public static DatabaseLiaison getLiaison (Connection conn)
throws SQLException
public static DatabaseLiaison getLiaison (String url)
{
DatabaseMetaData dmd = conn.getMetaData();
String url = dmd.getURL();
// see if we already have a liaison mapped for this connection
DatabaseLiaison liaison = _mappings.get(url);
if (liaison == null) {
// scan the list looking for a matching liaison
for (DatabaseLiaison candidate : _liaisons) {
@@ -58,8 +52,8 @@ public class LiaisonRegistry
// if we didn't find a matching liaison, use the default
if (liaison == null) {
Log.warning("Unable to match liaison for database " +
"[url=" + url + "]. Using default.");
Log.warning("Unable to match liaison for database [url=" + url + "]. " +
"Using default.");
liaison = new DefaultLiaison();
}
@@ -70,25 +64,34 @@ public class LiaisonRegistry
return liaison;
}
protected static void registerLiaisonClass (
Class<? extends DatabaseLiaison> lclass)
/**
* Fetch the appropriate database liaison for the supplied database connection.
*/
public static DatabaseLiaison getLiaison (Connection conn)
throws SQLException
{
return getLiaison(conn.getMetaData().getURL());
}
protected static void registerLiaisonClass (Class<? extends DatabaseLiaison> lclass)
{
// create a new instance and stick it on our list
try {
_liaisons.add(lclass.newInstance());
} catch (Exception e) {
Log.warning("Unable to instantiate liaison " +
"[class=" + lclass.getName() + ", error=" + e + "].");
Log.warning("Unable to instantiate liaison [class=" + lclass.getName() +
", error=" + e + "].");
}
}
protected static ArrayList<DatabaseLiaison> _liaisons =
new ArrayList<DatabaseLiaison>();
protected static ArrayList<DatabaseLiaison> _liaisons = new ArrayList<DatabaseLiaison>();
protected static HashMap<String,DatabaseLiaison> _mappings =
new HashMap<String,DatabaseLiaison>();
// register our liaison classes
static {
registerLiaisonClass(MySQLLiaison.class);
registerLiaisonClass(PostgreSQLLiaison.class);
}
}
+78 -14
View File
@@ -3,7 +3,7 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2007 Michael Bayne
//
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
@@ -22,25 +22,28 @@ package com.samskivert.jdbc;
import java.sql.*;
import com.samskivert.Log;
import com.samskivert.util.StringUtil;
/**
* A database liaison for the MySQL database.
*/
public class MySQLLiaison implements DatabaseLiaison
public class MySQLLiaison extends BaseLiaison
{
// documentation inherited
// from DatabaseLiaison
public boolean matchesURL (String url)
{
return url.startsWith("jdbc:mysql");
}
// documentation inherited
// from DatabaseLiaison
public boolean isDuplicateRowException (SQLException sqe)
{
String msg = sqe.getMessage();
return (msg != null && msg.indexOf("Duplicate entry") != -1);
}
// documentation inherited
// from DatabaseLiaison
public boolean isTransientException (SQLException sqe)
{
String msg = sqe.getMessage();
@@ -50,23 +53,84 @@ public class MySQLLiaison implements DatabaseLiaison
msg.indexOf("Broken pipe") != -1));
}
// documentation inherited
// from DatabaseLiaison
public int lastInsertedId (Connection conn) throws SQLException
{
Statement stmt = null;
return lastInsertedId(conn, null, null);
}
// we have to do this by hand. alas all is not roses.
// from DatabaseLiaison
public void initializeGenerator (
Connection conn, String table, String column, int first, int step)
throws SQLException
{
// AUTO_INCREMENT does not allow this kind of control
}
// from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException
{
// MySQL does not keep track of per-table-and-column insertion data, so we are pretty much
// going on blind faith here that we're fetching the right ID. In the overwhelming number
// of cases that will be so, but it's still not pretty.
Statement stmt = null;
try {
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()");
if (rs.next()) {
return rs.getInt(1);
} else {
return -1;
}
return rs.next() ? rs.getInt(1) : -1;
} finally {
JDBCUtil.close(stmt);
}
}
@Override // from BaseLiaison
public boolean addIndexToTable (Connection conn, String table, String[] columns, String ixName)
throws SQLException
{
if (tableContainsIndex(conn, table, ixName)) {
return false;
}
ixName = (ixName != null ? ixName : StringUtil.join(columns, "_"));
// MySQL's "CREATE INDEX" is buggy, it actually changes the case of the table names you're
// working with. Luckily (?) ALTER TABLE ADD INDEX works, so we do that here.
StringBuilder update = new StringBuilder("ALTER TABLE ").append(table).
append(" ADD INDEX ").append(indexSQL(ixName)).append(" (");
for (int ii = 0; ii < columns.length; ii ++) {
if (ii > 0) {
update.append(", ");
}
update.append(columnSQL(columns[ii]));
}
update.append(")");
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(update.toString());
stmt.executeUpdate();
} finally {
JDBCUtil.close(stmt);
}
Log.info("Database index '" + ixName + "' added to table '" + table + "'");
return true;
}
// from DatabaseLiaison
public String columnSQL (String column)
{
return column;
}
// from DatabaseLiaison
public String tableSQL (String table)
{
return table;
}
// from DatabaseLiaison
public String indexSQL (String index)
{
return index;
}
}
@@ -0,0 +1,113 @@
//
// $Id$
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc;
import java.sql.*;
/**
* A database liaison for the MySQL database.
*/
public class PostgreSQLLiaison extends BaseLiaison
{
// from DatabaseLiaison
public boolean matchesURL (String url)
{
return url.startsWith("jdbc:postgresql");
}
// from DatabaseLiaison
public boolean isDuplicateRowException (SQLException sqe)
{
String msg = sqe.getMessage();
return (msg != null && msg.indexOf("duplicate key") != -1);
}
// from DatabaseLiaison
public boolean isTransientException (SQLException sqe)
{
// TODO: Add more error messages here as we encounter them.
String msg = sqe.getMessage();
return (msg != null &&
msg.indexOf("An I/O error occured while sending to the backend") != -1);
}
// from DatabaseLiaison
public int lastInsertedId (Connection conn) throws SQLException
{
throw new SQLException(
"No backwards-compatible lastInsertedId() support for PostgreSQL in this code.");
}
// from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException
{
// PostgreSQL's support for auto-generated ID's comes in the form of appropriately named
// sequences and DEFAULT nextval(sequence) modifiers in the ID columns. To get the next ID,
// we use the currval() method which is set in a database sessions when any given sequence
// is incremented.
Statement stmt = null;
try {
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
"select currval('\"" + table + "_" + column + "_seq\"')");
if (rs.next()) {
return rs.getInt(1);
} else {
return -1;
}
} finally {
JDBCUtil.close(stmt);
}
}
public void initializeGenerator (
Connection conn, String table, String column, int first, int step)
throws SQLException
{
Statement stmt = conn.createStatement();
try {
stmt.executeUpdate("alter sequence \"" + table + "_" + column + "_seq\" " +
" restart with " + first + " increment " + step);
} finally {
JDBCUtil.close(stmt);
}
}
// from DatabaseLiaison
public String columnSQL (String column)
{
return "\"" + column + "\"";
}
// from DatabaseLiaison
public String tableSQL (String table)
{
return "\"" + table + "\"";
}
// from DatabaseLiaison
public String indexSQL (String index)
{
return "\"" + index + "\"";
}
}
@@ -27,9 +27,8 @@ import com.samskivert.io.PersistenceException;
import com.samskivert.util.StringUtil;
/**
* The simple repository should be used for a repository that only needs
* access to a single JDBC connection instance to perform its persistence
* services.
* The simple repository should be used for a repository that only needs access to a single JDBC
* connection instance to perform its persistence services.
*/
public class SimpleRepository extends Repository
{
@@ -41,12 +40,10 @@ public class SimpleRepository extends Repository
}
/**
* Configures an operation that will be invoked prior to the execution
* of every database operation to validate whether some pre-condition
* is met. This mainly exists for systems that wish to ensure that all
* database operations take place on a particular thread (or not on a
* particular thread) as database operations are generally slow and
* blocking.
* Configures an operation that will be invoked prior to the execution of every database
* operation to validate whether some pre-condition is met. This mainly exists for systems that
* wish to ensure that all database operations take place on a particular thread (or not on a
* particular thread) as database operations are generally slow and blocking.
*/
public static void setExecutePreCondition (PreCondition condition)
{
@@ -54,14 +51,13 @@ public class SimpleRepository extends Repository
}
/**
* Creates and initializes a simple repository which will access the
* database identified by the supplied database identifier.
* Creates and initializes a simple repository which will access the database identified by the
* supplied database identifier.
*
* @param provider the connection provider which will be used to
* obtain our database connection.
* @param dbident the identifier of the database that will be accessed by
* this repository or null if the derived class will call {@link
* #configureDatabaseIdent} by hand later.
* @param provider the connection provider which will be used to obtain our database
* connection.
* @param dbident the identifier of the database that will be accessed by this repository or
* null if the derived class will call {@link #configureDatabaseIdent} by hand later.
*/
public SimpleRepository (ConnectionProvider provider, String dbident)
{
@@ -73,18 +69,17 @@ public class SimpleRepository extends Repository
}
/**
* This is called automatically if a dbident is provided at construct time,
* but a derived class can pass null to its constructor and then call this
* method itself later if it wishes to obtain its database identifier from
* an overridable method which could not otherwise be called at construct
* time.
* This is called automatically if a dbident is provided at construct time, but a derived class
* can pass null to its constructor and then call this method itself later if it wishes to
* obtain its database identifier from an overridable method which could not otherwise be
* called at construct time.
*/
protected void configureDatabaseIdent (String dbident)
{
_dbident = dbident;
// give the repository a chance to do any schema migration before
// things get further underway
// give the repository a chance to do any schema migration before things get further
// underway
try {
executeUpdate(new Operation<Object>() {
public Object invoke (Connection conn, DatabaseLiaison liaison)
@@ -101,9 +96,8 @@ public class SimpleRepository extends Repository
}
/**
* Executes the supplied read-only operation. In the event of a transient
* failure, the repository will attempt to reestablish the database
* connection and try the operation again.
* Executes the supplied read-only operation. In the event of a transient failure, the
* repository will attempt to reestablish the database connection and try the operation again.
*
* @return whatever value is returned by the invoked operation.
*/
@@ -114,9 +108,8 @@ public class SimpleRepository extends Repository
}
/**
* Executes the supplied read-write operation. In the event of a transient
* failure, the repository will attempt to reestablish the database
* connection and try the operation again.
* Executes the supplied read-write operation. In the event of a transient failure, the
* repository will attempt to reestablish the database connection and try the operation again.
*
* @return whatever value is returned by the invoked operation.
*/
@@ -127,22 +120,18 @@ public class SimpleRepository extends Repository
}
/**
* Executes the supplied operation followed by a call to
* <code>commit()</code> on the connection unless a
* <code>PersistenceException</code> or runtime error occurs, in which
* case a call to <code>rollback()</code> is executed on the
* connection.
* Executes the supplied operation followed by a call to <code>commit()</code> on the
* connection unless a <code>PersistenceException</code> or runtime error occurs, in which case
* a call to <code>rollback()</code> is executed on the connection.
*
* @param retryOnTransientFailure if true and the operation fails due to a
* transient failure (like losing the connection to the database or
* deadlock detection), the connection to the database will be
* reestablished (if necessary) and the operation attempted once more.
* @param retryOnTransientFailure if true and the operation fails due to a transient failure
* (like losing the connection to the database or deadlock detection), the connection to the
* database will be reestablished (if necessary) and the operation attempted once more.
* @param readOnly whether or not to request a read-only connection.
*
* @return whatever value is returned by the invoked operation.
*/
protected <V> V execute (Operation<V> op, boolean retryOnTransientFailure,
boolean readOnly)
protected <V> V execute (Operation<V> op, boolean retryOnTransientFailure, boolean readOnly)
throws PersistenceException
{
Connection conn = null;
@@ -153,16 +142,16 @@ public class SimpleRepository extends Repository
// check our pre-condition
if (_precond != null && !_precond.validate(_dbident, op)) {
Log.warning("Repository operation failed pre-condition check! " +
"[dbident=" + _dbident + ", op=" + op + "].");
Log.warning("Repository operation failed pre-condition check! [dbident=" + _dbident +
", op=" + op + "].");
Thread.dumpStack();
}
// obtain our database connection and associated goodies
conn = _provider.getConnection(_dbident, readOnly);
// make sure that no one else performs a database operation using the
// same connection until we're done
// make sure that no one else performs a database operation using the same connection until
// we're done
synchronized (conn) {
try {
liaison = LiaisonRegistry.getLiaison(conn);
@@ -193,15 +182,15 @@ public class SimpleRepository extends Repository
} catch (SQLException sqe) {
if (attemptedOperation) {
// back out our changes if something got hosed (but not if
// the hosage was a result of losing our connection)
// back out our changes if something got hosed (but not if the hosage was a
// result of losing our connection)
try {
if (supportsTransactions && !conn.isClosed()) {
conn.rollback();
}
} catch (SQLException rbe) {
Log.warning("Unable to roll back operation " +
"[err=" + sqe + ", rberr=" + rbe + "].");
Log.warning("Unable to roll back operation [err=" + sqe +
", rberr=" + rbe + "].");
}
}
@@ -218,13 +207,11 @@ public class SimpleRepository extends Repository
throw new PersistenceException(err, sqe);
}
// the MySQL JDBC driver has the annoying habit of including
// the embedded exception stack trace in the message of their
// outer exception; if I want a fucking stack trace, I'll call
// printStackTrace() thanksverymuch
// the MySQL JDBC driver has the annoying habit of including the embedded exception
// stack trace in the message of their outer exception; if I want a fucking stack
// trace, I'll call printStackTrace() thanksverymuch
String msg = StringUtil.split("" + sqe, "\n")[0];
Log.info("Transient failure executing operation, " +
"retrying [error=" + msg + "].");
Log.info("Transient failure executing operation, retrying [error=" + msg + "].");
} catch (PersistenceException pe) {
// back out our changes if something got hosed
@@ -233,8 +220,8 @@ public class SimpleRepository extends Repository
conn.rollback();
}
} catch (SQLException rbe) {
Log.warning("Unable to roll back operation " +
"[origerr=" + pe + ", rberr=" + rbe + "].");
Log.warning("Unable to roll back operation [origerr=" + pe +
", rberr=" + rbe + "].");
}
throw pe;
@@ -246,8 +233,8 @@ public class SimpleRepository extends Repository
conn.rollback();
}
} catch (SQLException rbe) {
Log.warning("Unable to roll back operation " +
"[origerr=" + rte + ", rberr=" + rbe + "].");
Log.warning("Unable to roll back operation [origerr=" + rte +
", rberr=" + rbe + "].");
}
throw rte;
@@ -259,15 +246,15 @@ public class SimpleRepository extends Repository
}
}
// we'll only fall through here if the above code failed due to a
// transient exception (the connection was closed for being idle, for
// example) and we've been asked to retry; so let's do so
// we'll only fall through here if the above code failed due to a transient exception (the
// connection was closed for being idle, for example) and we've been asked to retry; so
// let's do so
return execute(op, false, readOnly);
}
/**
* Executes the supplied update query in this repository, returning
* the number of rows modified.
* Executes the supplied update query in this repository, returning the number of rows
* modified.
*/
protected int update (final String query)
throws PersistenceException
@@ -288,9 +275,8 @@ public class SimpleRepository extends Repository
}
/**
* Executes the supplied update query in this repository, throwing an
* exception if the modification count is not equal to the specified
* count.
* Executes the supplied update query in this repository, throwing an exception if the
* modification count is not equal to the specified count.
*/
protected void checkedUpdate (final String query, final int count)
throws PersistenceException
@@ -312,8 +298,8 @@ public class SimpleRepository extends Repository
}
/**
* Executes the supplied update query in this repository, logging a warning
* if the modification count is not equal to the specified count.
* Executes the supplied update query in this repository, logging a warning if the modification
* count is not equal to the specified count.
*/
protected void warnedUpdate (final String query, final int count)
throws PersistenceException
@@ -335,15 +321,12 @@ public class SimpleRepository extends Repository
}
/**
* Instructs MySQL to perform table maintenance on the specified
* table.
* Instructs MySQL to perform table maintenance on the specified table.
*
* @param action <code>analyze</code> recomputes the distribution of
* the keys for the specified table. This can help certain joins to be
* performed more efficiently. <code>optimize</code> instructs MySQL
* to coalesce fragmented records and reclaim space left by deleted
* records. This can improve a tables efficiency but can take a long
* time to run on large tables.
* @param action <code>analyze</code> recomputes the distribution of the keys for the specified
* table. This can help certain joins to be performed more efficiently. <code>optimize</code>
* instructs MySQL to coalesce fragmented records and reclaim space left by deleted records.
* This can improve a tables efficiency but can take a long time to run on large tables.
*/
protected void maintenance (final String action, final String table)
throws PersistenceException
@@ -355,15 +338,12 @@ public class SimpleRepository extends Repository
Statement stmt = null;
try {
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
action + " table " + table);
ResultSet rs = stmt.executeQuery(action + " table " + table);
while (rs.next()) {
String result = rs.getString("Msg_text");
if (result == null ||
result.indexOf("up to date") == -1 &&
!result.equals("OK")) {
Log.info("Table maintenance [" +
SimpleRepository.toString(rs) + "].");
(result.indexOf("up to date") == -1 && !result.equals("OK"))) {
Log.info("Table maintenance [" + SimpleRepository.toString(rs) + "].");
}
}
@@ -376,12 +356,10 @@ public class SimpleRepository extends Repository
}
/**
* Derived classes can override this method and perform any schema
* migration they might need (using the idempotent {@link JDBCUtil} schema
* migration methods). This is called during the repository's constructor
* and will thus take place before derived classes (like the {@link
* JORARepository} introspect on the schema to match it up to associated
* Java classes).
* Derived classes can override this method and perform any schema migration they might need
* (using the idempotent {@link JDBCUtil} schema migration methods). This is called during the
* repository's constructor and will thus take place before derived classes (like the {@link
* JORARepository} introspect on the schema to match it up to associated Java classes).
*/
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
@@ -389,17 +367,17 @@ public class SimpleRepository extends Repository
}
/**
* Called when we fetch a connection from the provider. This gives
* derived classes an opportunity to configure whatever internals they
* might be using with the connection that was fetched.
* Called when we fetch a connection from the provider. This gives derived classes an
* opportunity to configure whatever internals they might be using with the connection that was
* fetched.
*/
protected void gotConnection (Connection conn)
{
}
/**
* Converts a row of a result set to a string, prepending each column
* with the column name from the result set metadata.
* Converts a row of a result set to a string, prepending each column with the column name from
* the result set metadata.
*/
protected static String toString (ResultSet rs)
throws SQLException
@@ -418,5 +396,6 @@ public class SimpleRepository extends Repository
}
protected String _dbident;
protected static PreCondition _precond;
}
@@ -3,7 +3,7 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2007 Michael Bayne
//
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
@@ -31,15 +31,12 @@ import com.samskivert.util.PropertiesUtil;
import com.samskivert.util.StringUtil;
/**
* The static connection provider generates JDBC connections based on
* configuration information provided via a properties file. It does no
* connection pooling and always returns the same connection for a
* particular identifier (unless that connection need be closed because of
* a connection failure, in which case it opens a new one the next time
* the connection is requested).
* The static connection provider generates JDBC connections based on configuration information
* provided via a properties file. It does no connection pooling and always returns the same
* connection for a particular identifier (unless that connection need be closed because of a
* connection failure, in which case it opens a new one the next time the connection is requested).
*
* <p> The configuration properties file should contain the following
* information:
* <p> The configuration properties file should contain the following information:
*
* <pre>
* IDENT.driver=[jdbc driver class]
@@ -50,14 +47,13 @@ import com.samskivert.util.StringUtil;
* [...]
* </pre>
*
* Where <code>IDENT</code> is the database identifier for a particular
* database connection. When a particular database identifier is
* requested, the configuration information will be fetched from the
* properties.
* Where <code>IDENT</code> is the database identifier for a particular database connection. When a
* particular database identifier is requested, the configuration information will be fetched from
* the properties.
*
* <p> Additionally, a default set of properties can be provided using the
* identifier <code>default</code>. Values not provided for a specific
* identifier will be sought from the defaults. For example:
* <p> Additionally, a default set of properties can be provided using the identifier
* <code>default</code>. Values not provided for a specific identifier will be sought from the
* defaults. For example:
*
* <pre>
* default.driver=[jdbc driver class]
@@ -75,15 +71,15 @@ import com.samskivert.util.StringUtil;
public class StaticConnectionProvider implements ConnectionProvider
{
/**
* Constructs a static connection provider which will load its
* configuration from a properties file accessible via the classpath
* of the running application and identified by the specified path.
* Constructs a static connection provider which will load its configuration from a properties
* file accessible via the classpath of the running application and identified by the specified
* path.
*
* @param propPath the path (relative to the classpath) to the
* properties file that will be used for configuration information.
* @param propPath the path (relative to the classpath) to the properties file that will be
* used for configuration information.
*
* @exception IOException thrown if an error occurs locating or
* loading the specified properties file.
* @exception IOException thrown if an error occurs locating or loading the specified
* properties file.
*/
public StaticConnectionProvider (String propPath)
throws IOException
@@ -92,8 +88,8 @@ public class StaticConnectionProvider implements ConnectionProvider
}
/**
* Constructs a static connection provider which will fetch its
* configuration information from the specified properties object.
* Constructs a static connection provider which will fetch its configuration information from
* the specified properties object.
*
* @param props the configuration for this connection provider.
*/
@@ -103,8 +99,7 @@ public class StaticConnectionProvider implements ConnectionProvider
}
/**
* Closes all of the open database connections in preparation for
* shutting down.
* Closes all of the open database connections in preparation for shutting down.
*/
public void shutdown ()
{
@@ -112,12 +107,11 @@ public class StaticConnectionProvider implements ConnectionProvider
Iterator iter = _keys.keySet().iterator();
while (iter.hasNext()) {
String key = (String)iter.next();
Mapping conmap = (Mapping)_keys.get(key);
Mapping conmap = _keys.get(key);
try {
conmap.connection.close();
} catch (SQLException sqe) {
Log.warning("Error shutting down connection " +
"[key=" + key + ", err=" + sqe + "].");
Log.warning("Error shutting down connection [key=" + key + ", err=" + sqe + "].");
}
}
@@ -126,6 +120,13 @@ public class StaticConnectionProvider implements ConnectionProvider
_idents.clear();
}
// from ConnectionProvider
public String getURL (String ident)
{
Properties props = PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
return props.getProperty("url");
}
// documentation inherited
public Connection getConnection (String ident, boolean readOnly)
throws PersistenceException
@@ -135,9 +136,7 @@ public class StaticConnectionProvider implements ConnectionProvider
// open the connection if we haven't already
if (conmap == null) {
String[] defaults;
Properties props =
PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
Properties props = PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
// get the JDBC configuration info
String err = "No driver class specified [ident=" + ident + "].";
@@ -149,33 +148,30 @@ public class StaticConnectionProvider implements ConnectionProvider
err = "No driver password specified [ident=" + ident + "].";
String password = requireProp(props, "password", err);
// if this is a read-only connection,
// we cache connections by username+url+readOnly to avoid making
// more that one connection to a particular database server
// if this is a read-only connection, we cache connections by username+url+readOnly to
// avoid making more that one connection to a particular database server
String key = username + "@" + url + ":" + readOnly;
conmap = _keys.get(key);
if (conmap == null) {
Log.debug("Creating " + key + " for " + ident + ".");
conmap = new Mapping();
conmap.key = key;
conmap.connection =
openConnection(driver, url, username, password);
conmap.connection = openConnection(driver, url, username, password);
// make the connection read-only to let the JDBC driver know
// that it can and should use the read-only mirror(s)
// make the connection read-only to let the JDBC driver know that it can and should
// use the read-only mirror(s)
if (readOnly) {
try {
conmap.connection.setReadOnly(true);
} catch (SQLException sqe) {
closeConnection(ident, conmap.connection);
err = "Failed to make connection read-only " +
"[key=" + key + ", ident=" + ident + "].";
err = "Failed to make connection read-only [key=" + key +
", ident=" + ident + "].";
throw new PersistenceException(err, sqe);
}
}
_keys.put(key, conmap);
} else {
Log.debug("Reusing " + key + " for " + ident + ".");
}
@@ -185,19 +181,25 @@ public class StaticConnectionProvider implements ConnectionProvider
_idents.put(mapkey, conmap);
}
// in case the previous user turned off auto-commit we have to make sure here it's on
try {
conmap.connection.setAutoCommit(true);
} catch (SQLException e) {
throw new PersistenceException("Failed to turn on connection auto-commit", e);
}
return conmap.connection;
}
// documentation inherited
public void releaseConnection (String ident, boolean readOnly,
Connection conn)
public void releaseConnection (String ident, boolean readOnly, Connection conn)
{
// nothing to do here, all is well
}
// documentation inherited
public void connectionFailed (String ident, boolean readOnly,
Connection conn, SQLException error)
public void connectionFailed (
String ident, boolean readOnly, Connection conn, SQLException error)
{
String mapkey = ident + ":" + readOnly;
Mapping conmap = _idents.get(mapkey);
@@ -216,8 +218,7 @@ public class StaticConnectionProvider implements ConnectionProvider
_keys.remove(conmap.key);
}
protected Connection openConnection (
String driver, String url, String username, String password)
protected Connection openConnection (String driver, String url, String username, String passwd)
throws PersistenceException
{
// create an instance of the driver
@@ -233,11 +234,11 @@ public class StaticConnectionProvider implements ConnectionProvider
try {
Properties props = new Properties();
props.put("user", username);
props.put("password", password);
props.put("password", passwd);
return jdriver.connect(url, props);
} catch (SQLException sqe) {
String err = "Error creating database connection " +
"[driver=" + driver + ", url=" + url +
String err = "Error creating database connection [driver=" + driver + ", url=" + url +
", username=" + username + "].";
throw new PersistenceException(err, sqe);
}
@@ -253,25 +254,23 @@ public class StaticConnectionProvider implements ConnectionProvider
}
}
protected static String requireProp (
Properties props, String name, String errmsg)
protected static String requireProp (Properties props, String name, String errmsg)
throws PersistenceException
{
String value = props.getProperty(name);
if (StringUtil.isBlank(value)) {
// augment the error message
errmsg = "Unable to get connection. " + errmsg;
errmsg = "Unable to get connection. " + errmsg; // augment the error message
throw new PersistenceException(errmsg);
}
return value;
}
/** Contains information on a particular connection to which any
* number of database identifiers can be mapped. */
/** Contains information on a particular connection to which any number of database identifiers
* can be mapped. */
protected static class Mapping
{
/** The combination of username and JDBC url that uniquely
* identifies our database connection. */
/** The combination of username and JDBC url that uniquely identifies our database
* connection. */
public String key;
/** The connection itself. */
@@ -3,7 +3,7 @@
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2007 Michael Bayne
//
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
@@ -28,9 +28,8 @@ import java.sql.SQLException;
import com.samskivert.io.PersistenceException;
/**
* Used to note that transitionary code has been run to migrate persistent
* data. The TransitionRepository is especially useful for data that
* one cannot examine to determine if it's been transitioned.
* Used to note that transitionary code has been run to migrate persistent data. This is especially
* useful for data that one cannot examine to determine if it's been transitioned.
*/
public class TransitionRepository extends SimpleRepository
{
@@ -81,18 +80,20 @@ public class TransitionRepository extends SimpleRepository
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
Object found = null;
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement("select NAME " +
"from TRANSITIONS where CLASS=? and NAME=?");
stmt = conn.prepareStatement(
" select " + liaison.columnSQL("NAME") +
" from " + liaison.tableSQL("TRANSITIONS") +
" where " + liaison.columnSQL("CLASS") + "=?" +
" and " + liaison.columnSQL("NAME") + "=?");
stmt.setString(1, cname);
stmt.setString(2, name);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
while (rs.next()); // is this really necessary?
return true;
}
} finally {
JDBCUtil.close(stmt);
}
@@ -114,12 +115,14 @@ public class TransitionRepository extends SimpleRepository
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement("insert into TRANSITIONS " +
"(CLASS, NAME) values (?, ?)");
stmt = conn.prepareStatement(
"insert into " + liaison.tableSQL("TRANSITIONS") + " (" +
liaison.columnSQL("CLASS") + ", " + liaison.columnSQL("NAME") +
") values (?, ?)");
stmt.setString(1, cname);
stmt.setString(2, name);
JDBCUtil.checkedUpdate(stmt, 1);
} finally {
JDBCUtil.close(stmt);
}
@@ -141,8 +144,10 @@ public class TransitionRepository extends SimpleRepository
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement("delete from TRANSITIONS " +
"where CLASS=? and NAME=?");
stmt = conn.prepareStatement(
" delete from " + liaison.tableSQL("TRANSITIONS") +
" where " + liaison.columnSQL("CLASS") + "=? " +
" and " + liaison.columnSQL("NAME") + "=?");
stmt.setString(1, cname);
stmt.setString(2, name);
@@ -160,10 +165,12 @@ public class TransitionRepository extends SimpleRepository
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
JDBCUtil.createTableIfMissing(conn, "TRANSITIONS", new String[] {
"CLASS varchar(200) not null",
"NAME varchar(50) not null",
"APPLIED timestamp not null",
"primary key (CLASS, NAME)" }, "");
liaison.createTableIfMissing(
conn,
"TRANSITIONS",
new String[] { "CLASS", "NAME", "APPLIED" },
new String[] { "VARCHAR(200)", "VARCHAR(50)", "TIMESTAMP NOT NULL" },
null,
new String[] { "CLASS", "NAME" });
}
}