#374 - Refactor to support generating multiple platform DDL for a single migration

This commit is contained in:
Robin Bygrave
2015-08-17 16:03:51 +12:00
parent c1c45139a0
commit 24971bcc86
5 changed files with 234 additions and 169 deletions
@@ -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<Pair> platforms = new ArrayList<Pair>();
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;
}
}
}
@@ -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();
}
/**
@@ -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<ChangeSet> 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);
}
}
@@ -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.
@@ -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<ChangeSet> 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);
}
}