Out with arrays, in with collections.

This commit is contained in:
Michael Bayne
2013-05-06 11:07:21 -07:00
parent 678c990270
commit bd7e747eeb
7 changed files with 172 additions and 195 deletions
@@ -9,6 +9,7 @@ import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.List;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
@@ -26,7 +27,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
super(); super();
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean tableExists (Connection conn, String name) throws SQLException public boolean tableExists (Connection conn, String name) throws SQLException
{ {
ResultSet rs = conn.getMetaData().getTables(null, null, name, null); ResultSet rs = conn.getMetaData().getTables(null, null, name, null);
@@ -39,7 +40,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
return false; return false;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean tableContainsColumn (Connection conn, String table, String column) public boolean tableContainsColumn (Connection conn, String table, String column)
throws SQLException throws SQLException
{ {
@@ -54,7 +55,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
return false; return false;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean tableContainsIndex (Connection conn, String table, String index) public boolean tableContainsIndex (Connection conn, String table, String index)
throws SQLException throws SQLException
{ {
@@ -69,15 +70,15 @@ public abstract class BaseLiaison implements DatabaseLiaison
return false; return false;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean addIndexToTable ( public boolean addIndexToTable (Connection conn, String table, List<String> columns,
Connection conn, String table, String[] columns, String ixName, boolean unique) String ixName, boolean unique) throws SQLException
throws SQLException
{ {
if (tableContainsIndex(conn, table, ixName)) { if (tableContainsIndex(conn, table, ixName)) {
return false; return false;
} }
ixName = (ixName != null ? ixName : StringUtil.join(columns, "_")); ixName = (ixName != null ? ixName :
StringUtil.join(columns.toArray(new String[columns.size()]), "_"));
StringBuilder update = new StringBuilder("CREATE "); StringBuilder update = new StringBuilder("CREATE ");
if (unique) { if (unique) {
@@ -85,12 +86,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
} }
update.append("INDEX ").append(indexSQL(ixName)).append(" ON "). update.append("INDEX ").append(indexSQL(ixName)).append(" ON ").
append(tableSQL(table)).append(" ("); append(tableSQL(table)).append(" (");
for (int ii = 0; ii < columns.length; ii ++) { appendColumns(columns, update);
if (ii > 0) {
update.append(", ");
}
update.append(columnSQL(columns[ii]));
}
update.append(")"); update.append(")");
executeQuery(conn, update.toString()); executeQuery(conn, update.toString());
@@ -98,17 +94,12 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void addPrimaryKey (Connection conn, String table, String[] columns) public void addPrimaryKey (Connection conn, String table, List<String> columns)
throws SQLException throws SQLException
{ {
StringBuilder fields = new StringBuilder("("); StringBuilder fields = new StringBuilder("(");
for (int ii = 0; ii < columns.length; ii ++) { appendColumns(columns, fields);
if (ii > 0) {
fields.append(", ");
}
fields.append(columnSQL(columns[ii]));
}
fields.append(")"); fields.append(")");
String update = "ALTER TABLE " + tableSQL(table) + " ADD PRIMARY KEY " + fields.toString(); String update = "ALTER TABLE " + tableSQL(table) + " ADD PRIMARY KEY " + fields.toString();
@@ -116,24 +107,22 @@ public abstract class BaseLiaison implements DatabaseLiaison
log.info("Primary key " + fields + " added to table '" + table + "'"); log.info("Primary key " + fields + " added to table '" + table + "'");
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void dropIndex (Connection conn, String table, String index) throws SQLException public void dropIndex (Connection conn, String table, String index) throws SQLException
{ {
executeQuery(conn, "DROP INDEX " + columnSQL(index)); executeQuery(conn, "DROP INDEX " + columnSQL(index));
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void dropPrimaryKey (Connection conn, String table, String pkName) public void dropPrimaryKey (Connection conn, String table, String pkName) throws SQLException
throws SQLException
{ {
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + executeQuery(conn, "ALTER TABLE " + tableSQL(table) +
" DROP CONSTRAINT " + columnSQL(pkName)); " DROP CONSTRAINT " + columnSQL(pkName));
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean addColumn ( public boolean addColumn (Connection conn, String table, String column, String definition,
Connection conn, String table, String column, String definition, boolean check) boolean check) throws SQLException
throws SQLException
{ {
if (check && tableContainsColumn(conn, table, column)) { if (check && tableContainsColumn(conn, table, column)) {
return false; return false;
@@ -145,7 +134,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean addColumn (Connection conn, String table, String column, public boolean addColumn (Connection conn, String table, String column,
ColumnDefinition newColumnDef, boolean check) ColumnDefinition newColumnDef, boolean check)
throws SQLException throws SQLException
@@ -160,14 +149,13 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean changeColumn (Connection conn, String table, String column, String type, public boolean changeColumn (Connection conn, String table, String column, String type,
Boolean nullable, Boolean unique, String defaultValue) Boolean nullable, Boolean unique, String defaultValue)
throws SQLException throws SQLException
{ {
String defStr = expandDefinition( String defStr = expandDefinition(type, nullable != null ? nullable : false,
type, nullable != null ? nullable : false, unique != null ? unique : false, unique != null ? unique : false, defaultValue);
defaultValue);
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " CHANGE " + executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " CHANGE " +
columnSQL(column) + " " + columnSQL(column) + " " + defStr); columnSQL(column) + " " + columnSQL(column) + " " + defStr);
@@ -176,10 +164,9 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean renameColumn (Connection conn, String table, String from, String to, public boolean renameColumn (Connection conn, String table, String from, String to,
ColumnDefinition newColumnDef) ColumnDefinition newColumnDef) throws SQLException
throws SQLException
{ {
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " RENAME COLUMN " + executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " RENAME COLUMN " +
columnSQL(from) + " TO " + columnSQL(to)); columnSQL(from) + " TO " + columnSQL(to));
@@ -187,7 +174,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean dropColumn (Connection conn, String table, String column) throws SQLException public boolean dropColumn (Connection conn, String table, String column) throws SQLException
{ {
if (!tableContainsColumn(conn, table, column)) { if (!tableContainsColumn(conn, table, column)) {
@@ -198,50 +185,39 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean createTableIfMissing ( public boolean createTableIfMissing (Connection conn, String table, List<String> columns,
Connection conn, String table, String[] columns, ColumnDefinition[] definitions, List<ColumnDefinition> definitions,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns) List<List<String>> uniqueConstraintColumns,
List<String> primaryKeyColumns)
throws SQLException throws SQLException
{ {
if (tableExists(conn, table)) { if (tableExists(conn, table)) {
return false; return false;
} }
if (columns.length != definitions.length) { if (columns.size() != definitions.size()) {
throw new IllegalArgumentException("Column name and definition number mismatch"); throw new IllegalArgumentException("Column name and definition number mismatch");
} }
StringBuilder builder = StringBuilder builder = new StringBuilder("CREATE TABLE ").
new StringBuilder("CREATE TABLE ").append(tableSQL(table)).append(" ("); append(tableSQL(table)).append(" (");
for (int ii = 0; ii < columns.length; ii ++) { for (int ii = 0; ii < columns.size(); ii ++) {
if (ii > 0) { if (ii > 0) {
builder.append(", "); builder.append(", ");
} }
builder.append(columnSQL(columns[ii])).append(" "); builder.append(columnSQL(columns.get(ii))).append(" ");
builder.append(expandDefinition(definitions[ii])); builder.append(expandDefinition(definitions.get(ii)));
} }
if (uniqueConstraintColumns != null && uniqueConstraintColumns.length > 0) { for (List<String> uCols : uniqueConstraintColumns) {
for (String[] uCols : uniqueConstraintColumns) { builder.append(", UNIQUE (");
builder.append(", UNIQUE ("); appendColumns(uCols, builder);
for (int ii = 0; ii < uCols.length; ii ++) { builder.append(")");
if (ii > 0) {
builder.append(", ");
}
builder.append(columnSQL(uCols[ii]));
}
builder.append(")");
}
} }
if (primaryKeyColumns != null && primaryKeyColumns.length > 0) { if (!primaryKeyColumns.isEmpty()) {
builder.append(", PRIMARY KEY ("); builder.append(", PRIMARY KEY (");
for (int ii = 0; ii < primaryKeyColumns.length; ii ++) { appendColumns(primaryKeyColumns, builder);
if (ii > 0) {
builder.append(", ");
}
builder.append(columnSQL(primaryKeyColumns[ii]));
}
builder.append(")"); builder.append(")");
} }
@@ -252,8 +228,7 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
public boolean dropTable (Connection conn, String name) public boolean dropTable (Connection conn, String name) throws SQLException
throws SQLException
{ {
if (!tableExists(conn, name)) { if (!tableExists(conn, name)) {
return false; return false;
@@ -264,17 +239,16 @@ public abstract class BaseLiaison implements DatabaseLiaison
} }
/** /**
* Create an SQL string that summarizes a column definition in that format generally * Create an SQL string that summarizes a column definition in that format generally accepted
* accepted in table creation and column addition statements, e.g. * in table creation and column addition statements, e.g. {@code INTEGER UNIQUE NOT NULL
* INTEGER UNIQUE NOT NULL DEFAULT 100 * DEFAULT 100}.
*/ */
public String expandDefinition (ColumnDefinition def) public String expandDefinition (ColumnDefinition def)
{ {
return expandDefinition(def.type, def.nullable, def.unique, def.defaultValue); return expandDefinition(def.type, def.nullable, def.unique, def.defaultValue);
} }
protected int executeQuery (Connection conn, String query) protected int executeQuery (Connection conn, String query) throws SQLException
throws SQLException
{ {
Statement stmt = conn.createStatement(); Statement stmt = conn.createStatement();
try { try {
@@ -284,23 +258,31 @@ public abstract class BaseLiaison implements DatabaseLiaison
} }
} }
protected String expandDefinition ( protected String expandDefinition (String type, boolean nullable, boolean unique,
String type, boolean nullable, boolean unique, String defaultValue) String defaultValue)
{ {
StringBuilder builder = new StringBuilder(type); StringBuilder builder = new StringBuilder(type);
if (!nullable) { if (!nullable) {
builder.append(" NOT NULL"); builder.append(" NOT NULL");
} }
if (unique) { if (unique) {
builder.append(" UNIQUE"); builder.append(" UNIQUE");
} }
// append the default value if one was specified // append the default value if one was specified
if (defaultValue != null) { if (defaultValue != null) {
builder.append(" DEFAULT ").append(defaultValue); builder.append(" DEFAULT ").append(defaultValue);
} }
return builder.toString(); return builder.toString();
} }
/** Escapes {@code columns} with {@link #columnSQL}, appends (comma-sepped) to {@code buf}. */
protected void appendColumns (Iterable<String> columns, StringBuilder buf) {
int ii = 0;
for (String column : columns) {
if (ii++ > 0) {
buf.append(", ");
}
buf.append(columnSQL(column));
}
}
} }
@@ -7,6 +7,7 @@ package com.samskivert.jdbc;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
/** /**
* Despite good intentions, JDBC and SQL do not provide a unified interface to all databases. There * Despite good intentions, JDBC and SQL do not provide a unified interface to all databases. There
@@ -56,7 +57,7 @@ public interface DatabaseLiaison
* should have no negative effect like resetting it). * should have no negative effect like resetting it).
*/ */
public void createGenerator (Connection conn, String tableName, String columnName, public void createGenerator (Connection conn, String tableName, String columnName,
int initialValue) int initialValue)
throws SQLException; throws SQLException;
/** /**
@@ -76,8 +77,8 @@ public interface DatabaseLiaison
* Adds a named index to a table on the given columns. Returns true or false if the database * 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. * did or did not report a schema modification.
*/ */
public boolean addIndexToTable ( public boolean addIndexToTable (Connection conn, String table, List<String> columns,
Connection conn, String table, String[] columns, String index, boolean unique) String index, boolean unique)
throws SQLException; throws SQLException;
/** /**
@@ -90,7 +91,7 @@ public interface DatabaseLiaison
* Adds a primary key to a table of the given name and on the given columns. Returns true or * Adds a primary key to a table of the given name and on the given columns. Returns true or
* false if the database did nor did not report a schema modification. * false if the database did nor did not report a schema modification.
*/ */
public void addPrimaryKey (Connection conn, String table, String[] columns) public void addPrimaryKey (Connection conn, String table, List<String> columns)
throws SQLException; throws SQLException;
/** /**
@@ -103,16 +104,16 @@ public interface DatabaseLiaison
* Adds a column to a table with the given definition. Tests for the previous existence of * Adds a column to a table with the given definition. Tests for the previous existence of
* the column iff 'check' is true. * the column iff 'check' is true.
*/ */
public boolean addColumn ( public boolean addColumn (Connection conn, String table, String column, String definition,
Connection conn, String table, String column, String definition, boolean check) boolean check)
throws SQLException; throws SQLException;
/** /**
* Adds a column to a table with the given definition. Tests for the previous existence of * Adds a column to a table with the given definition. Tests for the previous existence of
* the column iff 'check' is true. * the column iff 'check' is true.
*/ */
public boolean addColumn ( public boolean addColumn (Connection conn, String table, String column,
Connection conn, String table, String column, ColumnDefinition columnDef, boolean check) ColumnDefinition columnDef, boolean check)
throws SQLException; throws SQLException;
/** /**
@@ -121,9 +122,8 @@ public interface DatabaseLiaison
* unique and defaultValue arguments may be null, in which case the implementation will attempt * unique and defaultValue arguments may be null, in which case the implementation will attempt
* to leave that aspect of the column unchanged. * to leave that aspect of the column unchanged.
*/ */
public boolean changeColumn ( public boolean changeColumn (Connection conn, String table, String column, String type,
Connection conn, String table, String column, String type, Boolean nullable, Boolean nullable, Boolean unique, String defaultValue)
Boolean unique, String defaultValue)
throws SQLException; throws SQLException;
/** /**
@@ -133,9 +133,8 @@ public interface DatabaseLiaison
* @param columnDef the full definition of the new column, including its new name (MySQL * @param columnDef the full definition of the new column, including its new name (MySQL
* requires this for a column rename). * requires this for a column rename).
*/ */
public boolean renameColumn ( public boolean renameColumn (Connection conn, String table, String oldColumn, String newColumn,
Connection conn, String table, String oldColumn, String newColumn, ColumnDefinition columnDef)
ColumnDefinition columnDef)
throws SQLException; throws SQLException;
/** /**
@@ -143,15 +142,16 @@ public interface DatabaseLiaison
* the given set of unique constraints (or null) and the given primary key columns (or null). * 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. * Returns true if the table was successfully created, false if it already existed.
*/ */
public boolean createTableIfMissing ( public boolean createTableIfMissing (Connection conn, String table, List<String> columns,
Connection conn, String table, String[] columns, ColumnDefinition[] declarations, List<ColumnDefinition> declarations,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns) List<List<String>> uniqueConstraintColumns,
List<String> primaryKeyColumns)
throws SQLException; throws SQLException;
/** /**
* Create an SQL string that summarizes a column definition in that format generally * Create an SQL string that summarizes a column definition in that format generally accepted
* accepted in table creation and column addition statements, e.g. * in table creation and column addition statements, e.g. {@code INTEGER UNIQUE NOT NULL
* INTEGER UNIQUE NOT NULL DEFAULT 100 * DEFAULT 100}
*/ */
public String expandDefinition (ColumnDefinition coldef); public String expandDefinition (ColumnDefinition coldef);
@@ -173,13 +173,15 @@ public interface DatabaseLiaison
* Returns true if the table with the specified name exists, false if it does not. * Returns true if the table with the specified name exists, false if it does not.
* <em>Note:</em> the table name is case sensitive. * <em>Note:</em> the table name is case sensitive.
*/ */
public boolean tableExists (Connection conn, String name) throws SQLException; public boolean tableExists (Connection conn, String name)
throws SQLException;
/** /**
* Drops the given table and returns true if the table exists, else returns false. * Drops the given table and returns true if the table exists, else returns false.
* <em>Note:</em> the table name is case sensitive. * <em>Note:</em> the table name is case sensitive.
*/ */
public boolean dropTable (Connection conn, String name) throws SQLException; public boolean dropTable (Connection conn, String name)
throws SQLException;
/** /**
* Returns the proper SQL to identify a table. Some databases require table names to be quoted. * Returns the proper SQL to identify a table. Some databases require table names to be quoted.
@@ -5,6 +5,7 @@
package com.samskivert.jdbc; package com.samskivert.jdbc;
import java.util.List;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
@@ -14,58 +15,58 @@ import java.sql.SQLException;
*/ */
public class DefaultLiaison extends BaseLiaison public class DefaultLiaison extends BaseLiaison
{ {
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean matchesURL (String url) public boolean matchesURL (String url)
{ {
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean isDuplicateRowException (SQLException sqe) public boolean isDuplicateRowException (SQLException sqe)
{ {
return false; return false;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean isTransientException (SQLException sqe) public boolean isTransientException (SQLException sqe)
{ {
return false; return false;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void createGenerator (Connection conn, String tableName, String columnName, int initValue) public void createGenerator (Connection conn, String tableName, String columnName, int initValue)
throws SQLException throws SQLException
{ {
// nothing doing // nothing doing
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void deleteGenerator (Connection conn, String tableName, String columnName) public void deleteGenerator (Connection conn, String tableName, String columnName)
throws SQLException throws SQLException
{ {
// nothing doing // nothing doing
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) public int lastInsertedId (Connection conn, String table, String column)
throws SQLException throws SQLException
{ {
return -1; return -1;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String columnSQL (String column) public String columnSQL (String column)
{ {
return column; return column;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String tableSQL (String table) public String tableSQL (String table)
{ {
return table; return table;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String indexSQL (String index) public String indexSQL (String index)
{ {
return index; return index;
@@ -26,31 +26,31 @@ import static com.samskivert.Log.log;
*/ */
public class HsqldbLiaison extends BaseLiaison public class HsqldbLiaison extends BaseLiaison
{ {
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean matchesURL (String url) public boolean matchesURL (String url)
{ {
return url.startsWith("jdbc:hsqldb"); return url.startsWith("jdbc:hsqldb");
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String columnSQL (String column) public String columnSQL (String column)
{ {
return "\"" + column + "\""; return "\"" + column + "\"";
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String tableSQL (String table) public String tableSQL (String table)
{ {
return "\"" + table + "\""; return "\"" + table + "\"";
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String indexSQL (String index) public String indexSQL (String index)
{ {
return "\"" + index + "\""; return "\"" + index + "\"";
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void createGenerator (Connection conn, String tableName, public void createGenerator (Connection conn, String tableName,
String columnName, int initValue) String columnName, int initValue)
throws SQLException throws SQLException
@@ -70,14 +70,14 @@ public class HsqldbLiaison extends BaseLiaison
log.info("Initial value of " + tableName + ":" + columnName + " set to " + initValue + "."); log.info("Initial value of " + tableName + ":" + columnName + " set to " + initValue + ".");
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void deleteGenerator (Connection conn, String tableName, String columnName) public void deleteGenerator (Connection conn, String tableName, String columnName)
throws SQLException throws SQLException
{ {
// HSQL's IDENTITY() does not create any database entities that we need to delete // HSQL's IDENTITY() does not create any database entities that we need to delete
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException public int lastInsertedId (Connection conn, String table, String column) throws SQLException
{ {
// HSQL does not keep track of per-table-and-column insertion data, so we are pretty much // HSQL does not keep track of per-table-and-column insertion data, so we are pretty much
@@ -93,13 +93,13 @@ public class HsqldbLiaison extends BaseLiaison
} }
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean isTransientException (SQLException sqe) public boolean isTransientException (SQLException sqe)
{ {
return false; // no known transient exceptions for HSQLDB return false; // no known transient exceptions for HSQLDB
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean isDuplicateRowException (SQLException sqe) public boolean isDuplicateRowException (SQLException sqe)
{ {
// Violation of unique constraint SYS_PK_51: duplicate value(s) for column(s) FOO // Violation of unique constraint SYS_PK_51: duplicate value(s) for column(s) FOO
@@ -119,64 +119,59 @@ public class HsqldbLiaison extends BaseLiaison
// TODO: Consider making this the general MO instead of a subclass override. In fact // TODO: Consider making this the general MO instead of a subclass override. In fact
// it may be that uniqueness should be removed from ColumnDefinition. // it may be that uniqueness should be removed from ColumnDefinition.
@Override // from DatabaseLiaison @Override // from DatabaseLiaison
public boolean createTableIfMissing ( public boolean createTableIfMissing (Connection conn, String table,
Connection conn, String table, String[] columns, ColumnDefinition[] definitions, List<String> columns, List<ColumnDefinition> defns,
String[][] uniqueConstraintColumns, String[] primaryKeyColumns) List<List<String>> uniqueColumns, List<String> pkColumns)
throws SQLException throws SQLException
{ {
if (columns.length != definitions.length) { if (columns.size() != defns.size()) {
throw new IllegalArgumentException("Column name and definition number mismatch"); throw new IllegalArgumentException("Column name and definition number mismatch");
} }
// turn this into something less fiddly to work with
List<String[]> uniqueCsts = new ArrayList<String[]>();
if (uniqueConstraintColumns != null) {
uniqueCsts.addAll(Arrays.asList(uniqueConstraintColumns));
}
// note the set of single column unique constraints already provided (see method comment) // note the set of single column unique constraints already provided (see method comment)
Set<String> seenUniques = new HashSet<String>(); Set<String> seenUniques = new HashSet<String>();
for (String[] uCols : uniqueCsts) { for (List<String> udef : uniqueColumns) {
if (uCols.length == 1) { if (udef.size() == 1) {
seenUniques.add(uCols[0]); seenUniques.addAll(udef);
} }
} }
// primary key columns are also considered implicitly unique as of HSQL 2.2.4, and it will // primary key columns are also considered implicitly unique as of HSQL 2.2.4, and it will
// freak out if we also try to include them in the UNIQUE clause, so add those too // freak out if we also try to include them in the UNIQUE clause, so add those too
if (primaryKeyColumns != null) { seenUniques.addAll(pkColumns);
for (String pkCol : primaryKeyColumns) {
seenUniques.add(pkCol);
}
}
// go through the columns and find any that are unique; these we replace with a // lazily create a copy of uniqueColumns, if needed
// non-unique variant, and instead add a new entry to the table unique constraint List<List<String>> newUniques = uniqueColumns;
ColumnDefinition[] newDefinitions = new ColumnDefinition[definitions.length];
for (int ii = 0; ii < definitions.length; ii ++) { // go through the columns and find any that are unique; these we replace with a non-unique
ColumnDefinition def = definitions[ii]; // variant, and instead add a new entry to the table unique constraint
if (def.unique) { List<ColumnDefinition> newDefns = new ArrayList<ColumnDefinition>(defns.size());
// let's be nice and not mutate the caller's object for (int ii = 0; ii < defns.size(); ii ++) {
newDefinitions[ii] = new ColumnDefinition( ColumnDefinition def = defns.get(ii);
def.type, def.nullable, false, def.defaultValue); if (!def.unique) {
// if a uniqueness constraint for this column was not in the primaryKeys or newDefns.add(def);
// uniqueConstraintColumns parameters, add the column to uniqueCsts continue;
if (!seenUniques.contains(columns[ii])) { }
uniqueCsts.add(new String[] { columns[ii] });
// let's be nice and not mutate the caller's object
newDefns.add(
new ColumnDefinition(def.type, def.nullable, false, def.defaultValue));
// if a uniqueness constraint for this column was not in the primaryKeys or
// uniqueColumns parameters, add the column to uniqueCsts
if (!seenUniques.contains(columns.get(ii))) {
if (newUniques == uniqueColumns) {
newUniques = new ArrayList<List<String>>(uniqueColumns);
} }
} else { newUniques.add(Collections.singletonList(columns.get(ii)));
newDefinitions[ii] = def;
} }
} }
// now call the real implementation with our modified data // now call the real implementation with our modified data
return super.createTableIfMissing(conn, table, columns, newDefinitions, return super.createTableIfMissing(conn, table, columns, newDefns, newUniques, pkColumns);
uniqueCsts.toArray(new String[uniqueCsts.size()][]),
primaryKeyColumns);
} }
@Override // from DatabaseLiaison @Override // from DatabaseLiaison
protected String expandDefinition ( protected String expandDefinition (String type, boolean nullable, boolean unique,
String type, boolean nullable, boolean unique, String defaultValue) String defaultValue)
{ {
StringBuilder builder = new StringBuilder(type); StringBuilder builder = new StringBuilder(type);
@@ -6,6 +6,7 @@
package com.samskivert.jdbc; package com.samskivert.jdbc;
import java.sql.*; import java.sql.*;
import java.util.List;
import static com.samskivert.Log.log; import static com.samskivert.Log.log;
@@ -14,20 +15,20 @@ import static com.samskivert.Log.log;
*/ */
public class MySQLLiaison extends BaseLiaison public class MySQLLiaison extends BaseLiaison
{ {
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean matchesURL (String url) public boolean matchesURL (String url)
{ {
return url.startsWith("jdbc:mysql"); return url.startsWith("jdbc:mysql");
} }
// from DatabaseLiaison @Override // 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);
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public boolean isTransientException (SQLException sqe) public boolean isTransientException (SQLException sqe)
{ {
String msg = sqe.getMessage(); String msg = sqe.getMessage();
@@ -36,21 +37,21 @@ public class MySQLLiaison extends BaseLiaison
msg.indexOf("Broken pipe") != -1)); msg.indexOf("Broken pipe") != -1));
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void createGenerator (Connection conn, String tableName, String columnName, int initValue) public void createGenerator (Connection conn, String tableName, String columnName, int initValue)
throws SQLException throws SQLException
{ {
// TODO: is there any way we can set the initial AUTO_INCREMENT value? // TODO: is there any way we can set the initial AUTO_INCREMENT value?
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public void deleteGenerator (Connection conn, String tableName, String columnName) public void deleteGenerator (Connection conn, String tableName, String columnName)
throws SQLException throws SQLException
{ {
// AUTO_INCREMENT does not create any database entities that we need to delete // AUTO_INCREMENT does not create any database entities that we need to delete
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public int lastInsertedId (Connection conn, String table, String column) throws SQLException 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 // MySQL does not keep track of per-table-and-column insertion data, so we are pretty much
@@ -67,9 +68,8 @@ public class MySQLLiaison extends BaseLiaison
} }
@Override // from BaseLiaison @Override // from BaseLiaison
public boolean addIndexToTable ( public boolean addIndexToTable (Connection conn, String table, List<String> columns,
Connection conn, String table, String[] columns, String ixName, boolean unique) String ixName, boolean unique) throws SQLException
throws SQLException
{ {
if (tableContainsIndex(conn, table, ixName)) { if (tableContainsIndex(conn, table, ixName)) {
return false; return false;
@@ -82,12 +82,7 @@ public class MySQLLiaison extends BaseLiaison
update.append("UNIQUE "); update.append("UNIQUE ");
} }
update.append("INDEX ").append(indexSQL(ixName)).append(" ("); update.append("INDEX ").append(indexSQL(ixName)).append(" (");
for (int ii = 0; ii < columns.length; ii ++) { appendColumns(columns, update);
if (ii > 0) {
update.append(", ");
}
update.append(columnSQL(columns[ii]));
}
update.append(")"); update.append(")");
executeQuery(conn, update.toString()); executeQuery(conn, update.toString());
@@ -96,15 +91,13 @@ public class MySQLLiaison extends BaseLiaison
} }
@Override // from BaseLiaison @Override // from BaseLiaison
public void dropIndex (Connection conn, String table, String index) public void dropIndex (Connection conn, String table, String index) throws SQLException
throws SQLException
{ {
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " DROP INDEX " + columnSQL(index)); executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " DROP INDEX " + columnSQL(index));
} }
@Override // from BaseLiaison @Override // from BaseLiaison
public void dropPrimaryKey (Connection conn, String table, String pkName) public void dropPrimaryKey (Connection conn, String table, String pkName) throws SQLException
throws SQLException
{ {
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " DROP PRIMARY KEY"); executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " DROP PRIMARY KEY");
} }
@@ -124,19 +117,19 @@ public class MySQLLiaison extends BaseLiaison
return true; return true;
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String columnSQL (String column) public String columnSQL (String column)
{ {
return "`" + column + "`"; return "`" + column + "`";
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String tableSQL (String table) public String tableSQL (String table)
{ {
return "`" + table + "`"; return "`" + table + "`";
} }
// from DatabaseLiaison @Override // from DatabaseLiaison
public String indexSQL (String index) public String indexSQL (String index)
{ {
return "`" + index + "`"; return "`" + index + "`";
@@ -10,6 +10,9 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.samskivert.io.PersistenceException; import com.samskivert.io.PersistenceException;
@@ -185,13 +188,11 @@ public class TransitionRepository extends SimpleRepository
liaison.createTableIfMissing( liaison.createTableIfMissing(
conn, conn,
"TRANSITIONS", "TRANSITIONS",
new String[] { "CLASS", "NAME", "APPLIED" }, Arrays.asList("CLASS", "NAME", "APPLIED"),
new ColumnDefinition[] { Arrays.asList(new ColumnDefinition("VARCHAR(200)", true, false, null),
new ColumnDefinition("VARCHAR(200)", true, false, null), new ColumnDefinition("VARCHAR(50)", true, false, null),
new ColumnDefinition("VARCHAR(50)", true, false, null), new ColumnDefinition("TIMESTAMP")),
new ColumnDefinition("TIMESTAMP") Collections.<List<String>>emptyList(),
}, Arrays.asList("CLASS", "NAME"));
null,
new String[] { "CLASS", "NAME" });
} }
} }
@@ -8,6 +8,9 @@ package com.samskivert.jdbc;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.*; import org.junit.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@@ -32,14 +35,14 @@ public class HsqldbLiaisonTest
{ {
invoke(new WithConnection() { invoke(new WithConnection() {
public void execute (Connection c, DatabaseLiaison dl) throws Exception { public void execute (Connection c, DatabaseLiaison dl) throws Exception {
String[] cols = new String[] { "col1", "col2", "col3" };
ColumnDefinition[] defs = new ColumnDefinition[] {
new ColumnDefinition("int", false, false, null),
new ColumnDefinition("int", false, false, "0"),
new ColumnDefinition("varchar(255)", false, false, "''")
};
boolean created = dl.createTableIfMissing( boolean created = dl.createTableIfMissing(
c, "test_table", cols, defs, null, new String[] { "col1" }); c, "test_table",
Arrays.asList("col1", "col2", "col3"),
Arrays.asList(new ColumnDefinition("int", false, false, null),
new ColumnDefinition("int", false, false, "0"),
new ColumnDefinition("varchar(255)", false, false, "''")),
Collections.<List<String>>emptyList(),
Arrays.asList("col1"));
assertTrue(created); assertTrue(created);
ResultSet rs = c.getMetaData().getColumns(null, null, "test_table", "%"); ResultSet rs = c.getMetaData().getColumns(null, null, "test_table", "%");