diff --git a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java
index 94bc86b01..64dc48c2f 100644
--- a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java
+++ b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java
@@ -61,11 +61,6 @@ public class DbMigrationConfig {
*/
protected String modelPath = "model";
- /**
- * Subdirectory the drop ddl scripts go into.
- */
- protected String dropPath = "drop";
-
/**
* Subdirectory the rollback ddl scripts go into.
*/
@@ -76,11 +71,6 @@ public class DbMigrationConfig {
*/
protected String applySuffix = ".sql";
- /**
- * Default drop script suffix to ddl so that it isn't picked up by FlywayDb.
- */
- protected String dropSuffix = ".drop.ddl";
-
/**
* Default rollback script suffix to ddl so that it isn't picked up by FlywayDb.
*/
@@ -90,6 +80,11 @@ public class DbMigrationConfig {
protected boolean includeGeneratedFileComment;
+ /**
+ * The version of a pending drop that should be generated as the next migration.
+ */
+ protected String generatePendingDrop;
+
/**
* Return the DB platform to generate migration DDL for.
*
@@ -140,20 +135,6 @@ public class DbMigrationConfig {
this.modelPath = modelPath;
}
- /**
- * Return the relative path for the drop ddl scripts (defaults to drop).
- */
- public String getDropPath() {
- return dropPath;
- }
-
- /**
- * Set the relative path for the drop ddl scripts (defaults to drop).
- */
- public void setDropPath(String dropPath) {
- this.dropPath = dropPath;
- }
-
/**
* Return the relative path for the rollback ddl scripts (defaults to rollback).
*/
@@ -210,20 +191,6 @@ public class DbMigrationConfig {
this.applySuffix = applySuffix;
}
- /**
- * Return the drop script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
- */
- public String getDropSuffix() {
- return dropSuffix;
- }
-
- /**
- * Set the drop script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
- */
- public void setDropSuffix(String dropSuffix) {
- this.dropSuffix = dropSuffix;
- }
-
/**
* Return the rollback script suffix (defaults to ddl so that it isn't picked up by FlywayDb).
*/
@@ -252,6 +219,20 @@ public class DbMigrationConfig {
this.includeGeneratedFileComment = includeGeneratedFileComment;
}
+ /**
+ * Return the migration version (or "next") to generate pending drops for.
+ */
+ public String getGeneratePendingDrop() {
+ return generatePendingDrop;
+ }
+
+ /**
+ * Set the migration version (or "next") to generate pending drops for.
+ */
+ public void setGeneratePendingDrop(String generatePendingDrop) {
+ this.generatePendingDrop = generatePendingDrop;
+ }
+
/**
* Set the migration version.
*
@@ -275,7 +256,6 @@ public class DbMigrationConfig {
* into a single directory.
*/
public void singleDirectory() {
- this.dropPath = "";
this.rollbackPath = "";
this.modelPath = "";
}
@@ -291,13 +271,12 @@ public class DbMigrationConfig {
} else {
modelPath = properties.get("migration.modelPath", modelPath);
rollbackPath = properties.get("migration.rollbackPath", rollbackPath);
- dropPath = properties.get("migration.dropPath", dropPath);
}
applySuffix = properties.get("migration.applySuffix", applySuffix);
- dropSuffix = properties.get("migration.dropSuffix", dropSuffix);
rollbackSuffix = properties.get("migration.rollbackSuffix", rollbackSuffix);
modelSuffix = properties.get("migration.modelSuffix", modelSuffix);
includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment);
+ generatePendingDrop = properties.get("migration.generatePendingDrop", generatePendingDrop);
platform = properties.getEnum(DbPlatformName.class, "migration.platform", platform);
suppressRollback = properties.getBoolean("migration.suppressRollback", suppressRollback);
@@ -324,7 +303,6 @@ public class DbMigrationConfig {
return generate;
}
-
/**
* Called by EbeanServer on start.
*
diff --git a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java
index 65d71b60a..f4cbc9701 100644
--- a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java
+++ b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java
@@ -20,6 +20,7 @@ import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter;
import com.avaje.ebean.dbmigration.model.CurrentModel;
import com.avaje.ebean.dbmigration.model.MConfiguration;
import com.avaje.ebean.dbmigration.model.MigrationModel;
+import com.avaje.ebean.dbmigration.model.MigrationVersion;
import com.avaje.ebean.dbmigration.model.ModelContainer;
import com.avaje.ebean.dbmigration.model.ModelDiff;
import com.avaje.ebean.dbmigration.model.PlatformDdlWriter;
@@ -201,46 +202,15 @@ public class DbMigration {
if (!online) {
DbOffline.setRunningMigration();
}
-
setDefaults();
-
try {
+ Request request = createRequest();
- File migrationDir = getMigrationDirectory();
- File modelDir = getModelDirectory(migrationDir);
-
- MigrationModel migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
- ModelContainer migrated = migrationModel.read();
-
- CurrentModel currentModel = new CurrentModel(server, constraintNaming);
- ModelContainer current = currentModel.read();
-
- ModelDiff diff = new ModelDiff(migrated);
- diff.compareTo(current);
-
- if (diff.isEmpty()) {
- logger.info("no changes detected - no migration written");
- return;
- }
-
- // there were actually changes to write
- Migration dbMigration = diff.getMigration();
-
- String fullVersion = getFullVersion(migrationModel);
-
- logger.info("generating migration:{}", fullVersion);
- if (!writeMigrationXml(dbMigration, modelDir, fullVersion)) {
- logger.warn("migration already exists, not generating DDL");
-
+ String pendingVersion = generatePendingDrop();
+ if (pendingVersion != null) {
+ generatePendingDrop(request, pendingVersion);
} else {
- if (databasePlatform != null) {
- // writer needs the current model to provide table/column details for
- // history ddl generation (triggers, history tables etc)
- DdlWrite write = new DdlWrite(new MConfiguration(), current);
- PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
- writer.processMigration(dbMigration, write, migrationDir , fullVersion);
- }
- writeExtraPlatformDdl(fullVersion, currentModel, dbMigration, migrationDir);
+ generateDiff(request);
}
} finally {
@@ -250,10 +220,127 @@ public class DbMigration {
}
}
+ private void generateDiff(Request request) throws IOException {
+
+ if (request.hasPendingDrops()) {
+ logger.info("Pending un-applied drops in versions {}", request.getPendingDrops());
+ }
+
+ ModelDiff diff = request.createDiff();
+ if (diff.isEmpty()) {
+ logger.info("no changes detected - no migration written");
+ } else {
+ // there were actually changes to write
+ generateMigration(request, diff.getMigration(), null);
+ }
+ }
+
+ private void generatePendingDrop(Request request, String pendingVersion) throws IOException {
+
+ Migration migration = request.migrationForPendingDrop(pendingVersion);
+
+ generateMigration(request, migration, pendingVersion);
+ if (request.hasPendingDrops()) {
+ logger.info("... remaining pending un-applied drops in versions {}", request.getPendingDrops());
+ }
+ }
+
+ private Request createRequest() {
+ return new Request();
+ }
+
+ private class Request {
+
+ final File migrationDir;
+ final File modelDir;
+ final MigrationModel migrationModel;
+ final CurrentModel currentModel;
+ final ModelContainer migrated;
+ final ModelContainer current;
+
+ private Request() {
+ this.migrationDir = getMigrationDirectory();
+ this.modelDir = getModelDirectory(migrationDir);
+ this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix());
+ this.migrated = migrationModel.read();
+ this.currentModel = new CurrentModel(server, constraintNaming);
+ this.current = currentModel.read();
+ }
+
+ /**
+ * Return true if there are pending un-applied drops.
+ */
+ public boolean hasPendingDrops() {
+ return migrated.hasPendingDrops();
+ }
+
+ /**
+ * Return the migration for the pending drops for a given version.
+ */
+ public Migration migrationForPendingDrop(String pendingVersion) {
+
+ Migration migration = migrated.migrationForPendingDrop(pendingVersion);
+
+ // register any remaining pending drops
+ migrated.registerPendingHistoryDropColumns(current);
+ return migration;
+ }
+
+ /**
+ * Return the list of versions that have pending un-applied drops.
+ */
+ public List getPendingDrops() {
+ return migrated.getPendingDrops();
+ }
+
+ /**
+ * Create an return the diff of the current model to the migration model.
+ */
+ public ModelDiff createDiff() {
+ ModelDiff diff = new ModelDiff(migrated);
+ diff.compareTo(current);
+ return diff;
+ }
+ }
+
+ private void generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException {
+
+ String fullVersion = getFullVersion(request.migrationModel, dropsFor);
+
+ logger.info("generating migration:{}", fullVersion);
+ if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) {
+ logger.warn("migration already exists, not generating DDL");
+
+ } else {
+ if (databasePlatform != null) {
+ // writer needs the current model to provide table/column details for
+ // history ddl generation (triggers, history tables etc)
+ DdlWrite write = new DdlWrite(new MConfiguration(), request.current);
+ PlatformDdlWriter writer = createDdlWriter(databasePlatform, "");
+ writer.processMigration(dbMigration, write, request.migrationDir , fullVersion);
+ }
+ writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir);
+ }
+ }
+
+ /**
+ * Return true if the next pending drop changeSet should be generated as the next migration.
+ */
+ private String generatePendingDrop() {
+
+ String nextDrop = System.getProperty("ddl.migration.pendingDrop");
+ if (nextDrop != null) {
+ return nextDrop;
+ }
+ return migrationConfig.getGeneratePendingDrop();
+ }
+
/**
* Return the full version for the migration being generated.
+ *
+ * The full version can contain a comment suffix after a "__" double underscore.
*/
- private String getFullVersion(MigrationModel migrationModel) {
+ private String getFullVersion(MigrationModel migrationModel, String dropsFor) {
String version = migrationConfig.getVersion();
if (version == null) {
@@ -261,10 +348,14 @@ public class DbMigration {
}
String fullVersion = version;
+ if (migrationConfig.getName() != null) {
+ fullVersion += "__" + toUnderScore(migrationConfig.getName());
- String name = migrationConfig.getName();
- if (name != null) {
- fullVersion += "__" + toUnderScore(name);
+ } else if (dropsFor != null) {
+ fullVersion += "__" + toUnderScore("dropsFor_" + MigrationVersion.trim(dropsFor));
+
+ } else if (version.equals(initialVersion)) {
+ fullVersion += "__initial";
}
return fullVersion;
}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java
index 4c60c18f2..f2f0fe279 100644
--- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java
+++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java
@@ -12,8 +12,7 @@ public class DdlWrite {
public enum Mode {
APPLY,
- ROLLBACK,
- DROP
+ ROLLBACK
}
private final ModelContainer currentModel;
@@ -32,28 +31,6 @@ public class DdlWrite {
private final DdlBuffer rollback;
- /**
- * For DDL that drops tables and columns etc.
- *
- * This DDL typically can not run automatically in production as there is most commonly
- * existing servers running the application using these tables and columns. Typically
- * these drop statements may be executed AFTER all the servers in the application have
- * migrated onto new code.
- */
- private final DdlBuffer drop;
-
- /**
- * For use when History is turned off for a base table or history is no longer
- * desired on specific columns. This DDL should typically execute manually after review
- * by DBA's.
- */
- private final DdlBuffer dropHistory;
-
- /**
- * Buffer used to drop dependencies early in the 'drop script'.
- */
- private final DdlBuffer dropDropDependencies;
-
/**
* Create without any configuration or current model (no history support).
*/
@@ -73,9 +50,6 @@ public class DdlWrite {
this.rollbackDropDependencies = new BaseDdlBuffer(configuration);
this.rollbackForeignKeys = new BaseDdlBuffer(configuration);
this.rollback = new BaseDdlBuffer(configuration);
- this.drop = new BaseDdlBuffer(configuration);
- this.dropHistory = new BaseDdlBuffer(configuration);
- this.dropDropDependencies = new BaseDdlBuffer(configuration);
}
/**
@@ -115,7 +89,6 @@ public class DdlWrite {
switch (mode) {
case APPLY: return apply();
case ROLLBACK: return rollback();
- case DROP: return drop();
default:
throw new IllegalStateException("Invalid mode" + mode);
}
@@ -128,7 +101,6 @@ public class DdlWrite {
switch (mode) {
case APPLY: return applyHistory();
case ROLLBACK: return rollback();
- case DROP: return dropHistory();
default:
throw new IllegalStateException("Invalid mode" + mode);
}
@@ -142,20 +114,11 @@ public class DdlWrite {
switch (mode) {
case APPLY: return applyDropDependencies();
case ROLLBACK: return rollbackDropDependencies();
- case DROP: return dropDropDependencies();
default:
throw new IllegalStateException("Invalid mode" + mode);
}
}
-
- /**
- * Return true the drop buffers are empty.
- */
- public boolean isDropEmpty() {
- return drop.getBuffer().isEmpty() && dropHistory.getBuffer().isEmpty();
- }
-
/**
* Return the buffer that APPLY DDL is written to.
*/
@@ -216,26 +179,8 @@ public class DdlWrite {
return rollback;
}
- /**
- * Return the buffer that destructive changes are written to. This is typically drop table and
- * drop column.
- */
public DdlBuffer drop() {
- return drop;
- }
-
- /**
- * Return the buffer that is used when history is no longer required on a table or specific columns.
- */
- public DdlBuffer dropHistory() {
- return dropHistory;
- }
-
- /**
- * Return the buffer that executes early for 'drop' script.
- */
- public DdlBuffer dropDropDependencies() {
- return dropDropDependencies;
+ return apply;
}
}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java
index b70b98d7d..f6925cc55 100644
--- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java
+++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java
@@ -71,37 +71,23 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
DbTriggerUpdate triggerUpdate = createDbTriggerUpdate(writer, table);
- if (update.hasApplyChanges()) {
- // includes add, include and exclude column changes
+ String description = update.description();
+ List includedColumns = columnNamesForApply(table);
- String applyChangeDescription = update.descriptionForApply();
- List includedColumns = columnNamesForApply(table);
+ DdlBuffer apply = writer.applyHistory();
+ apply.append("-- changes: ").append(description).newLine();
- DdlBuffer apply = writer.applyHistory();
- apply.append("-- changes: ").append(applyChangeDescription).newLine();
+ triggerUpdate.prepare(DdlWrite.Mode.APPLY, includedColumns);
+ updateHistoryTriggers(triggerUpdate);
- triggerUpdate.prepare(DdlWrite.Mode.APPLY, includedColumns);
- updateHistoryTriggers(triggerUpdate);
+ // put a reverted version into the rollback buffer
+ update.toRevertedColumns(includedColumns);
- // put a reverted version into the rollback buffer
- update.toRevertedColumns(includedColumns);
+ DdlBuffer rollback = writer.rollback();
+ rollback.append("-- revert changes: ").append(description).newLine();
- DdlBuffer rollback = writer.rollback();
- rollback.append("-- revert changes: ").append(applyChangeDescription).newLine();
-
- triggerUpdate.prepare(DdlWrite.Mode.ROLLBACK, includedColumns);
- updateHistoryTriggers(triggerUpdate);//writer, DdlWrite.Mode.ROLLBACK, baseTableName, historyTableName, includedColumns);
- }
-
- if (update.hasDropChanges()) {
- // effectively applies the dropped columns changes to history triggers
-
- DdlBuffer drop = writer.dropHistory();
- drop.append("-- changes: ").append(update.descriptionForDrop()).newLine();
-
- triggerUpdate.prepare(DdlWrite.Mode.DROP, columnNamesForDrop(table));
- updateHistoryTriggers(triggerUpdate);//writer, DdlWrite.Mode.DROP, baseTableName, historyTableName, columnNamesForDrop(table));
- }
+ triggerUpdate.prepare(DdlWrite.Mode.ROLLBACK, includedColumns);
+ updateHistoryTriggers(triggerUpdate);
}
protected DbTriggerUpdate createDbTriggerUpdate(DdlWrite writer, MTable table) {
@@ -116,11 +102,10 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
String baseTable = dropHistoryTable.getBaseTable();
// drop in appropriate order
- dropTriggers(writer.dropHistory(), baseTable);
- dropHistoryTableEtc(writer.dropHistory(), baseTable);
+ dropTriggers(writer.applyDropDependencies(), baseTable);
+ dropHistoryTableEtc(writer.applyDropDependencies(), baseTable);
}
-
@Override
public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
@@ -315,11 +300,4 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl {
return table.allHistoryColumns(true);
}
- /**
- * Return the column names included in history for the drop script.
- */
- protected List columnNamesForDrop(MTable table) throws IOException {
-
- return table.allHistoryColumns(false);
- }
}
diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java
index ce83ee0bb..a7f929b9c 100644
--- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java
+++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java
@@ -10,7 +10,6 @@ import java.util.List;
*/
public class HistoryTableUpdate {
-
/**
* Column change type.
*/
@@ -32,12 +31,12 @@ public class HistoryTableUpdate {
this.column = column;
}
- public String description() {
- return change.name().toLowerCase()+" "+column;
+ public String toString() {
+ return description();
}
- private boolean isChangeFor(boolean apply) {
- return apply ? change != Change.DROP : change == Change.DROP;
+ public String description() {
+ return change.name().toLowerCase()+" "+column;
}
private void revert(List includedColumns) {
@@ -47,12 +46,11 @@ public class HistoryTableUpdate {
includedColumns.remove(column);
break;
}
- case EXCLUDE: {
+ case EXCLUDE:
+ case DROP: {
includedColumns.add(column);
break;
}
- case DROP:
- break;
default:
throw new IllegalStateException("Unexpected change "+change);
}
@@ -70,60 +68,12 @@ public class HistoryTableUpdate {
this.baseTable = baseTable;
}
- private boolean isChangeFor(boolean apply) {
- for (Column columnChange : columnChanges) {
- if (columnChange.isChangeFor(apply)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Return true if the change includes apply changes (ADD, INCLUDE, EXCLUDE).
- */
- public boolean hasApplyChanges() {
- return isChangeFor(true);
- }
-
- /**
- * Return true if the change includes DROP column.
- */
- public boolean hasDropChanges() {
- return isChangeFor(false);
- }
-
/**
* Return a description of the changes that cause the history trigger/function
- * to be regenerated (added, included or excluded columns).
+ * to be regenerated (added, included, excluded and dropped columns).
*/
- public String descriptionForApply() {
- return descriptionFor(true);
- }
-
- /**
- * Return a description of the changes that cause the history trigger/function
- * to be regenerated in the drop script (dropped columns only).
- */
- public String descriptionForDrop() {
- return descriptionFor(false);
- }
-
- private String descriptionFor(boolean apply) {
-
- StringBuilder sb = new StringBuilder(90);
- boolean first = true;
- for (Column column : columnChanges) {
- if (column.isChangeFor(apply)) {
- if (first) {
- first = false;
- } else {
- sb.append(", ");
- }
- sb.append(column.description());
- }
- }
- return sb.toString();
+ public String description() {
+ return columnChanges.toString();
}
/**
diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java
index 9cefa6407..a835aeb71 100644
--- a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java
+++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java
@@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlType;
* </choice>
* </sequence>
* <attribute name="type" use="required" type="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetType" />
+ * <attribute name="dropsFor" type="{http://www.w3.org/2001/XMLSchema}string" />
* <attribute name="generated" type="{http://www.w3.org/2001/XMLSchema}boolean" />
* <attribute name="author" type="{http://www.w3.org/2001/XMLSchema}string" />
* <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" />
@@ -62,6 +63,8 @@ public class ChangeSet {
protected List