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:
@@ -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;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As the repository aims to interface with whatever database pooling
|
* As the repository aims to interface with whatever database pooling services a project cares to
|
||||||
* services a project cares to use, it obtains all of its database
|
* use, it obtains all of its database connections through a connection provider. The connection
|
||||||
* connections through a connection provider. The connection provider
|
* provider provides connections based on a database identifier (a string) which identifies a
|
||||||
* provides connections based on a database identifier (a string) which
|
* particular connection to a particular database. A user of these services would then coordinate
|
||||||
* identifies a particular connection to a particular database. A user of
|
* the database identifier (or identifiers) used to invoke a database operation with the database
|
||||||
* these services would then coordinate the database identifier (or
|
* connections that are returned by the connection provider that they provided to the repository at
|
||||||
* identifiers) used to invoke a database operation with the database
|
* construct time.
|
||||||
* connections that are returned by the connection provider that they
|
|
||||||
* provided to the repository at construct time.
|
|
||||||
*/
|
*/
|
||||||
public interface ConnectionProvider
|
public interface ConnectionProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Obtains a database connection based on the supplied database
|
* Obtains a database connection based on the supplied database identifier. The repository
|
||||||
* identifier. The repository expects to have exclusive use of this
|
* expects to have exclusive use of this connection instance until it releases it. This
|
||||||
* connection instance until it releases it. This connection will be
|
* connection will be released subsequently with a call to {@link #releaseConnection} or {@link
|
||||||
* released subsequently with a call to {@link #releaseConnection} or
|
* #connectionFailed} depending on the circumstances of the release. <code>close()</code>
|
||||||
* {@link #connectionFailed} depending on the circumstances of the
|
* <em>will not</em> be called on the connection. It is up to the connection provider to close
|
||||||
* release. <code>close()</code> <em>will not</em> be called on the
|
* the connection when it is released if appropriate.
|
||||||
* 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 ident the database connection identifier.
|
||||||
* @param readOnly whether or not the connection may be to a read-only
|
* @param readOnly whether or not the connection may be to a read-only mirror of the
|
||||||
* mirror of the repository.
|
* repository.
|
||||||
*
|
*
|
||||||
* @return an active JDBC connection (which may have come from a
|
* @return an active JDBC connection (which may have come from a connection pool).
|
||||||
* connection pool).
|
|
||||||
*
|
*
|
||||||
* @exception PersistenceException thrown if a problem occurs trying
|
* @exception PersistenceException thrown if a problem occurs trying to open the requested
|
||||||
* to open the requested connection.
|
* connection.
|
||||||
*/
|
*/
|
||||||
public Connection getConnection (String ident, boolean readOnly)
|
public Connection getConnection (String ident, boolean readOnly)
|
||||||
throws PersistenceException;
|
throws PersistenceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases a database connection when it is no longer needed by the
|
* Releases a database connection when it is no longer needed by the repository.
|
||||||
* 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.
|
* connection.
|
||||||
* @param readOnly the same value that was passed to {@link #getConnection}
|
* @param conn the connection to release (back into the pool or to be closed if pooling is not
|
||||||
* to obtain this connection.
|
* 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,
|
public void releaseConnection (String ident, boolean readOnly, Connection conn);
|
||||||
Connection conn);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the repository if a failure occurred on the connection.
|
* Called by the repository if a failure occurred on the connection. It is expected that the
|
||||||
* It is expected that the connection will be disposed of and
|
* connection will be disposed of and subsequent calls to <code>getConnection</code> will
|
||||||
* subsequent calls to <code>getConnection</code> will return a
|
* return a freshly established connection to the database.
|
||||||
* 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.
|
* connection.
|
||||||
* @param readOnly the same value that was passed to {@link #getConnection}
|
|
||||||
* to obtain this connection.
|
|
||||||
* @param conn the connection that failed.
|
* @param conn the connection that failed.
|
||||||
* @param error the error thrown by the connection (which may be used
|
* @param error the error thrown by the connection (which may be used to determine if the
|
||||||
* to determine if the connection should be closed or can be reused).
|
* connection should be closed or can be reused).
|
||||||
*/
|
*/
|
||||||
public void connectionFailed (String ident, boolean readOnly,
|
public void connectionFailed (String ident, boolean readOnly, Connection conn,
|
||||||
Connection conn, SQLException error);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// samskivert library - useful routines for java programs
|
// samskivert library - useful routines for java programs
|
||||||
// Copyright (C) 2001-2007 Michael Bayne
|
// Copyright (C) 2001-2007 Michael Bayne
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or modify it
|
// 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
|
// 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
|
// 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;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Despite good intentions, JDBC and SQL do not provide a unified
|
* Despite good intentions, JDBC and SQL do not provide a unified interface to all databases. There
|
||||||
* interface to all databases. There remain idiosyncrasies that must be
|
* remain idiosyncrasies that must be worked around when making code interact with different
|
||||||
* worked around when making code interact with different database
|
* database servers. The database liaison encapsulates the code needed to straighten out the curves
|
||||||
* servers. The database liaison encapsulates the code needed to
|
* and curve out the straights.
|
||||||
* straighten out the curves and curve out the straights.
|
|
||||||
*/
|
*/
|
||||||
public interface DatabaseLiaison
|
public interface DatabaseLiaison
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Indicates whether or not this database liaison is the proper
|
* Indicates whether or not this database liaison is the proper liaison for the specified
|
||||||
* liaison for the specified database URL.
|
* database URL.
|
||||||
*
|
*
|
||||||
* @return true if we should use this liaison for connections created
|
* @return true if we should use this liaison for connections created with the supplied URL,
|
||||||
* with the supplied URL, false if we should not.
|
* false if we should not.
|
||||||
*/
|
*/
|
||||||
public boolean matchesURL (String url);
|
public boolean matchesURL (String url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether or not the supplied SQL exception was caused by
|
* Determines whether or not the supplied SQL exception was caused by a duplicate row being
|
||||||
* a duplicate row being inserted into a table with a unique key.
|
* inserted into a table with a unique key.
|
||||||
*
|
*
|
||||||
* @return true if the exception was caused by the insertion of a
|
* @return true if the exception was caused by the insertion of a duplicate row, false if not.
|
||||||
* duplicate row, false if not.
|
|
||||||
*/
|
*/
|
||||||
public boolean isDuplicateRowException (SQLException sqe);
|
public boolean isDuplicateRowException (SQLException sqe);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether or not the supplied SQL exception is a transient
|
* Determines whether or not the supplied SQL exception is a transient failure, meaning one
|
||||||
* failure, meaning one that is not related to the SQL being executed,
|
* that is not related to the SQL being executed, but instead to the environment at the time of
|
||||||
* but instead to the environment at the time of execution, like the
|
* execution, like the connection to the database having been lost.
|
||||||
* connection to the database having been lost.
|
|
||||||
*
|
*
|
||||||
* @return true if the exception was thrown due to a transient
|
* @return true if the exception was thrown due to a transient failure, false if not.
|
||||||
* failure, false if not.
|
|
||||||
*/
|
*/
|
||||||
public boolean isTransientException (SQLException sqe);
|
public boolean isTransientException (SQLException sqe);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of an <code>AUTO_INCREMENT</code> column for the
|
* Returns the value of an <code>AUTO_INCREMENT</code> column for the last row insertion. This
|
||||||
* last row insertion. This is MySQL specific, but exists here for now
|
* is MySQL specific, but exists here for now until we sort out a general purpose mechanism for
|
||||||
* until we sort out a general purpose mechanism for assigning unique
|
* assigning unique ids.
|
||||||
* ids.
|
|
||||||
*
|
*
|
||||||
* @return the most recent ID generated by an insert into an
|
* Note: More general purpose: {@link #lastInsertedId(Connection, String, String)
|
||||||
* <code>AUTO_INCREMENT</code> table or -1 if it could not be
|
*
|
||||||
* obtained.
|
* @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;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default liaison is used if no other liaison could be matched for a
|
* The default liaison is used if no other liaison could be matched for a particular database
|
||||||
* particular database connection. It isn't very smart or useful but we
|
* connection. It isn't very smart or useful but we need something.
|
||||||
* need something.
|
|
||||||
*/
|
*/
|
||||||
public class DefaultLiaison implements DatabaseLiaison
|
public class DefaultLiaison extends BaseLiaison
|
||||||
{
|
{
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public boolean matchesURL (String url)
|
public boolean matchesURL (String url)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public boolean isDuplicateRowException (SQLException sqe)
|
public boolean isDuplicateRowException (SQLException sqe)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public boolean isTransientException (SQLException sqe)
|
public boolean isTransientException (SQLException sqe)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public int lastInsertedId (Connection conn) throws SQLException
|
public int lastInsertedId (Connection conn) throws SQLException
|
||||||
{
|
{
|
||||||
return -1;
|
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;
|
package com.samskivert.jdbc;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// samskivert library - useful routines for java programs
|
// samskivert library - useful routines for java programs
|
||||||
// Copyright (C) 2001-2007 Michael Bayne
|
// Copyright (C) 2001-2007 Michael Bayne
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or modify it
|
// 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
|
// 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
|
// 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.sql.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import com.samskivert.Log;
|
import com.samskivert.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The liaison registry provides access to the appropriate database
|
* The liaison registry provides access to the appropriate database liaison implementation for a
|
||||||
* liaison implementation for a particular database connection.
|
* particular database connection.
|
||||||
*/
|
*/
|
||||||
public class LiaisonRegistry
|
public class LiaisonRegistry
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Fetch the appropriate database liaison for the supplied database
|
* Fetch the appropriate database liaison for the supplied URL, which should be the same string
|
||||||
* connection.
|
* that would be used to configure a connection to the database.
|
||||||
*/
|
*/
|
||||||
public static DatabaseLiaison getLiaison (Connection conn)
|
public static DatabaseLiaison getLiaison (String url)
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
DatabaseMetaData dmd = conn.getMetaData();
|
|
||||||
String url = dmd.getURL();
|
|
||||||
|
|
||||||
// see if we already have a liaison mapped for this connection
|
// see if we already have a liaison mapped for this connection
|
||||||
DatabaseLiaison liaison = _mappings.get(url);
|
DatabaseLiaison liaison = _mappings.get(url);
|
||||||
|
|
||||||
if (liaison == null) {
|
if (liaison == null) {
|
||||||
// scan the list looking for a matching liaison
|
// scan the list looking for a matching liaison
|
||||||
for (DatabaseLiaison candidate : _liaisons) {
|
for (DatabaseLiaison candidate : _liaisons) {
|
||||||
@@ -58,8 +52,8 @@ public class LiaisonRegistry
|
|||||||
|
|
||||||
// if we didn't find a matching liaison, use the default
|
// if we didn't find a matching liaison, use the default
|
||||||
if (liaison == null) {
|
if (liaison == null) {
|
||||||
Log.warning("Unable to match liaison for database " +
|
Log.warning("Unable to match liaison for database [url=" + url + "]. " +
|
||||||
"[url=" + url + "]. Using default.");
|
"Using default.");
|
||||||
liaison = new DefaultLiaison();
|
liaison = new DefaultLiaison();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,25 +64,34 @@ public class LiaisonRegistry
|
|||||||
return liaison;
|
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
|
// create a new instance and stick it on our list
|
||||||
try {
|
try {
|
||||||
_liaisons.add(lclass.newInstance());
|
_liaisons.add(lclass.newInstance());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.warning("Unable to instantiate liaison " +
|
Log.warning("Unable to instantiate liaison [class=" + lclass.getName() +
|
||||||
"[class=" + lclass.getName() + ", error=" + e + "].");
|
", error=" + e + "].");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static ArrayList<DatabaseLiaison> _liaisons =
|
protected static ArrayList<DatabaseLiaison> _liaisons = new ArrayList<DatabaseLiaison>();
|
||||||
new ArrayList<DatabaseLiaison>();
|
|
||||||
protected static HashMap<String,DatabaseLiaison> _mappings =
|
protected static HashMap<String,DatabaseLiaison> _mappings =
|
||||||
new HashMap<String,DatabaseLiaison>();
|
new HashMap<String,DatabaseLiaison>();
|
||||||
|
|
||||||
// register our liaison classes
|
// register our liaison classes
|
||||||
static {
|
static {
|
||||||
registerLiaisonClass(MySQLLiaison.class);
|
registerLiaisonClass(MySQLLiaison.class);
|
||||||
|
registerLiaisonClass(PostgreSQLLiaison.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// samskivert library - useful routines for java programs
|
// samskivert library - useful routines for java programs
|
||||||
// Copyright (C) 2001-2007 Michael Bayne
|
// Copyright (C) 2001-2007 Michael Bayne
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or modify it
|
// 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
|
// 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
|
// 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 java.sql.*;
|
||||||
|
|
||||||
|
import com.samskivert.Log;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A database liaison for the MySQL database.
|
* 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)
|
public boolean matchesURL (String url)
|
||||||
{
|
{
|
||||||
return url.startsWith("jdbc:mysql");
|
return url.startsWith("jdbc:mysql");
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public boolean isDuplicateRowException (SQLException sqe)
|
public boolean isDuplicateRowException (SQLException sqe)
|
||||||
{
|
{
|
||||||
String msg = sqe.getMessage();
|
String msg = sqe.getMessage();
|
||||||
return (msg != null && msg.indexOf("Duplicate entry") != -1);
|
return (msg != null && msg.indexOf("Duplicate entry") != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public boolean isTransientException (SQLException sqe)
|
public boolean isTransientException (SQLException sqe)
|
||||||
{
|
{
|
||||||
String msg = sqe.getMessage();
|
String msg = sqe.getMessage();
|
||||||
@@ -50,23 +53,84 @@ public class MySQLLiaison implements DatabaseLiaison
|
|||||||
msg.indexOf("Broken pipe") != -1));
|
msg.indexOf("Broken pipe") != -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// from DatabaseLiaison
|
||||||
public int lastInsertedId (Connection conn) throws SQLException
|
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 {
|
try {
|
||||||
stmt = conn.createStatement();
|
stmt = conn.createStatement();
|
||||||
ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()");
|
ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()");
|
||||||
if (rs.next()) {
|
return rs.next() ? rs.getInt(1) : -1;
|
||||||
return rs.getInt(1);
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
JDBCUtil.close(stmt);
|
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;
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The simple repository should be used for a repository that only needs
|
* The simple repository should be used for a repository that only needs access to a single JDBC
|
||||||
* access to a single JDBC connection instance to perform its persistence
|
* connection instance to perform its persistence services.
|
||||||
* services.
|
|
||||||
*/
|
*/
|
||||||
public class SimpleRepository extends Repository
|
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
|
* Configures an operation that will be invoked prior to the execution of every database
|
||||||
* of every database operation to validate whether some pre-condition
|
* operation to validate whether some pre-condition is met. This mainly exists for systems that
|
||||||
* is met. This mainly exists for systems that wish to ensure that all
|
* wish to ensure that all database operations take place on a particular thread (or not on a
|
||||||
* database operations take place on a particular thread (or not on a
|
* particular thread) as database operations are generally slow and blocking.
|
||||||
* particular thread) as database operations are generally slow and
|
|
||||||
* blocking.
|
|
||||||
*/
|
*/
|
||||||
public static void setExecutePreCondition (PreCondition condition)
|
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
|
* Creates and initializes a simple repository which will access the database identified by the
|
||||||
* database identified by the supplied database identifier.
|
* supplied database identifier.
|
||||||
*
|
*
|
||||||
* @param provider the connection provider which will be used to
|
* @param provider the connection provider which will be used to obtain our database
|
||||||
* obtain our database connection.
|
* connection.
|
||||||
* @param dbident the identifier of the database that will be accessed by
|
* @param dbident the identifier of the database that will be accessed by this repository or
|
||||||
* this repository or null if the derived class will call {@link
|
* null if the derived class will call {@link #configureDatabaseIdent} by hand later.
|
||||||
* #configureDatabaseIdent} by hand later.
|
|
||||||
*/
|
*/
|
||||||
public SimpleRepository (ConnectionProvider provider, String dbident)
|
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,
|
* This is called automatically if a dbident is provided at construct time, but a derived class
|
||||||
* but a derived class can pass null to its constructor and then call this
|
* can pass null to its constructor and then call this method itself later if it wishes to
|
||||||
* method itself later if it wishes to obtain its database identifier from
|
* obtain its database identifier from an overridable method which could not otherwise be
|
||||||
* an overridable method which could not otherwise be called at construct
|
* called at construct time.
|
||||||
* time.
|
|
||||||
*/
|
*/
|
||||||
protected void configureDatabaseIdent (String dbident)
|
protected void configureDatabaseIdent (String dbident)
|
||||||
{
|
{
|
||||||
_dbident = dbident;
|
_dbident = dbident;
|
||||||
|
|
||||||
// give the repository a chance to do any schema migration before
|
// give the repository a chance to do any schema migration before things get further
|
||||||
// things get further underway
|
// underway
|
||||||
try {
|
try {
|
||||||
executeUpdate(new Operation<Object>() {
|
executeUpdate(new Operation<Object>() {
|
||||||
public Object invoke (Connection conn, DatabaseLiaison liaison)
|
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
|
* Executes the supplied read-only operation. In the event of a transient failure, the
|
||||||
* failure, the repository will attempt to reestablish the database
|
* repository will attempt to reestablish the database connection and try the operation again.
|
||||||
* connection and try the operation again.
|
|
||||||
*
|
*
|
||||||
* @return whatever value is returned by the invoked operation.
|
* @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
|
* Executes the supplied read-write operation. In the event of a transient failure, the
|
||||||
* failure, the repository will attempt to reestablish the database
|
* repository will attempt to reestablish the database connection and try the operation again.
|
||||||
* connection and try the operation again.
|
|
||||||
*
|
*
|
||||||
* @return whatever value is returned by the invoked operation.
|
* @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
|
* Executes the supplied operation followed by a call to <code>commit()</code> on the
|
||||||
* <code>commit()</code> on the connection unless a
|
* connection unless a <code>PersistenceException</code> or runtime error occurs, in which case
|
||||||
* <code>PersistenceException</code> or runtime error occurs, in which
|
* a call to <code>rollback()</code> is executed on the connection.
|
||||||
* case a call to <code>rollback()</code> is executed on the
|
|
||||||
* connection.
|
|
||||||
*
|
*
|
||||||
* @param retryOnTransientFailure if true and the operation fails due to a
|
* @param retryOnTransientFailure if true and the operation fails due to a transient failure
|
||||||
* transient failure (like losing the connection to the database or
|
* (like losing the connection to the database or deadlock detection), the connection to the
|
||||||
* deadlock detection), the connection to the database will be
|
* database will be reestablished (if necessary) and the operation attempted once more.
|
||||||
* reestablished (if necessary) and the operation attempted once more.
|
|
||||||
* @param readOnly whether or not to request a read-only connection.
|
* @param readOnly whether or not to request a read-only connection.
|
||||||
*
|
*
|
||||||
* @return whatever value is returned by the invoked operation.
|
* @return whatever value is returned by the invoked operation.
|
||||||
*/
|
*/
|
||||||
protected <V> V execute (Operation<V> op, boolean retryOnTransientFailure,
|
protected <V> V execute (Operation<V> op, boolean retryOnTransientFailure, boolean readOnly)
|
||||||
boolean readOnly)
|
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
Connection conn = null;
|
Connection conn = null;
|
||||||
@@ -153,16 +142,16 @@ public class SimpleRepository extends Repository
|
|||||||
|
|
||||||
// check our pre-condition
|
// check our pre-condition
|
||||||
if (_precond != null && !_precond.validate(_dbident, op)) {
|
if (_precond != null && !_precond.validate(_dbident, op)) {
|
||||||
Log.warning("Repository operation failed pre-condition check! " +
|
Log.warning("Repository operation failed pre-condition check! [dbident=" + _dbident +
|
||||||
"[dbident=" + _dbident + ", op=" + op + "].");
|
", op=" + op + "].");
|
||||||
Thread.dumpStack();
|
Thread.dumpStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
// obtain our database connection and associated goodies
|
// obtain our database connection and associated goodies
|
||||||
conn = _provider.getConnection(_dbident, readOnly);
|
conn = _provider.getConnection(_dbident, readOnly);
|
||||||
|
|
||||||
// make sure that no one else performs a database operation using the
|
// make sure that no one else performs a database operation using the same connection until
|
||||||
// same connection until we're done
|
// we're done
|
||||||
synchronized (conn) {
|
synchronized (conn) {
|
||||||
try {
|
try {
|
||||||
liaison = LiaisonRegistry.getLiaison(conn);
|
liaison = LiaisonRegistry.getLiaison(conn);
|
||||||
@@ -193,15 +182,15 @@ public class SimpleRepository extends Repository
|
|||||||
|
|
||||||
} catch (SQLException sqe) {
|
} catch (SQLException sqe) {
|
||||||
if (attemptedOperation) {
|
if (attemptedOperation) {
|
||||||
// back out our changes if something got hosed (but not if
|
// back out our changes if something got hosed (but not if the hosage was a
|
||||||
// the hosage was a result of losing our connection)
|
// result of losing our connection)
|
||||||
try {
|
try {
|
||||||
if (supportsTransactions && !conn.isClosed()) {
|
if (supportsTransactions && !conn.isClosed()) {
|
||||||
conn.rollback();
|
conn.rollback();
|
||||||
}
|
}
|
||||||
} catch (SQLException rbe) {
|
} catch (SQLException rbe) {
|
||||||
Log.warning("Unable to roll back operation " +
|
Log.warning("Unable to roll back operation [err=" + sqe +
|
||||||
"[err=" + sqe + ", rberr=" + rbe + "].");
|
", rberr=" + rbe + "].");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,13 +207,11 @@ public class SimpleRepository extends Repository
|
|||||||
throw new PersistenceException(err, sqe);
|
throw new PersistenceException(err, sqe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the MySQL JDBC driver has the annoying habit of including
|
// the MySQL JDBC driver has the annoying habit of including the embedded exception
|
||||||
// the embedded exception stack trace in the message of their
|
// stack trace in the message of their outer exception; if I want a fucking stack
|
||||||
// outer exception; if I want a fucking stack trace, I'll call
|
// trace, I'll call printStackTrace() thanksverymuch
|
||||||
// printStackTrace() thanksverymuch
|
|
||||||
String msg = StringUtil.split("" + sqe, "\n")[0];
|
String msg = StringUtil.split("" + sqe, "\n")[0];
|
||||||
Log.info("Transient failure executing operation, " +
|
Log.info("Transient failure executing operation, retrying [error=" + msg + "].");
|
||||||
"retrying [error=" + msg + "].");
|
|
||||||
|
|
||||||
} catch (PersistenceException pe) {
|
} catch (PersistenceException pe) {
|
||||||
// back out our changes if something got hosed
|
// back out our changes if something got hosed
|
||||||
@@ -233,8 +220,8 @@ public class SimpleRepository extends Repository
|
|||||||
conn.rollback();
|
conn.rollback();
|
||||||
}
|
}
|
||||||
} catch (SQLException rbe) {
|
} catch (SQLException rbe) {
|
||||||
Log.warning("Unable to roll back operation " +
|
Log.warning("Unable to roll back operation [origerr=" + pe +
|
||||||
"[origerr=" + pe + ", rberr=" + rbe + "].");
|
", rberr=" + rbe + "].");
|
||||||
}
|
}
|
||||||
throw pe;
|
throw pe;
|
||||||
|
|
||||||
@@ -246,8 +233,8 @@ public class SimpleRepository extends Repository
|
|||||||
conn.rollback();
|
conn.rollback();
|
||||||
}
|
}
|
||||||
} catch (SQLException rbe) {
|
} catch (SQLException rbe) {
|
||||||
Log.warning("Unable to roll back operation " +
|
Log.warning("Unable to roll back operation [origerr=" + rte +
|
||||||
"[origerr=" + rte + ", rberr=" + rbe + "].");
|
", rberr=" + rbe + "].");
|
||||||
}
|
}
|
||||||
throw rte;
|
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
|
// we'll only fall through here if the above code failed due to a transient exception (the
|
||||||
// transient exception (the connection was closed for being idle, for
|
// connection was closed for being idle, for example) and we've been asked to retry; so
|
||||||
// example) and we've been asked to retry; so let's do so
|
// let's do so
|
||||||
return execute(op, false, readOnly);
|
return execute(op, false, readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the supplied update query in this repository, returning
|
* Executes the supplied update query in this repository, returning the number of rows
|
||||||
* the number of rows modified.
|
* modified.
|
||||||
*/
|
*/
|
||||||
protected int update (final String query)
|
protected int update (final String query)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
@@ -288,9 +275,8 @@ public class SimpleRepository extends Repository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the supplied update query in this repository, throwing an
|
* Executes the supplied update query in this repository, throwing an exception if the
|
||||||
* exception if the modification count is not equal to the specified
|
* modification count is not equal to the specified count.
|
||||||
* count.
|
|
||||||
*/
|
*/
|
||||||
protected void checkedUpdate (final String query, final int count)
|
protected void checkedUpdate (final String query, final int count)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
@@ -312,8 +298,8 @@ public class SimpleRepository extends Repository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the supplied update query in this repository, logging a warning
|
* Executes the supplied update query in this repository, logging a warning if the modification
|
||||||
* if the modification count is not equal to the specified count.
|
* count is not equal to the specified count.
|
||||||
*/
|
*/
|
||||||
protected void warnedUpdate (final String query, final int count)
|
protected void warnedUpdate (final String query, final int count)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
@@ -335,15 +321,12 @@ public class SimpleRepository extends Repository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instructs MySQL to perform table maintenance on the specified
|
* Instructs MySQL to perform table maintenance on the specified table.
|
||||||
* table.
|
|
||||||
*
|
*
|
||||||
* @param action <code>analyze</code> recomputes the distribution of
|
* @param action <code>analyze</code> recomputes the distribution of the keys for the specified
|
||||||
* the keys for the specified table. This can help certain joins to be
|
* table. This can help certain joins to be performed more efficiently. <code>optimize</code>
|
||||||
* performed more efficiently. <code>optimize</code> instructs MySQL
|
* instructs MySQL to coalesce fragmented records and reclaim space left by deleted records.
|
||||||
* to coalesce fragmented records and reclaim space left by deleted
|
* This can improve a tables efficiency but can take a long time to run on large tables.
|
||||||
* 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)
|
protected void maintenance (final String action, final String table)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
@@ -355,15 +338,12 @@ public class SimpleRepository extends Repository
|
|||||||
Statement stmt = null;
|
Statement stmt = null;
|
||||||
try {
|
try {
|
||||||
stmt = conn.createStatement();
|
stmt = conn.createStatement();
|
||||||
ResultSet rs = stmt.executeQuery(
|
ResultSet rs = stmt.executeQuery(action + " table " + table);
|
||||||
action + " table " + table);
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
String result = rs.getString("Msg_text");
|
String result = rs.getString("Msg_text");
|
||||||
if (result == null ||
|
if (result == null ||
|
||||||
result.indexOf("up to date") == -1 &&
|
(result.indexOf("up to date") == -1 && !result.equals("OK"))) {
|
||||||
!result.equals("OK")) {
|
Log.info("Table maintenance [" + SimpleRepository.toString(rs) + "].");
|
||||||
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
|
* Derived classes can override this method and perform any schema migration they might need
|
||||||
* migration they might need (using the idempotent {@link JDBCUtil} schema
|
* (using the idempotent {@link JDBCUtil} schema migration methods). This is called during the
|
||||||
* migration methods). This is called during the repository's constructor
|
* repository's constructor and will thus take place before derived classes (like the {@link
|
||||||
* and will thus take place before derived classes (like the {@link
|
* JORARepository} introspect on the schema to match it up to associated Java classes).
|
||||||
* JORARepository} introspect on the schema to match it up to associated
|
|
||||||
* Java classes).
|
|
||||||
*/
|
*/
|
||||||
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
|
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
@@ -389,17 +367,17 @@ public class SimpleRepository extends Repository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when we fetch a connection from the provider. This gives
|
* Called when we fetch a connection from the provider. This gives derived classes an
|
||||||
* derived classes an opportunity to configure whatever internals they
|
* opportunity to configure whatever internals they might be using with the connection that was
|
||||||
* might be using with the connection that was fetched.
|
* fetched.
|
||||||
*/
|
*/
|
||||||
protected void gotConnection (Connection conn)
|
protected void gotConnection (Connection conn)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a row of a result set to a string, prepending each column
|
* Converts a row of a result set to a string, prepending each column with the column name from
|
||||||
* with the column name from the result set metadata.
|
* the result set metadata.
|
||||||
*/
|
*/
|
||||||
protected static String toString (ResultSet rs)
|
protected static String toString (ResultSet rs)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
@@ -418,5 +396,6 @@ public class SimpleRepository extends Repository
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String _dbident;
|
protected String _dbident;
|
||||||
|
|
||||||
protected static PreCondition _precond;
|
protected static PreCondition _precond;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// samskivert library - useful routines for java programs
|
// samskivert library - useful routines for java programs
|
||||||
// Copyright (C) 2001-2007 Michael Bayne
|
// Copyright (C) 2001-2007 Michael Bayne
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or modify it
|
// 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
|
// 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
|
// 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;
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The static connection provider generates JDBC connections based on
|
* The static connection provider generates JDBC connections based on configuration information
|
||||||
* configuration information provided via a properties file. It does no
|
* provided via a properties file. It does no connection pooling and always returns the same
|
||||||
* connection pooling and always returns the same connection for a
|
* connection for a particular identifier (unless that connection need be closed because of a
|
||||||
* particular identifier (unless that connection need be closed because of
|
* connection failure, in which case it opens a new one the next time the connection is requested).
|
||||||
* 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
|
* <p> The configuration properties file should contain the following information:
|
||||||
* information:
|
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* IDENT.driver=[jdbc driver class]
|
* IDENT.driver=[jdbc driver class]
|
||||||
@@ -50,14 +47,13 @@ import com.samskivert.util.StringUtil;
|
|||||||
* [...]
|
* [...]
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* Where <code>IDENT</code> is the database identifier for a particular
|
* Where <code>IDENT</code> is the database identifier for a particular database connection. When a
|
||||||
* database connection. When a particular database identifier is
|
* particular database identifier is requested, the configuration information will be fetched from
|
||||||
* requested, the configuration information will be fetched from the
|
* the properties.
|
||||||
* properties.
|
|
||||||
*
|
*
|
||||||
* <p> Additionally, a default set of properties can be provided using the
|
* <p> Additionally, a default set of properties can be provided using the identifier
|
||||||
* identifier <code>default</code>. Values not provided for a specific
|
* <code>default</code>. Values not provided for a specific identifier will be sought from the
|
||||||
* identifier will be sought from the defaults. For example:
|
* defaults. For example:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* default.driver=[jdbc driver class]
|
* default.driver=[jdbc driver class]
|
||||||
@@ -75,15 +71,15 @@ import com.samskivert.util.StringUtil;
|
|||||||
public class StaticConnectionProvider implements ConnectionProvider
|
public class StaticConnectionProvider implements ConnectionProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Constructs a static connection provider which will load its
|
* Constructs a static connection provider which will load its configuration from a properties
|
||||||
* configuration from a properties file accessible via the classpath
|
* file accessible via the classpath of the running application and identified by the specified
|
||||||
* of the running application and identified by the specified path.
|
* path.
|
||||||
*
|
*
|
||||||
* @param propPath the path (relative to the classpath) to the
|
* @param propPath the path (relative to the classpath) to the properties file that will be
|
||||||
* properties file that will be used for configuration information.
|
* used for configuration information.
|
||||||
*
|
*
|
||||||
* @exception IOException thrown if an error occurs locating or
|
* @exception IOException thrown if an error occurs locating or loading the specified
|
||||||
* loading the specified properties file.
|
* properties file.
|
||||||
*/
|
*/
|
||||||
public StaticConnectionProvider (String propPath)
|
public StaticConnectionProvider (String propPath)
|
||||||
throws IOException
|
throws IOException
|
||||||
@@ -92,8 +88,8 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a static connection provider which will fetch its
|
* Constructs a static connection provider which will fetch its configuration information from
|
||||||
* configuration information from the specified properties object.
|
* the specified properties object.
|
||||||
*
|
*
|
||||||
* @param props the configuration for this connection provider.
|
* @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
|
* Closes all of the open database connections in preparation for shutting down.
|
||||||
* shutting down.
|
|
||||||
*/
|
*/
|
||||||
public void shutdown ()
|
public void shutdown ()
|
||||||
{
|
{
|
||||||
@@ -112,12 +107,11 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
Iterator iter = _keys.keySet().iterator();
|
Iterator iter = _keys.keySet().iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
String key = (String)iter.next();
|
String key = (String)iter.next();
|
||||||
Mapping conmap = (Mapping)_keys.get(key);
|
Mapping conmap = _keys.get(key);
|
||||||
try {
|
try {
|
||||||
conmap.connection.close();
|
conmap.connection.close();
|
||||||
} catch (SQLException sqe) {
|
} catch (SQLException sqe) {
|
||||||
Log.warning("Error shutting down connection " +
|
Log.warning("Error shutting down connection [key=" + key + ", err=" + sqe + "].");
|
||||||
"[key=" + key + ", err=" + sqe + "].");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +120,13 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
_idents.clear();
|
_idents.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from ConnectionProvider
|
||||||
|
public String getURL (String ident)
|
||||||
|
{
|
||||||
|
Properties props = PropertiesUtil.getSubProperties(_props, ident, DEFAULTS_KEY);
|
||||||
|
return props.getProperty("url");
|
||||||
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public Connection getConnection (String ident, boolean readOnly)
|
public Connection getConnection (String ident, boolean readOnly)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
@@ -135,9 +136,7 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
|
|
||||||
// open the connection if we haven't already
|
// open the connection if we haven't already
|
||||||
if (conmap == null) {
|
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
|
// get the JDBC configuration info
|
||||||
String err = "No driver class specified [ident=" + ident + "].";
|
String err = "No driver class specified [ident=" + ident + "].";
|
||||||
@@ -149,33 +148,30 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
err = "No driver password specified [ident=" + ident + "].";
|
err = "No driver password specified [ident=" + ident + "].";
|
||||||
String password = requireProp(props, "password", err);
|
String password = requireProp(props, "password", err);
|
||||||
|
|
||||||
// if this is a read-only connection,
|
// 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
|
||||||
// we cache connections by username+url+readOnly to avoid making
|
|
||||||
// more that one connection to a particular database server
|
|
||||||
String key = username + "@" + url + ":" + readOnly;
|
String key = username + "@" + url + ":" + readOnly;
|
||||||
conmap = _keys.get(key);
|
conmap = _keys.get(key);
|
||||||
if (conmap == null) {
|
if (conmap == null) {
|
||||||
Log.debug("Creating " + key + " for " + ident + ".");
|
Log.debug("Creating " + key + " for " + ident + ".");
|
||||||
conmap = new Mapping();
|
conmap = new Mapping();
|
||||||
conmap.key = key;
|
conmap.key = key;
|
||||||
conmap.connection =
|
conmap.connection = openConnection(driver, url, username, password);
|
||||||
openConnection(driver, url, username, password);
|
|
||||||
|
|
||||||
// make the connection read-only to let the JDBC driver know
|
// make the connection read-only to let the JDBC driver know that it can and should
|
||||||
// that it can and should use the read-only mirror(s)
|
// use the read-only mirror(s)
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
try {
|
try {
|
||||||
conmap.connection.setReadOnly(true);
|
conmap.connection.setReadOnly(true);
|
||||||
} catch (SQLException sqe) {
|
} catch (SQLException sqe) {
|
||||||
closeConnection(ident, conmap.connection);
|
closeConnection(ident, conmap.connection);
|
||||||
err = "Failed to make connection read-only " +
|
err = "Failed to make connection read-only [key=" + key +
|
||||||
"[key=" + key + ", ident=" + ident + "].";
|
", ident=" + ident + "].";
|
||||||
throw new PersistenceException(err, sqe);
|
throw new PersistenceException(err, sqe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_keys.put(key, conmap);
|
_keys.put(key, conmap);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Log.debug("Reusing " + key + " for " + ident + ".");
|
Log.debug("Reusing " + key + " for " + ident + ".");
|
||||||
}
|
}
|
||||||
@@ -185,19 +181,25 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
_idents.put(mapkey, conmap);
|
_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;
|
return conmap.connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void releaseConnection (String ident, boolean readOnly,
|
public void releaseConnection (String ident, boolean readOnly, Connection conn)
|
||||||
Connection conn)
|
|
||||||
{
|
{
|
||||||
// nothing to do here, all is well
|
// nothing to do here, all is well
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentation inherited
|
// documentation inherited
|
||||||
public void connectionFailed (String ident, boolean readOnly,
|
public void connectionFailed (
|
||||||
Connection conn, SQLException error)
|
String ident, boolean readOnly, Connection conn, SQLException error)
|
||||||
{
|
{
|
||||||
String mapkey = ident + ":" + readOnly;
|
String mapkey = ident + ":" + readOnly;
|
||||||
Mapping conmap = _idents.get(mapkey);
|
Mapping conmap = _idents.get(mapkey);
|
||||||
@@ -216,8 +218,7 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
_keys.remove(conmap.key);
|
_keys.remove(conmap.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Connection openConnection (
|
protected Connection openConnection (String driver, String url, String username, String passwd)
|
||||||
String driver, String url, String username, String password)
|
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
// create an instance of the driver
|
// create an instance of the driver
|
||||||
@@ -233,11 +234,11 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
try {
|
try {
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.put("user", username);
|
props.put("user", username);
|
||||||
props.put("password", password);
|
props.put("password", passwd);
|
||||||
return jdriver.connect(url, props);
|
return jdriver.connect(url, props);
|
||||||
|
|
||||||
} catch (SQLException sqe) {
|
} catch (SQLException sqe) {
|
||||||
String err = "Error creating database connection " +
|
String err = "Error creating database connection [driver=" + driver + ", url=" + url +
|
||||||
"[driver=" + driver + ", url=" + url +
|
|
||||||
", username=" + username + "].";
|
", username=" + username + "].";
|
||||||
throw new PersistenceException(err, sqe);
|
throw new PersistenceException(err, sqe);
|
||||||
}
|
}
|
||||||
@@ -253,25 +254,23 @@ public class StaticConnectionProvider implements ConnectionProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static String requireProp (
|
protected static String requireProp (Properties props, String name, String errmsg)
|
||||||
Properties props, String name, String errmsg)
|
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
String value = props.getProperty(name);
|
String value = props.getProperty(name);
|
||||||
if (StringUtil.isBlank(value)) {
|
if (StringUtil.isBlank(value)) {
|
||||||
// augment the error message
|
errmsg = "Unable to get connection. " + errmsg; // augment the error message
|
||||||
errmsg = "Unable to get connection. " + errmsg;
|
|
||||||
throw new PersistenceException(errmsg);
|
throw new PersistenceException(errmsg);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Contains information on a particular connection to which any
|
/** Contains information on a particular connection to which any number of database identifiers
|
||||||
* number of database identifiers can be mapped. */
|
* can be mapped. */
|
||||||
protected static class Mapping
|
protected static class Mapping
|
||||||
{
|
{
|
||||||
/** The combination of username and JDBC url that uniquely
|
/** The combination of username and JDBC url that uniquely identifies our database
|
||||||
* identifies our database connection. */
|
* connection. */
|
||||||
public String key;
|
public String key;
|
||||||
|
|
||||||
/** The connection itself. */
|
/** The connection itself. */
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// samskivert library - useful routines for java programs
|
// samskivert library - useful routines for java programs
|
||||||
// Copyright (C) 2001-2007 Michael Bayne
|
// Copyright (C) 2001-2007 Michael Bayne
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or modify it
|
// 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
|
// 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
|
// 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;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to note that transitionary code has been run to migrate persistent
|
* Used to note that transitionary code has been run to migrate persistent data. This is especially
|
||||||
* data. The TransitionRepository is especially useful for data that
|
* useful for data that one cannot examine to determine if it's been transitioned.
|
||||||
* one cannot examine to determine if it's been transitioned.
|
|
||||||
*/
|
*/
|
||||||
public class TransitionRepository extends SimpleRepository
|
public class TransitionRepository extends SimpleRepository
|
||||||
{
|
{
|
||||||
@@ -81,18 +80,20 @@ public class TransitionRepository extends SimpleRepository
|
|||||||
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
|
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
Object found = null;
|
|
||||||
PreparedStatement stmt = null;
|
PreparedStatement stmt = null;
|
||||||
try {
|
try {
|
||||||
stmt = conn.prepareStatement("select NAME " +
|
stmt = conn.prepareStatement(
|
||||||
"from TRANSITIONS where CLASS=? and NAME=?");
|
" select " + liaison.columnSQL("NAME") +
|
||||||
|
" from " + liaison.tableSQL("TRANSITIONS") +
|
||||||
|
" where " + liaison.columnSQL("CLASS") + "=?" +
|
||||||
|
" and " + liaison.columnSQL("NAME") + "=?");
|
||||||
stmt.setString(1, cname);
|
stmt.setString(1, cname);
|
||||||
stmt.setString(2, name);
|
stmt.setString(2, name);
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
while (rs.next()); // is this really necessary?
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
}
|
}
|
||||||
@@ -114,12 +115,14 @@ public class TransitionRepository extends SimpleRepository
|
|||||||
{
|
{
|
||||||
PreparedStatement stmt = null;
|
PreparedStatement stmt = null;
|
||||||
try {
|
try {
|
||||||
stmt = conn.prepareStatement("insert into TRANSITIONS " +
|
stmt = conn.prepareStatement(
|
||||||
"(CLASS, NAME) values (?, ?)");
|
"insert into " + liaison.tableSQL("TRANSITIONS") + " (" +
|
||||||
|
liaison.columnSQL("CLASS") + ", " + liaison.columnSQL("NAME") +
|
||||||
|
") values (?, ?)");
|
||||||
stmt.setString(1, cname);
|
stmt.setString(1, cname);
|
||||||
stmt.setString(2, name);
|
stmt.setString(2, name);
|
||||||
|
|
||||||
JDBCUtil.checkedUpdate(stmt, 1);
|
JDBCUtil.checkedUpdate(stmt, 1);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
JDBCUtil.close(stmt);
|
JDBCUtil.close(stmt);
|
||||||
}
|
}
|
||||||
@@ -141,8 +144,10 @@ public class TransitionRepository extends SimpleRepository
|
|||||||
{
|
{
|
||||||
PreparedStatement stmt = null;
|
PreparedStatement stmt = null;
|
||||||
try {
|
try {
|
||||||
stmt = conn.prepareStatement("delete from TRANSITIONS " +
|
stmt = conn.prepareStatement(
|
||||||
"where CLASS=? and NAME=?");
|
" delete from " + liaison.tableSQL("TRANSITIONS") +
|
||||||
|
" where " + liaison.columnSQL("CLASS") + "=? " +
|
||||||
|
" and " + liaison.columnSQL("NAME") + "=?");
|
||||||
stmt.setString(1, cname);
|
stmt.setString(1, cname);
|
||||||
stmt.setString(2, name);
|
stmt.setString(2, name);
|
||||||
|
|
||||||
@@ -160,10 +165,12 @@ public class TransitionRepository extends SimpleRepository
|
|||||||
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
|
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
|
||||||
throws SQLException, PersistenceException
|
throws SQLException, PersistenceException
|
||||||
{
|
{
|
||||||
JDBCUtil.createTableIfMissing(conn, "TRANSITIONS", new String[] {
|
liaison.createTableIfMissing(
|
||||||
"CLASS varchar(200) not null",
|
conn,
|
||||||
"NAME varchar(50) not null",
|
"TRANSITIONS",
|
||||||
"APPLIED timestamp not null",
|
new String[] { "CLASS", "NAME", "APPLIED" },
|
||||||
"primary key (CLASS, NAME)" }, "");
|
new String[] { "VARCHAR(200)", "VARCHAR(50)", "TIMESTAMP NOT NULL" },
|
||||||
|
null,
|
||||||
|
new String[] { "CLASS", "NAME" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user