Created an explicit EntityMigration.Add for when you want to add a column to a

database and need to provide a defaultValue at the time that you add it, but
don't want to have to define a permanent default value on the field.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@2409 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
samskivert
2008-09-10 02:00:19 +00:00
parent e0d3f6b2c9
commit d45da29c7b
5 changed files with 91 additions and 12 deletions
@@ -160,6 +160,21 @@ public abstract class BaseLiaison implements DatabaseLiaison
return true; return true;
} }
// from DatabaseLiaison
public boolean addColumn (Connection conn, String table, String column,
ColumnDefinition newColumnDef, boolean check)
throws SQLException
{
if (check && tableContainsColumn(conn, table, column)) {
return false;
}
executeQuery(conn, "ALTER TABLE " + tableSQL(table) + " ADD COLUMN " +
columnSQL(column) + " " + expandDefinition(newColumnDef));
log.info("Database column '" + column + "' added to table '" + table + "'.");
return true;
}
// from DatabaseLiaison // 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)
@@ -114,11 +114,19 @@ public interface DatabaseLiaison
Connection conn, String table, String column, String definition, boolean check) Connection conn, String table, String column, String definition, boolean check)
throws SQLException; throws SQLException;
/**
* Adds a column to a table with the given definition. Tests for the previous existence of
* the column iff 'check' is true.
*/
public boolean addColumn (
Connection conn, String table, String column, ColumnDefinition columnDef, boolean check)
throws SQLException;
/** /**
* Alter the definition, but not the name, of a given column on a given table. Returns true or * 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. Any of the type, * false if the database did or did not report a schema modification. Any of the nullable,
* nullable, unique and defaultValue arguments may be null, in which case the implementation * unique and defaultValue arguments may be null, in which case the implementation will attempt
* 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, Boolean nullable, Connection conn, String table, String column, String type, Boolean nullable,
@@ -150,6 +150,52 @@ public abstract class EntityMigration extends Modifier
protected ColumnDefinition _newColumnDef; protected ColumnDefinition _newColumnDef;
} }
/**
* A convenient migration for adding a new column that requires a default value to be specified
* during the addition. Normally Depot will automatically handle column addition, but if you
* have a column that normally does not have a default value but needs one when it is added to
* a table with existing rows, you can use this migration.
*
* @see Column#defaultValue
*/
public static class Add extends EntityMigration
{
public Add (int targetVersion, String fieldName, String defaultValue) {
super(targetVersion);
_fieldName = fieldName;
_defaultValue = defaultValue;
}
@Override public int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException {
// override the default value in the column definition with the one provided
ColumnDefinition defColumnDef = new ColumnDefinition(
_newColumnDef.getType(), _newColumnDef.isNullable(),
_newColumnDef.isUnique(), _defaultValue);
// first add the column with the overridden default value
if (liaison.addColumn(conn, _tableName, _fieldName, defColumnDef, true)) {
// then change the column to the permanent default value
liaison.changeColumn(conn, _tableName, _fieldName, _newColumnDef.getType(),
null, null, _newColumnDef.getDefaultValue());
return 1;
}
return 0;
}
@Override public boolean runBeforeDefault () {
return true;
}
@Override
protected void init (String tableName, Map<String, FieldMarshaller<?>> marshallers) {
super.init(tableName, marshallers);
_columnName = marshallers.get(_fieldName).getColumnName();
_newColumnDef = marshallers.get(_fieldName).getColumnDefinition();
}
protected String _fieldName, _columnName, _defaultValue;
protected ColumnDefinition _newColumnDef;
}
/** /**
* If this method returns true, this migration will be run <b>before</b> the default * If this method returns true, this migration will be run <b>before</b> the default
* migrations, if false it will be run after. * migrations, if false it will be run after.
@@ -61,6 +61,13 @@ public class TestRecord extends PersistentRecord
public static final ColumnExp AGE_C = public static final ColumnExp AGE_C =
new ColumnExp(TestRecord.class, AGE); new ColumnExp(TestRecord.class, AGE);
/** The column identifier for the {@link #homeTown} field. */
public static final String HOME_TOWN = "homeTown";
/** The qualified column identifier for the {@link #homeTown} field. */
public static final ColumnExp HOME_TOWN_C =
new ColumnExp(TestRecord.class, HOME_TOWN);
/** The column identifier for the {@link #created} field. */ /** The column identifier for the {@link #created} field. */
public static final String CREATED = "created"; public static final String CREATED = "created";
@@ -76,21 +83,19 @@ public class TestRecord extends PersistentRecord
new ColumnExp(TestRecord.class, LAST_MODIFIED); new ColumnExp(TestRecord.class, LAST_MODIFIED);
// AUTO-GENERATED: FIELDS END // AUTO-GENERATED: FIELDS END
public static final int SCHEMA_VERSION = 1; public static final int SCHEMA_VERSION = 2;
@Id @Id
public int recordId; public int recordId;
@Column(nullable=false)
public String name; public String name;
@Column(nullable=false)
public int age; public int age;
@Column(nullable=false) public String homeTown;
public Date created; public Date created;
@Column(nullable=false)
public Timestamp lastModified; public Timestamp lastModified;
@Override @Override
@@ -24,11 +24,10 @@ import java.sql.Date;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.Set; import java.util.Set;
// import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.StaticConnectionProvider; import com.samskivert.jdbc.StaticConnectionProvider;
import com.samskivert.util.RandomUtil; import com.samskivert.util.RandomUtil;
import com.samskivert.jdbc.depot.DepotRepository; import com.samskivert.jdbc.depot.DepotRepository;
// import com.samskivert.jdbc.depot.EntityMigration; import com.samskivert.jdbc.depot.EntityMigration;
import com.samskivert.jdbc.depot.PersistenceContext; import com.samskivert.jdbc.depot.PersistenceContext;
import com.samskivert.jdbc.depot.PersistentRecord; import com.samskivert.jdbc.depot.PersistentRecord;
import com.samskivert.jdbc.depot.expression.LiteralExp; import com.samskivert.jdbc.depot.expression.LiteralExp;
@@ -46,8 +45,12 @@ public class TestRepository extends DepotRepository
PersistenceContext perCtx = new PersistenceContext( PersistenceContext perCtx = new PersistenceContext(
"test", new StaticConnectionProvider("depot.properties")); "test", new StaticConnectionProvider("depot.properties"));
// tests a bogus rename migration // tests a bogus rename migration
// perCtx.registerMigration(TestRecord.class, new EntityMigration.Rename(1, "foo", "bar")); // perCtx.registerMigration(TestRecord.class, new EntityMigration.Rename(1, "foo", "bar"));
// tests a custom add column migration
perCtx.registerMigration(TestRecord.class,
new EntityMigration.Add(2, TestRecord.HOME_TOWN, "'Anytown USA'"));
TestRepository repo = new TestRepository(perCtx); TestRepository repo = new TestRepository(perCtx);
@@ -60,6 +63,7 @@ public class TestRepository extends DepotRepository
record.name = "Elvis"; record.name = "Elvis";
record.age = 99; record.age = 99;
record.created = now; record.created = now;
record.homeTown = "Right here";
record.lastModified = tnow; record.lastModified = tnow;
repo.insert(record); repo.insert(record);
@@ -75,6 +79,7 @@ public class TestRepository extends DepotRepository
record.recordId = ii; record.recordId = ii;
record.name = "Spam!"; record.name = "Spam!";
record.age = RandomUtil.getInt(150); record.age = RandomUtil.getInt(150);
record.homeTown = "Over there";
record.created = now; record.created = now;
record.lastModified = tnow; record.lastModified = tnow;
repo.insert(record); repo.insert(record);