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;
}
// 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
public boolean changeColumn (Connection conn, String table, String column, String type,
Boolean nullable, Boolean unique, String defaultValue)
@@ -114,11 +114,19 @@ public interface DatabaseLiaison
Connection conn, String table, String column, String definition, boolean check)
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
* false if the database did or did not report a schema modification. Any of the type,
* nullable, unique and defaultValue arguments may be null, in which case the implementation
* will attempt to leave that aspect of the column unchanged.
* false if the database did or did not report a schema modification. Any of the nullable,
* unique and defaultValue arguments may be null, in which case the implementation will attempt
* to leave that aspect of the column unchanged.
*/
public boolean changeColumn (
Connection conn, String table, String column, String type, Boolean nullable,
@@ -150,6 +150,52 @@ public abstract class EntityMigration extends Modifier
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
* migrations, if false it will be run after.
@@ -61,6 +61,13 @@ public class TestRecord extends PersistentRecord
public static final ColumnExp AGE_C =
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. */
public static final String CREATED = "created";
@@ -76,21 +83,19 @@ public class TestRecord extends PersistentRecord
new ColumnExp(TestRecord.class, LAST_MODIFIED);
// AUTO-GENERATED: FIELDS END
public static final int SCHEMA_VERSION = 1;
public static final int SCHEMA_VERSION = 2;
@Id
public int recordId;
@Column(nullable=false)
public String name;
@Column(nullable=false)
public int age;
@Column(nullable=false)
public String homeTown;
public Date created;
@Column(nullable=false)
public Timestamp lastModified;
@Override
@@ -24,11 +24,10 @@ import java.sql.Date;
import java.sql.Timestamp;
import java.util.Set;
// import com.samskivert.jdbc.ConnectionProvider;
import com.samskivert.jdbc.StaticConnectionProvider;
import com.samskivert.util.RandomUtil;
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.PersistentRecord;
import com.samskivert.jdbc.depot.expression.LiteralExp;
@@ -46,8 +45,12 @@ public class TestRepository extends DepotRepository
PersistenceContext perCtx = new PersistenceContext(
"test", new StaticConnectionProvider("depot.properties"));
// tests a bogus rename migration
// perCtx.registerMigration(TestRecord.class, new EntityMigration.Rename(1, "foo", "bar"));
// tests a bogus rename migration
// 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);
@@ -60,6 +63,7 @@ public class TestRepository extends DepotRepository
record.name = "Elvis";
record.age = 99;
record.created = now;
record.homeTown = "Right here";
record.lastModified = tnow;
repo.insert(record);
@@ -75,6 +79,7 @@ public class TestRepository extends DepotRepository
record.recordId = ii;
record.name = "Spam!";
record.age = RandomUtil.getInt(150);
record.homeTown = "Over there";
record.created = now;
record.lastModified = tnow;
repo.insert(record);