From 24971bcc866d19edb60b95c29589b02b30613ddb Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 17 Aug 2015 16:03:51 +1200 Subject: [PATCH] #374 - Refactor to support generating multiple platform DDL for a single migration --- .../avaje/ebean/dbmigration/DbMigration.java | 106 ++++++++++--- .../dbmigration/ddlgeneration/DdlWrite.java | 13 +- .../dbmigration/model/ModelDdlWriter.java | 140 ------------------ .../ebean/dbmigration/model/ModelDiff.java | 9 ++ .../dbmigration/model/PlatformDdlWriter.java | 135 +++++++++++++++++ 5 files changed, 234 insertions(+), 169 deletions(-) delete mode 100644 src/main/java/com/avaje/ebean/dbmigration/model/ModelDdlWriter.java create mode 100644 src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java diff --git a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java index 789a8aa56..9009cd00a 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java +++ b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java @@ -16,11 +16,12 @@ import com.avaje.ebean.config.dbplatform.PostgresPlatform; import com.avaje.ebean.config.dbplatform.SQLitePlatform; import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; import com.avaje.ebean.dbmigration.migration.Migration; +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.ModelContainer; -import com.avaje.ebean.dbmigration.model.ModelDdlWriter; +import com.avaje.ebean.dbmigration.model.PlatformDdlWriter; import com.avaje.ebean.dbmigration.model.ModelDiff; import com.avaje.ebeaninternal.api.SpiEbeanServer; import org.slf4j.Logger; @@ -28,25 +29,29 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * */ public class DbMigration { - private static final Logger logger = LoggerFactory.getLogger(DbMigration.class); + protected static final Logger logger = LoggerFactory.getLogger(DbMigration.class); - private SpiEbeanServer server; + protected SpiEbeanServer server; - private DbMigrationConfig migrationConfig; + protected DbMigrationConfig migrationConfig; - private String pathToResources = "src/main/resources"; + protected String pathToResources = "src/main/resources"; - private DatabasePlatform databasePlatform; + protected DatabasePlatform databasePlatform; - private ServerConfig serverConfig; + protected List platforms = new ArrayList(); - private DbConstraintNaming constraintNaming; + protected ServerConfig serverConfig; + + protected DbConstraintNaming constraintNaming; public DbMigration() { DbOffline.asH2(); @@ -82,6 +87,16 @@ public class DbMigration { DbOffline.setPlatform(databasePlatform.getName()); } + /** + * Add an additional platform to write the migration DDL. + */ + public void addPlatform(DbPlatformName platform, String prefix) { + if (!prefix.endsWith("-")) { + prefix+="-"; + } + platforms.add(new Pair(getPlatform(platform), prefix)); + } + public void runMigration() throws IOException { @@ -102,35 +117,58 @@ public class DbMigration { 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(); - // writer needs the current model to provide table/column details for - // history ddl generation (triggers, history tables etc) - DdlWrite write = new DdlWrite(new MConfiguration(), currentModel.read()); + File writePath = getWritePath(); + logger.info("migration writing version {} to {}", nextMajorVersion, writePath.getAbsolutePath()); + writeMigrationXml(dbMigration, writePath, nextMajorVersion); - ModelDdlWriter writer = new ModelDdlWriter(databasePlatform, serverConfig); - if (!writer.processMigration(dbMigration, write)) { - logger.info("no changes detected - no migration written"); - - } else { - // there were actually changes to write - File writePath = getWritePath(); - logger.info("migration writing version {} to {}", nextMajorVersion, writePath.getAbsolutePath()); - writer.writeMigration(writePath, nextMajorVersion); + 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(), currentModel.read()); + PlatformDdlWriter writer = new PlatformDdlWriter(databasePlatform, serverConfig); + writer.processMigration(dbMigration, write, writePath, nextMajorVersion); } + writeExtraPlatformDdl(nextMajorVersion, currentModel, dbMigration, writePath); + } finally { DbOffline.reset(); } } + /** + * Write any extra platform ddl. + */ + protected void writeExtraPlatformDdl(int nextMajorVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException { + + for (Pair pair : platforms) { + DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read()); + + PlatformDdlWriter platformWriter = new PlatformDdlWriter(pair.platform, serverConfig, pair.prefix); + platformWriter.processMigration(dbMigration, platformBuffer, writePath, nextMajorVersion); + } + } + + protected void writeMigrationXml(Migration dbMigration, File resourcePath, int migrationVersion) { + File file = new File(resourcePath, "v"+migrationVersion+".0.xml"); + + MigrationXmlWriter xmlWriter = new MigrationXmlWriter(); + xmlWriter.write(dbMigration, file); + } + protected void setDefaults() { if (server == null) { setServer(Ebean.getDefaultServer()); } - if (databasePlatform == null) { - databasePlatform = server.getDatabasePlatform(); - } } protected File getWritePath() { @@ -172,4 +210,26 @@ public class DbMigration { } } + /** + * Holds a platform and prefix. Used to generate multiple platform specific DDL + * for a single migration. + */ + public static class Pair { + + /** + * The platform to generate the DDL for. + */ + public final DatabasePlatform platform; + + /** + * A prefix included into the file/resource names indicating the platform. + */ + public final String prefix; + + public Pair(DatabasePlatform platform, String prefix) { + this.platform = platform; + this.prefix = prefix; + } + } + } 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 bfe6f853f..cc2746045 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java @@ -81,17 +81,18 @@ public class DdlWrite { } /** - * Return true the drop buffer is empty. + * Return true if the apply rollback buffers are all empty. */ - public boolean isDropEmpty() { - return drop.getBuffer().isEmpty(); + public boolean isApplyRollbackEmpty() { + return rollback.getBuffer().isEmpty() + && rollbackForeignKeys.getBuffer().isEmpty(); } /** - * Return true the drop history buffer is empty. + * Return true the drop buffers are empty. */ - public boolean isDropHistoryEmpty() { - return dropHistory.getBuffer().isEmpty(); + public boolean isDropEmpty() { + return drop.getBuffer().isEmpty() && dropHistory.getBuffer().isEmpty(); } /** diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDdlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDdlWriter.java deleted file mode 100644 index ebd97f71e..000000000 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDdlWriter.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.avaje.ebean.dbmigration.model; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler; -import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; -import com.avaje.ebean.dbmigration.migration.ChangeSet; -import com.avaje.ebean.dbmigration.migration.Migration; -import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.util.List; - -/** - */ -public class ModelDdlWriter { - - - private final ServerConfig serverConfig; - - private final DatabasePlatform platform; - - private Migration dbMigration; - - private DdlWrite write; - - int changeSetCount; - - public ModelDdlWriter(DatabasePlatform platform, ServerConfig serverConfig) { - this.platform = platform; - this.serverConfig = serverConfig; - } - - public boolean processMigration(Migration dbMigration, DdlWrite write) throws IOException { - - this.changeSetCount = 0; - this.dbMigration = dbMigration; - this.write = write; - - DdlHandler handler = handler(); - - List changeSets = dbMigration.getChangeSet(); - for (ChangeSet changeSet : changeSets) { - if (!changeSet.getChangeSetChildren().isEmpty()) { - changeSetCount++; - handler.generate(write, changeSet); - } - } - handler.generateExtra(write); - - return changeSetCount > 0; - } - - /** - * Write as migration xml to the given file. - */ - public void writeMigration(File resourcePath, int migrationVersion) throws IOException { - - File file = new File(resourcePath, "v"+migrationVersion+".0.xml"); - - MigrationXmlWriter xmlWriter = new MigrationXmlWriter(); - xmlWriter.write(dbMigration, file); - - if (!write.isApplyEmpty()) { - FileWriter ddlWriter = createWriter(resourcePath, migrationVersion, ".0-apply.sql"); - try { - writeApplyDdl(ddlWriter); - ddlWriter.flush(); - } finally { - ddlWriter.close(); - } - - FileWriter rbWriter = createWriter(resourcePath, migrationVersion, ".0-applyRollback.sql"); - try { - writeApplyRollbackDdl(rbWriter); - rbWriter.flush(); - } finally { - rbWriter.close(); - } - } - - String content = write.drop().getBuffer(); - if (!content.isEmpty()) { - writeFile(resourcePath, migrationVersion, ".0-drop.sql", content); - } - - String dropHistory = write.dropHistory().getBuffer(); - if (!dropHistory.isEmpty()) { - writeFile(resourcePath, migrationVersion, ".0-dropHistory.sql", dropHistory); - } - } - - private FileWriter createWriter(File resourcePath, int migrationVersion, String suffix) throws IOException { - - File applyFile = new File(resourcePath, "v" + migrationVersion + suffix); - return new FileWriter(applyFile); - } - - private void writeFile(File resourcePath, int migrationVersion, String suffix, String content) throws IOException { - - FileWriter ddlWriter =createWriter(resourcePath, migrationVersion, suffix); - try { - ddlWriter.append(content); - ddlWriter.flush(); - } finally { - ddlWriter.close(); - } - } - - - /** - * Return the 'Create' DDL. - */ - private void writeApplyDdl(Writer writer) throws IOException { - - writer.append(write.apply().getBuffer()); - writer.append(write.applyForeignKeys().getBuffer()); - writer.append(write.applyHistory().getBuffer()); - } - - /** - * Return the 'Rollback' DDL. - */ - private void writeApplyRollbackDdl(Writer writer) throws IOException { - - writer.append(write.rollbackForeignKeys().getBuffer()); - writer.append(write.rollback().getBuffer()); - } - - /** - * Return the platform specific DdlHandler (to generate DDL). - */ - private DdlHandler handler() { - return platform.createDdlHandler(serverConfig); - } - -} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java index df4c9b56e..a0214661c 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -50,6 +50,15 @@ public class ModelDiff { this.baseModel = new ModelContainer(); } + + /** + * Return true if the apply and drop changes are both empty. + * This means there are no migration changes. + */ + public boolean isEmpty() { + return applyChanges.isEmpty() && dropChanges.isEmpty(); + } + /** * Return the diff as a migration potentially containing * an apply changeSet and a drop changeSet. diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java new file mode 100644 index 000000000..3bb9a2b18 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java @@ -0,0 +1,135 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler; +import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.List; + +/** + * Writes migration changes as platform specific DDL. + */ +public class PlatformDdlWriter { + + private final ServerConfig serverConfig; + + private final DatabasePlatform platform; + + private final String platformPrefix; + + public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig) { + this(platform, serverConfig, ""); + } + + public PlatformDdlWriter(DatabasePlatform platform, ServerConfig serverConfig, String platformPrefix) { + this.platform = platform; + this.serverConfig = serverConfig; + this.platformPrefix = platformPrefix; + } + + /** + * Write the migration as platform specific ddl. + */ + public void processMigration(Migration dbMigration, DdlWrite write, File writePath, int nextMajorVersion) throws IOException { + + DdlHandler handler = handler(); + + List changeSets = dbMigration.getChangeSet(); + for (ChangeSet changeSet : changeSets) { + if (!changeSet.getChangeSetChildren().isEmpty()) { + handler.generate(write, changeSet); + } + } + handler.generateExtra(write); + + writePlatformDdl(write, writePath, nextMajorVersion); + } + + /** + * Write the ddl files. + */ + protected void writePlatformDdl(DdlWrite write, File resourcePath, int migrationVersion) throws IOException { + + if (!write.isApplyEmpty()) { + FileWriter applyWriter = createWriter(resourcePath, migrationVersion, "apply.sql"); + try { + writeApplyDdl(applyWriter, write); + applyWriter.flush(); + } finally { + applyWriter.close(); + } + + if (!write.isApplyRollbackEmpty()) { + FileWriter applyRollbackWriter = createWriter(resourcePath, migrationVersion, "applyRollback.sql"); + try { + writeApplyRollbackDdl(applyRollbackWriter, write); + applyRollbackWriter.flush(); + } finally { + applyRollbackWriter.close(); + } + } + } + + if (!write.isDropEmpty()) { + FileWriter dropWriter = createWriter(resourcePath, migrationVersion, "drop.sql"); + try { + writeDropDdl(dropWriter, write); + dropWriter.flush(); + } finally { + dropWriter.close(); + } + } + } + + protected FileWriter createWriter(File resourcePath, int migrationVersion, String suffix) throws IOException { + + File applyFile = new File(resourcePath, "v" + migrationVersion + ".0-" + platformPrefix + suffix); + return new FileWriter(applyFile); + } + + /** + * Write the 'Apply' DDL buffers to the writer. + */ + protected void writeApplyDdl(Writer writer, DdlWrite write) throws IOException { + + // merge the apply buffers in the appropriate order + writer.append(write.apply().getBuffer()); + writer.append(write.applyForeignKeys().getBuffer()); + writer.append(write.applyHistory().getBuffer()); + } + + /** + * Write the 'Rollback' DDL buffers to the writer. + */ + protected void writeApplyRollbackDdl(Writer writer, DdlWrite write) throws IOException { + + // merge the rollback buffers in the appropriate order + writer.append(write.rollbackForeignKeys().getBuffer()); + writer.append(write.rollback().getBuffer()); + } + + /** + * Write the 'Drop' DDL buffers to the writer. + */ + protected void writeDropDdl(Writer writer, DdlWrite write) throws IOException { + + // merge the rollback buffers in the appropriate order + writer.append(write.dropHistory().getBuffer()); + writer.append(write.drop().getBuffer()); + } + + /** + * Return the platform specific DdlHandler (to generate DDL). + */ + protected DdlHandler handler() { + return platform.createDdlHandler(serverConfig); + } + +}