diff --git a/dbmigration-test/resources/dbmigration/myapp/migration-current.xml b/dbmigration-test/resources/dbmigration/myapp/migration-current.xml new file mode 100644 index 000000000..bda435bc4 --- /dev/null +++ b/dbmigration-test/resources/dbmigration/myapp/migration-current.xml @@ -0,0 +1,1311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ms-migration.xml b/ms-migration.xml new file mode 100644 index 000000000..339a4c535 --- /dev/null +++ b/ms-migration.xml @@ -0,0 +1,1311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mysql-migration.xml b/mysql-migration.xml new file mode 100644 index 000000000..a676629fb --- /dev/null +++ b/mysql-migration.xml @@ -0,0 +1,1311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ora-migration.xml b/ora-migration.xml new file mode 100644 index 000000000..f2df70ed6 --- /dev/null +++ b/ora-migration.xml @@ -0,0 +1,1311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java new file mode 100644 index 000000000..030b026f6 --- /dev/null +++ b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java @@ -0,0 +1,42 @@ +package com.avaje.ebean.config; + +/** + * Configuration for the DB migration processing. + */ +public class DbMigrationConfig { + + /** + * The application name which is used as the unique code when applying migrations. + */ + private String appName; + + /** + * Path where migration + */ + private String resourcePath; + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + public String getResourcePath() { + return resourcePath; + } + + public void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + /** + * Load the settings from the PropertiesWrapper. + */ + public void loadSettings(PropertiesWrapper properties) { + + appName = properties.get("migration.appName", appName); + resourcePath = properties.get("migration.resourcePath", resourcePath); + } +} diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 47a86d573..29f9affe4 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -207,6 +207,11 @@ public class ServerConfig { */ private DataSourceConfig dataSourceConfig = new DataSourceConfig(); + /** + * The db migration config (migration resource path etc). + */ + private DbMigrationConfig migrationConfig = new DbMigrationConfig(); + /** * Set to true if the DataSource uses autoCommit. *

@@ -502,7 +507,7 @@ public class ServerConfig { /** * Return the JDBC batch mode to use per save(), delete(), insert() or update() request. *

- * This makes sense when a save() or delete() etc cascades and executes multiple child statements. The best caase + * This makes sense when a save() or delete() cascades and executes multiple child statements. The best case * for this is when saving a master/parent bean this cascade inserts many detail/child beans. *

*

@@ -620,6 +625,20 @@ public class ServerConfig { this.databaseSequenceBatchSize = databaseSequenceBatchSize; } + /** + * Return the DB migration configuration. + */ + public DbMigrationConfig getMigrationConfig() { + return migrationConfig; + } + + /** + * Set the DB migration configuration. + */ + public void setMigrationConfig(DbMigrationConfig migrationConfig) { + this.migrationConfig = migrationConfig; + } + /** * Return the suffix appended to the base table to derive the view that contains the union * of the base table and the history table in order to support asOf queries. @@ -1856,6 +1875,8 @@ public class ServerConfig { */ protected void loadSettings(PropertiesWrapper p) { + migrationConfig.loadSettings(p); + namingConvention = createNamingConvention(p, namingConvention); if (namingConvention != null) { namingConvention.loadFromProperties(p); diff --git a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java new file mode 100644 index 000000000..6163ec561 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java @@ -0,0 +1,56 @@ +package com.avaje.ebean.dbmigration; + +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.config.DbMigrationConfig; +import com.avaje.ebean.dbmigration.model.CurrentModel; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +/** + * + */ +public class DbMigration { + + private static final Logger logger = LoggerFactory.getLogger(DbMigration.class); + + private final SpiEbeanServer server; + + private final DbMigrationConfig migrationConfig; + + public DbMigration() { + this(Ebean.getDefaultServer()); + } + + public DbMigration(EbeanServer ebeanServer) { + this.server = (SpiEbeanServer) ebeanServer; + this.migrationConfig = server.getServerConfig().getMigrationConfig(); + } + + + + public void writeCurrent() { + + CurrentModel currentModel = new CurrentModel(server); + + File writeTo = getWritePath(); + logger.info("... write to {}", writeTo.getAbsolutePath()); + currentModel.writeMigration(writeTo); + } + + public File getWritePath() { + File resourceRootDir = new File("./dbmigration-test/resources"); + + // expect to be a relative path + String resourcePath = migrationConfig.getResourcePath(); + + File path = new File(resourceRootDir, resourcePath); + if (!path.exists()) { + path.mkdirs(); + } + return new File(path, "migration-current.xml"); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/DbOffline.java b/src/main/java/com/avaje/ebean/dbmigration/DbOffline.java new file mode 100644 index 000000000..b65fa303d --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/DbOffline.java @@ -0,0 +1,37 @@ +package com.avaje.ebean.dbmigration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class DbOffline { + + private static final Logger logger = LoggerFactory.getLogger(DbOffline.class); + + private static final String KEY = "ebean.dboffline"; + + public static final String H2 = "H2"; + + public static void setPlatform(String platformName) { + System.setProperty(KEY, platformName); + } + + public static String getPlatform() { + return System.getProperty(KEY); + } + + public static void asH2() { + setPlatform(H2); + } + + public static boolean isSet() { + return getPlatform() != null; + } + + public static void reset() { + System.clearProperty(KEY); + logger.info("reset"); + } +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java index d627c5458..edf2cb695 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/BaseDdlHandler.java @@ -5,6 +5,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.platform.BaseTableDdl; import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; import com.avaje.ebean.dbmigration.ddlgeneration.platform.PlatformDdl; import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.ChangeSet; import com.avaje.ebean.dbmigration.migration.CreateTable; import com.avaje.ebean.dbmigration.migration.DropColumn; @@ -23,7 +24,7 @@ public class BaseDdlHandler implements DdlHandler { public BaseDdlHandler(DdlNamingConvention namingConvention, PlatformDdl platformDdl) { this.tableDdl = new BaseTableDdl(namingConvention, platformDdl); - this.columnDdl = new BaseColumnDdl(); + this.columnDdl = new BaseColumnDdl(platformDdl); } @Override @@ -37,6 +38,8 @@ public class BaseDdlHandler implements DdlHandler { generate(writer, (AddColumn) change); } else if (change instanceof DropColumn) { generate(writer, (DropColumn) change); + } else if (change instanceof AlterColumn) { + generate(writer, (AlterColumn) change); } } } @@ -56,4 +59,8 @@ public class BaseDdlHandler implements DdlHandler { columnDdl.generate(writer, dropColumn); } + @Override + public void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException { + columnDdl.generate(writer, alterColumn); + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java index 00192f92a..ade0ee159 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/ColumnDdl.java @@ -1,23 +1,28 @@ package com.avaje.ebean.dbmigration.ddlgeneration; import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.DropColumn; import java.io.IOException; /** - * Write AddColumn or DropColumn. + * Write DDL for AddColumn , DropColumn or AlterColumn. */ public interface ColumnDdl { /** - * Write a AddColumn change. + * Write the add column change. */ void generate(DdlWrite writer, AddColumn addColumn) throws IOException; /** - * Write a DropColumn change. + * Write the drop column change. */ void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; + /** + * Write the alter column changes. + */ + void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException; } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java index 715657724..fa5f7b043 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlHandler.java @@ -1,6 +1,7 @@ package com.avaje.ebean.dbmigration.ddlgeneration; import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.ChangeSet; import com.avaje.ebean.dbmigration.migration.CreateTable; import com.avaje.ebean.dbmigration.migration.DropColumn; @@ -18,4 +19,7 @@ public interface DdlHandler { void generate(DdlWrite writer, AddColumn addColumn) throws IOException; void generate(DdlWrite writer, DropColumn dropColumn) throws IOException; -} + + void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException; + +} \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java index e92f2593c..f7416ff48 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseColumnDdl.java @@ -4,6 +4,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.ColumnDdl; import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.Column; import com.avaje.ebean.dbmigration.migration.DropColumn; @@ -14,6 +15,11 @@ import java.util.List; */ public class BaseColumnDdl implements ColumnDdl { + protected final PlatformDdl platformDdl; + + public BaseColumnDdl(PlatformDdl platformDdl) { + this.platformDdl = platformDdl; + } @Override public void generate(DdlWrite writer, AddColumn addColumn) throws IOException { @@ -40,6 +46,89 @@ public class BaseColumnDdl implements ColumnDdl { // are put into a separate changeSet that is run last } + @Override + public void generate(DdlWrite writer, AlterColumn alterColumn) throws IOException { + + if (isTrue(alterColumn.isHistoryExclude())) { + historyExcludeColumn(writer, alterColumn); + } else if (isFalse(alterColumn.isHistoryExclude())) { + historyIncludeColumn(writer, alterColumn); + } + + if (hasValue(alterColumn.getOldReferences())) { + dropForeignKey(writer, alterColumn); + } + if (hasValue(alterColumn.getNewReferences())) { + addForeignKey(writer, alterColumn); + } + + if (isTrue(alterColumn.isUnique())) { + addUniqueConstraint(writer, alterColumn); + } else if (isFalse(alterColumn.isUnique())) { + dropUniqueConstraint(writer, alterColumn); + } + + if (isTrue(alterColumn.isUniqueOneToOne())) { + addUniqueOneToOneConstraint(writer, alterColumn); + } else if (isFalse(alterColumn.isUniqueOneToOne())) { + dropUniqueOneToOneConstraint(writer, alterColumn); + } + + } + + + + protected void addForeignKey(DdlWrite writer, AlterColumn alterColumn) { + + } + + protected void dropForeignKey(DdlWrite writer, AlterColumn alterColumn) { + + } + + protected void dropUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alterColumn) { + + } + + protected void addUniqueOneToOneConstraint(DdlWrite writer, AlterColumn alterColumn) { + + } + + protected void dropUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException { + + String tableName = alter.getTableName(); + String columnName = alter.getColumnName(); + String uqName = platformDdl.namingConvention.uniqueConstraintName(tableName, columnName, 50); + + writer.apply() + .append(platformDdl.dropIndex(uqName, tableName)) + .endOfStatement(); + } + + protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter) throws IOException { + + String tableName = alter.getTableName(); + String columnName = alter.getColumnName(); + String uqName = platformDdl.namingConvention.uniqueConstraintName(tableName, columnName, 50); + + String[] cols = {columnName}; + writer.apply() + .append(platformDdl.createExternalUniqueForOneToOne(uqName, tableName, cols)) + .endOfStatement(); + + writer.rollbackForeignKeys() + .append(platformDdl.dropIndex(uqName, tableName)) + .endOfStatement(); + } + + protected void historyIncludeColumn(DdlWrite writer, AlterColumn alterColumn) { + platformDdl.historyIncludeColumn(writer, alterColumn); + } + + protected void historyExcludeColumn(DdlWrite writer, AlterColumn alterColumn) { + platformDdl.historyExcludeColumn(writer, alterColumn); + } + protected void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException { buffer.append("alter table ").append(tableName) @@ -63,6 +152,14 @@ public class BaseColumnDdl implements ColumnDdl { } + protected boolean isFalse(Boolean value) { + return value != null && !value; + } + + protected boolean isTrue(Boolean value) { + return value != null && value; + } + protected boolean hasValue(String value) { return value != null && !value.trim().isEmpty(); } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java index 3877de935..9d740c606 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -356,20 +356,6 @@ public class BaseTableDdl implements TableDdl { appendColumns(pkColumns, buffer); } - /** - * Write alter table add primary key statement. - */ - public void alterTableAddPrimaryKey(DdlBuffer buffer, String tableName, List pk) throws IOException { - - String[] pkColumns = toColumnNames(pk); - String pkName = determinePrimaryKeyName(tableName); - - buffer.append("alter table ").append(tableName); - buffer.append(" add primary key ").append(pkName); - appendColumns(pkColumns, buffer); - buffer.append(")").endOfStatement(); - } - /** * Return as an array of string column names. */ diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MsSqlServerDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MsSqlServerDdl.java index 8204eb8df..15636f65e 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MsSqlServerDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/MsSqlServerDdl.java @@ -13,6 +13,7 @@ public class MsSqlServerDdl extends PlatformDdl { this.identitySuffix = " identity(1,1)"; this.foreignKeyRestrict = ""; this.inlineUniqueOneToOne = false; + this.namingConvention.maxConstraintNameLength = 62; //Actually 128 } @Override diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java index 422d383c7..eadee2052 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -7,6 +7,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.BaseDdlHandler; import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler; import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; import com.avaje.ebean.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter; +import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.IdentityType; import com.avaje.ebean.dbmigration.model.MTable; @@ -176,4 +177,12 @@ public class PlatformDdl { // does nothing by default, really this is a MsSqlServer specific requirement return ""; } + + public void historyExcludeColumn(DdlWrite writer, AlterColumn alterColumn) { + + } + + public void historyIncludeColumn(DdlWrite writer, AlterColumn alterColumn) { + + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java index 8db849012..30dd1e10e 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/PostgresDdl.java @@ -12,6 +12,7 @@ public class PostgresDdl extends PlatformDdl { super(platformTypes, dbIdentity); this.historyDdl = new PostgresHistoryDdl(this.namingConvention.normalise); this.dropTableCascade = " cascade"; + this.namingConvention.maxConstraintNameLength = 62; } /** diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/AlterColumn.java b/src/main/java/com/avaje/ebean/dbmigration/migration/AlterColumn.java new file mode 100644 index 000000000..55a05a233 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/AlterColumn.java @@ -0,0 +1,384 @@ + +package com.avaje.ebean.dbmigration.migration; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <attribute name="columnName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="tableName" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="notnull" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *       <attribute name="historyExclude" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *       <attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *       <attribute name="uniqueOneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *       <attribute name="oldDefaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="newDefaultValue" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="oldCheckConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="newCheckConstraint" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="oldReferences" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="newReferences" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "") +@XmlRootElement(name = "alterColumn") +public class AlterColumn { + + @XmlAttribute(name = "columnName", required = true) + protected String columnName; + @XmlAttribute(name = "tableName", required = true) + protected String tableName; + @XmlAttribute(name = "type") + protected String type; + @XmlAttribute(name = "notnull") + protected Boolean notnull; + @XmlAttribute(name = "historyExclude") + protected Boolean historyExclude; + @XmlAttribute(name = "unique") + protected Boolean unique; + @XmlAttribute(name = "uniqueOneToOne") + protected Boolean uniqueOneToOne; + @XmlAttribute(name = "oldDefaultValue") + protected String oldDefaultValue; + @XmlAttribute(name = "newDefaultValue") + protected String newDefaultValue; + @XmlAttribute(name = "oldCheckConstraint") + protected String oldCheckConstraint; + @XmlAttribute(name = "newCheckConstraint") + protected String newCheckConstraint; + @XmlAttribute(name = "oldReferences") + protected String oldReferences; + @XmlAttribute(name = "newReferences") + protected String newReferences; + + /** + * Gets the value of the columnName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getColumnName() { + return columnName; + } + + /** + * Sets the value of the columnName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setColumnName(String value) { + this.columnName = value; + } + + /** + * Gets the value of the tableName property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTableName() { + return tableName; + } + + /** + * Sets the value of the tableName property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTableName(String value) { + this.tableName = value; + } + + /** + * Gets the value of the type property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getType() { + return type; + } + + /** + * Sets the value of the type property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setType(String value) { + this.type = value; + } + + /** + * Gets the value of the notnull property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isNotnull() { + return notnull; + } + + /** + * Sets the value of the notnull property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setNotnull(Boolean value) { + this.notnull = value; + } + + /** + * Gets the value of the historyExclude property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isHistoryExclude() { + return historyExclude; + } + + /** + * Sets the value of the historyExclude property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setHistoryExclude(Boolean value) { + this.historyExclude = value; + } + + /** + * Gets the value of the unique property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUnique() { + return unique; + } + + /** + * Sets the value of the unique property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUnique(Boolean value) { + this.unique = value; + } + + /** + * Gets the value of the uniqueOneToOne property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isUniqueOneToOne() { + return uniqueOneToOne; + } + + /** + * Sets the value of the uniqueOneToOne property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setUniqueOneToOne(Boolean value) { + this.uniqueOneToOne = value; + } + + /** + * Gets the value of the oldDefaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldDefaultValue() { + return oldDefaultValue; + } + + /** + * Sets the value of the oldDefaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldDefaultValue(String value) { + this.oldDefaultValue = value; + } + + /** + * Gets the value of the newDefaultValue property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewDefaultValue() { + return newDefaultValue; + } + + /** + * Sets the value of the newDefaultValue property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewDefaultValue(String value) { + this.newDefaultValue = value; + } + + /** + * Gets the value of the oldCheckConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldCheckConstraint() { + return oldCheckConstraint; + } + + /** + * Sets the value of the oldCheckConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldCheckConstraint(String value) { + this.oldCheckConstraint = value; + } + + /** + * Gets the value of the newCheckConstraint property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewCheckConstraint() { + return newCheckConstraint; + } + + /** + * Sets the value of the newCheckConstraint property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewCheckConstraint(String value) { + this.newCheckConstraint = value; + } + + /** + * Gets the value of the oldReferences property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getOldReferences() { + return oldReferences; + } + + /** + * Sets the value of the oldReferences property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setOldReferences(String value) { + this.oldReferences = value; + } + + /** + * Gets the value of the newReferences property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getNewReferences() { + return newReferences; + } + + /** + * Sets the value of the newReferences property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setNewReferences(String value) { + this.newReferences = value; + } + +} 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 4024e6f50..16fd6b4aa 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java @@ -51,14 +51,10 @@ public class ChangeSet { @XmlElement(name = "dropTable", type = DropTable.class), @XmlElement(name = "renameTable", type = RenameTable.class), @XmlElement(name = "createHistoryTable", type = CreateHistoryTable.class), - @XmlElement(name = "createView", type = CreateView.class), - @XmlElement(name = "dropView", type = DropView.class), - @XmlElement(name = "renameView", type = RenameView.class), @XmlElement(name = "addColumn", type = AddColumn.class), @XmlElement(name = "dropColumn", type = DropColumn.class), - @XmlElement(name = "renameColumn", type = RenameColumn.class), - @XmlElement(name = "addForeignKey", type = AddForeignKey.class), - @XmlElement(name = "dropForeignKey", type = DropForeignKey.class) + @XmlElement(name = "alterColumn", type = AlterColumn.class), + @XmlElement(name = "renameColumn", type = RenameColumn.class) }) protected List changeSetChildren; @XmlAttribute(name = "type", required = true) @@ -94,14 +90,10 @@ public class ChangeSet { * {@link DropTable } * {@link RenameTable } * {@link CreateHistoryTable } - * {@link CreateView } - * {@link DropView } - * {@link RenameView } * {@link AddColumn } * {@link DropColumn } + * {@link AlterColumn } * {@link RenameColumn } - * {@link AddForeignKey } - * {@link DropForeignKey } * * */ diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java index 0825264e3..69e84822c 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/Column.java @@ -23,6 +23,7 @@ import javax.xml.bind.annotation.XmlValue; * <attribute name="defaultValue" type="{http://www.w3.org/2001/XMLSchema}string" /> * <attribute name="notnull" type="{http://www.w3.org/2001/XMLSchema}boolean" /> * <attribute name="checkConstraint" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="historyExclude" type="{http://www.w3.org/2001/XMLSchema}boolean" /> * <attribute name="unique" type="{http://www.w3.org/2001/XMLSchema}boolean" /> * <attribute name="uniqueOneToOne" type="{http://www.w3.org/2001/XMLSchema}boolean" /> * <attribute name="primaryKey" type="{http://www.w3.org/2001/XMLSchema}boolean" /> @@ -55,6 +56,8 @@ public class Column { protected Boolean notnull; @XmlAttribute(name = "checkConstraint") protected String checkConstraint; + @XmlAttribute(name = "historyExclude") + protected Boolean historyExclude; @XmlAttribute(name = "unique") protected Boolean unique; @XmlAttribute(name = "uniqueOneToOne") @@ -212,6 +215,30 @@ public class Column { this.checkConstraint = value; } + /** + * Gets the value of the historyExclude property. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isHistoryExclude() { + return historyExclude; + } + + /** + * Sets the value of the historyExclude property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setHistoryExclude(Boolean value) { + this.historyExclude = value; + } + /** * Gets the value of the unique property. * diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java index 1b9610b16..83a1ae9ef 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ObjectFactory.java @@ -30,11 +30,19 @@ public class ObjectFactory { } /** - * Create an instance of {@link CreateTable } + * Create an instance of {@link Rollback } * */ - public CreateTable createCreateTable() { - return new CreateTable(); + public Rollback createRollback() { + return new Rollback(); + } + + /** + * Create an instance of {@link AddColumn } + * + */ + public AddColumn createAddColumn() { + return new AddColumn(); } /** @@ -45,6 +53,14 @@ public class ObjectFactory { return new Column(); } + /** + * Create an instance of {@link CreateTable } + * + */ + public CreateTable createCreateTable() { + return new CreateTable(); + } + /** * Create an instance of {@link UniqueConstraint } * @@ -62,11 +78,11 @@ public class ObjectFactory { } /** - * Create an instance of {@link DropForeignKey } + * Create an instance of {@link Apply } * */ - public DropForeignKey createDropForeignKey() { - return new DropForeignKey(); + public Apply createApply() { + return new Apply(); } /** @@ -101,6 +117,14 @@ public class ObjectFactory { return new DropHistoryTable(); } + /** + * Create an instance of {@link AlterColumn } + * + */ + public AlterColumn createAlterColumn() { + return new AlterColumn(); + } + /** * Create an instance of {@link DropColumn } * @@ -110,11 +134,11 @@ public class ObjectFactory { } /** - * Create an instance of {@link DropView } + * Create an instance of {@link ChangeSet } * */ - public DropView createDropView() { - return new DropView(); + public ChangeSet createChangeSet() { + return new ChangeSet(); } /** @@ -126,19 +150,11 @@ public class ObjectFactory { } /** - * Create an instance of {@link Apply } + * Create an instance of {@link DropTable } * */ - public Apply createApply() { - return new Apply(); - } - - /** - * Create an instance of {@link Rollback } - * - */ - public Rollback createRollback() { - return new Rollback(); + public DropTable createDropTable() { + return new DropTable(); } /** @@ -157,62 +173,6 @@ public class ObjectFactory { return new RenameColumn(); } - /** - * Create an instance of {@link CreateView } - * - */ - public CreateView createCreateView() { - return new CreateView(); - } - - /** - * Create an instance of {@link DropTable } - * - */ - public DropTable createDropTable() { - return new DropTable(); - } - - /** - * Create an instance of {@link AddColumn } - * - */ - public AddColumn createAddColumn() { - return new AddColumn(); - } - - /** - * Create an instance of {@link RenameView } - * - */ - public RenameView createRenameView() { - return new RenameView(); - } - - /** - * Create an instance of {@link AddForeignKey } - * - */ - public AddForeignKey createAddForeignKey() { - return new AddForeignKey(); - } - - /** - * Create an instance of {@link ChangeSet } - * - */ - public ChangeSet createChangeSet() { - return new ChangeSet(); - } - - /** - * Create an instance of {@link Application } - * - */ - public Application createApplication() { - return new Application(); - } - /** * Create an instance of {@link Migration } * @@ -221,12 +181,4 @@ public class ObjectFactory { return new Migration(); } - /** - * Create an instance of {@link Applications } - * - */ - public Applications createApplications() { - return new Applications(); - } - } diff --git a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java index 5e3eb421b..95d4df445 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java +++ b/src/main/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlReader.java @@ -15,6 +15,19 @@ public class MigrationXmlReader { private static final MigrationXmlReader INSTANCE = new MigrationXmlReader(); + /** + * Read and return a Migration from an xml document at the given resource path. + */ + public static Migration readMaybe(String resourcePath) { + + InputStream is = MigrationXmlReader.class.getResourceAsStream(resourcePath); + if (is == null) { + return null; + } + + return INSTANCE.read(is); + } + /** * Read and return a Migration from an xml document at the given resource path. */ diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java index 45f653898..fdcfd4732 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/CurrentModel.java @@ -2,7 +2,6 @@ package com.avaje.ebean.dbmigration.model; import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler; import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; -import com.avaje.ebean.dbmigration.ddlgeneration.platform.DdlNamingConvention; import com.avaje.ebean.dbmigration.migration.ChangeSet; import com.avaje.ebean.dbmigration.migration.Migration; import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; @@ -22,17 +21,22 @@ public class CurrentModel { private final SpiEbeanServer server; - private DdlNamingConvention namingConvention; - private ModelContainer model; + private ChangeSet changeSet; + private DdlWrite write; + /** + * Construct with a given EbeanServer instance. + */ public CurrentModel(SpiEbeanServer server) { this.server = server; - this.namingConvention = new DdlNamingConvention(); } + /** + * Return the current model by reading all the bean descriptors and properties. + */ public ModelContainer read() { if (model == null) { model = new ModelContainer(); @@ -48,6 +52,9 @@ public class CurrentModel { this.changeSet = changeSet; } + /** + * Return as a ChangeSet. + */ public ChangeSet getChangeSet() { read(); if (changeSet == null) { @@ -56,6 +63,9 @@ public class CurrentModel { return changeSet; } + /** + * Write as migration xml to the given file. + */ public void writeMigration(File file) { ChangeSet changeSet = getChangeSet(); @@ -66,6 +76,9 @@ public class CurrentModel { writer.write(migration, file); } + /** + * Return the 'Create' DDL. + */ public String getCreateDdl() throws IOException { createDdl(); @@ -78,6 +91,9 @@ public class CurrentModel { return ddl.toString(); } + /** + * Return the 'Drop' DDL. + */ public String getDropDdl() throws IOException { createDdl(); @@ -89,16 +105,9 @@ public class CurrentModel { return ddl.toString(); } - public DdlWrite generateDdl(ChangeSet changeSet) throws IOException { - - DdlWrite write = new DdlWrite(); - - DdlHandler handler = handler(); - handler.generate(write, changeSet); - - return write; - } - + /** + * Create all the DDL based on the changeSet. + */ private void createDdl() throws IOException { if (write == null) { @@ -111,8 +120,10 @@ public class CurrentModel { } } + /** + * Return the platform specific DdlHandler (to generate DDL). + */ private DdlHandler handler() { - return server.getDatabasePlatform().createDdlHandler(); } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java index 1be7074ae..7855d9920 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MColumn.java @@ -1,5 +1,6 @@ package com.avaje.ebean.dbmigration.model; +import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.Column; /** @@ -12,6 +13,7 @@ public class MColumn { private String checkConstraint; private String defaultValue; private String references; + private boolean historyExclude; private boolean notnull; private boolean primaryKey; private boolean identity; @@ -34,6 +36,7 @@ public class MColumn { this.primaryKey = Boolean.TRUE.equals(column.isPrimaryKey()); this.identity = Boolean.TRUE.equals(column.isIdentity()); this.unique = Boolean.TRUE.equals(column.isUnique()); + this.historyExclude = Boolean.TRUE.equals(column.isHistoryExclude()); } public MColumn(String name, String type) { @@ -103,6 +106,14 @@ public class MColumn { this.notnull = notnull; } + public boolean isHistoryExclude() { + return historyExclude; + } + + public void setHistoryExclude(boolean historyExclude) { + this.historyExclude = historyExclude; + } + public void setUnique(boolean unique) { this.unique = unique; } @@ -136,6 +147,7 @@ public class MColumn { if (uniqueOneToOne) c.setUniqueOneToOne(true); if (primaryKey) c.setPrimaryKey(true); if (identity) c.setIdentity(true); + if (historyExclude) c.setHistoryExclude(true); c.setCheckConstraint(checkConstraint); c.setReferences(references); @@ -143,4 +155,64 @@ public class MColumn { return c; } + + private boolean different(String val1, String val2) { + return (val1 == null) ? val2 != null : !val1.equals(val2); + } + + AlterColumn alterColumn; + + private AlterColumn getAlterColumn(String tableName) { + if (alterColumn == null) { + alterColumn = new AlterColumn(); + alterColumn.setColumnName(name); + alterColumn.setTableName(tableName); + } + return alterColumn; + } + + public void compare(ModelDiff modelDiff, MTable table, MColumn newColumn) { + + String tableName = table.getName(); + + this.alterColumn = null; + + if (different(type, newColumn.type)) { + getAlterColumn(tableName).setType(newColumn.type); + } + if (historyExclude != newColumn.historyExclude) { + getAlterColumn(tableName).setHistoryExclude(newColumn.historyExclude); + } + if (notnull != newColumn.notnull) { + getAlterColumn(tableName).setNotnull(newColumn.notnull); + } + if (different(defaultValue, newColumn.defaultValue)) { + AlterColumn alter = getAlterColumn(tableName); + alter.setOldDefaultValue(defaultValue); + alter.setNewDefaultValue(newColumn.defaultValue); + } + if (different(checkConstraint, newColumn.checkConstraint)) { + AlterColumn alter = getAlterColumn(tableName); + alter.setOldCheckConstraint(checkConstraint); + alter.setNewCheckConstraint(newColumn.checkConstraint); + } + if (different(references, newColumn.references)) { + AlterColumn alter = getAlterColumn(tableName); + alter.setOldReferences(references); + alter.setNewReferences(newColumn.references); + } + + if (unique != newColumn.unique) { + AlterColumn alter = getAlterColumn(tableName); + alter.setUnique(newColumn.unique); + } + if (uniqueOneToOne != newColumn.uniqueOneToOne) { + AlterColumn alter = getAlterColumn(tableName); + alter.setUniqueOneToOne(newColumn.uniqueOneToOne); + } + + if (alterColumn != null) { + modelDiff.addAlterColumn(alterColumn); + } + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java index 25e8b5781..950ed6416 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -8,9 +8,12 @@ import com.avaje.ebean.dbmigration.migration.IdentityType; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Holds the logical model for a given Table and everything associated to it. @@ -29,11 +32,6 @@ import java.util.Map; */ public class MTable { - /** - * Flag set to indicate - */ - private boolean matched; - private final String name; private String comment; @@ -60,6 +58,8 @@ public class MTable { private List compoundKeys = new ArrayList(); + private AddColumn addColumn; + /** * Construct for migration. */ @@ -110,12 +110,37 @@ public class MTable { return createTable; } - public boolean isMatched() { - return matched; - } + public void compare(ModelDiff modelDiff, MTable newTable) { + + // TODO: compare indexes? + // TODO: compare primary key + + addColumn = null; + + Set mappedColumns = new LinkedHashSet(); + + Collection newColumns = newTable.getColumns().values(); + for (MColumn newColumn : newColumns) { + MColumn localColumn = columns.get(newColumn.getName()); + if (localColumn == null) { + diffNewColumn(newColumn); + } else { + localColumn.compare(modelDiff, this, newColumn); + mappedColumns.add(newColumn.getName()); + } + } + + Collection existingColumns = columns.values(); + for (MColumn existingColumn : existingColumns) { + if (!mappedColumns.contains(existingColumn.getName())) { + diffDropColumn(modelDiff, existingColumn); + } + } + + if (addColumn != null) { + modelDiff.addAddColumn(addColumn); + } - public void setMatched(boolean matched) { - this.matched = matched; } /** @@ -293,4 +318,24 @@ public class MTable { addColumn(newCol); return newCol; } + + + private void diffNewColumn(MColumn newColumn) { + + if (addColumn == null) { + addColumn = new AddColumn(); + addColumn.setTableName(name); + } + + addColumn.getColumn().add(newColumn.createColumn()); + } + + private void diffDropColumn(ModelDiff modelDiff, MColumn existingColumn) { + + DropColumn dropColumn = new DropColumn(); + dropColumn.setTableName(name); + dropColumn.setColumnName(existingColumn.getName()); + + modelDiff.addDropColumn(dropColumn); + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java new file mode 100644 index 000000000..1446fdcb5 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java @@ -0,0 +1,93 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.Migration; +import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Build the model from the series of migrations. + */ +public class MigrationModel { + + private static final Logger logger = LoggerFactory.getLogger(MigrationModel.class); + + private final ModelContainer model = new ModelContainer(); + + private final Set readVersions = new LinkedHashSet(); + + private final String resourcePath; + + public MigrationModel(String resourcePath) { + this.resourcePath = normaliseResourcePath(resourcePath); + } + + private String normaliseResourcePath(String resourcePath) { + if (resourcePath.endsWith("/")) { + // trim trailing slash + resourcePath = resourcePath.substring(0, resourcePath.length()-1); + } + if (resourcePath.startsWith("/")) { + // trim leading slash + resourcePath = resourcePath.substring(1); + } + return resourcePath; + } + + /** + * Read all the migrations returning the model with all + * the migrations applied in version order. + */ + public ModelContainer read() { + + readMigrations(); + logger.info("read versions {}", readVersions); + return model; + } + + /** + * Return the set of versions that were read. + */ + public Set getReadVersions() { + return readVersions; + } + + private void readMigrations() { + for (int majorVersion = 1; majorVersion < 100; majorVersion++) { + if (!readMinorVersions(majorVersion)){ + // no major.0 version so stopping + return; + } + } + } + + private boolean readMinorVersions(int majorVersion) { + + for (int minorVersion = 0; minorVersion < 100; minorVersion++) { + if (!readMigration(majorVersion, minorVersion)) { + // continue reading next major if minorVersion 0 was read + return (minorVersion > 0); + } + } + return true; + } + + private boolean readMigration(int majorVersion, int minorVersion) { + + String version = majorVersion+"."+minorVersion; + String path = "/"+resourcePath+"/v"+version+".xml"; + + Migration migration = MigrationXmlReader.readMaybe(path); + if (migration == null) { + return false; + } + readVersions.add(version); + logger.trace("... read migration v{}", version); + model.apply(migration); + return true; + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java index 7eed356cf..c57b4f392 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -23,6 +23,9 @@ public class ModelContainer { */ private Map tables = new LinkedHashMap(); + public ModelContainer() { + + } /** * Return the map of all the tables. @@ -73,7 +76,7 @@ public class ModelContainer { protected void applyChange(CreateTable createTable) { String tableName = createTable.getName(); if (tables.containsKey(tableName)) { - throw new IllegalStateException("Table [" + tableName + "] already exists?"); + throw new IllegalStateException("Table [" + tableName + "] already exists in model?"); } MTable table = new MTable(createTable); tables.put(tableName, table); @@ -85,7 +88,7 @@ public class ModelContainer { protected void applyChange(AddColumn addColumn) { MTable table = tables.get(addColumn.getTableName()); if (table == null) { - throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist?"); + throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist in model?"); } table.apply(addColumn); } @@ -96,7 +99,7 @@ public class ModelContainer { protected void applyChange(DropColumn dropColumn) { MTable table = tables.get(dropColumn.getTableName()); if (table == null) { - throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist?"); + throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist in model?"); } table.apply(dropColumn); } 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 23be84b02..e09f027c7 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -1,5 +1,9 @@ package com.avaje.ebean.dbmigration.model; +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.AlterColumn; +import com.avaje.ebean.dbmigration.migration.DropColumn; + import java.util.ArrayList; import java.util.List; @@ -12,17 +16,17 @@ public class ModelDiff { /** * The base model to which we compare the newer model. */ - protected final ModelContainer baseModel; + private final ModelContainer baseModel; /** * List of 'create' type changes. */ - protected final List createChanges = new ArrayList(); + private final List createChanges = new ArrayList(); /** - * List of 'drop' type changes. Potential for putting into a separate changeSet. + * List of 'drop' type changes. Expected to be placed into a separate DDL script. */ - protected final List dropChanges = new ArrayList(); + private final List dropChanges = new ArrayList(); /** * Construct with a base model. @@ -77,7 +81,6 @@ public class ModelDiff { protected void addNewTable(MTable newTable) { createChanges.add(newTable.createTable()); -// createChanges.add(newTable.createForeignKeys()); } /** @@ -85,14 +88,19 @@ public class ModelDiff { */ protected void compareTables(MTable currentTable, MTable newTable) { - //TODO: compareTables() - // changed columns - // find additional columns - // find removed columns - // changes to indexes? - // changes to primary key - // changes to foreign key - // changes to unique constraints? + currentTable.compare(this, newTable); } + + public void addAlterColumn(AlterColumn alterColumn) { + createChanges.add(alterColumn); + } + + public void addDropColumn(DropColumn dropColumn) { + dropChanges.add(dropColumn); + } + + public void addAddColumn(AddColumn addColumn) { + createChanges.add(addColumn); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java b/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java index a79961e68..6c8407454 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DatabasePlatformFactory.java @@ -9,6 +9,7 @@ import javax.sql.DataSource; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.*; +import com.avaje.ebean.dbmigration.DbOffline; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +31,17 @@ public class DatabasePlatformFactory { try { + String offlinePlatform = DbOffline.getPlatform(); + if (offlinePlatform != null) { + logger.info("offline platform [{}]", offlinePlatform); + return byDatabaseName(offlinePlatform); + } + if (serverConfig.getDatabasePlatformName() != null) { // choose based on dbName return byDatabaseName(serverConfig.getDatabasePlatformName()); - } + if (serverConfig.getDataSourceConfig().isOffline()) { String m = "You must specify a DatabasePlatformName when you are offline"; throw new PersistenceException(m); diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java index da9f730f7..e6d847c03 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -11,6 +11,7 @@ import com.avaje.ebean.config.PropertyMap; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.UnderscoreNamingConvention; import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.dbmigration.DbOffline; import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.cache.DefaultServerCacheFactory; @@ -113,9 +114,6 @@ public class DefaultContainer implements SpiContainer { serverConfig.getDatabasePlatform().setDbEncrypt(serverConfig.getDbEncrypt()); } - DatabasePlatform dbPlatform = serverConfig.getDatabasePlatform(); - - // inform the NamingConvention of the associated DatabasePlaform serverConfig.getNamingConvention().setDatabasePlatform(serverConfig.getDatabasePlatform()); @@ -169,6 +167,7 @@ public class DefaultContainer implements SpiContainer { // start any services after registering with clusterManager server.start(); + DbOffline.reset(); return server; } } @@ -284,6 +283,11 @@ public class DefaultContainer implements SpiContainer { private DataSource getDataSourceFromConfig(ServerConfig config) { + if (DbOffline.isSet()) { + logger.trace("... DbOffline using platform [{}]", DbOffline.getPlatform()); + return null; + } + DataSource ds; if (config.getDataSourceJndiName() != null) { @@ -324,6 +328,10 @@ public class DefaultContainer implements SpiContainer { */ private boolean checkDataSource(ServerConfig serverConfig) { + if (DbOffline.isSet()) { + return false; + } + if (serverConfig.getDataSource() == null) { if (serverConfig.getDataSourceConfig().isOffline()) { // this is ok - offline DDL generation etc diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ConvertInetAddresses.java b/src/main/java/com/avaje/ebeaninternal/server/type/ConvertInetAddresses.java index d3d83dff9..2473e140a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/type/ConvertInetAddresses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ConvertInetAddresses.java @@ -185,7 +185,6 @@ public final class ConvertInetAddresses { boolean isIpv6 = false; // handle IPv6 forms of IPv4 addresses - // TODO: use Ascii.toUpperCase() when available if (ipString.toUpperCase(Locale.US).startsWith("::FFFF:")) { ipString = ipString.substring(7); } else if (ipString.startsWith("::")) { diff --git a/src/main/resources/ebean-dbmigration-1.0.xsd b/src/main/resources/ebean-dbmigration-1.0.xsd index fe72b0e0f..cbd6f4141 100644 --- a/src/main/resources/ebean-dbmigration-1.0.xsd +++ b/src/main/resources/ebean-dbmigration-1.0.xsd @@ -8,20 +8,20 @@ - - - - - - - + + + + + + + - - - - - - + + + + + + @@ -201,6 +201,24 @@ + + + + + + + + + + + + + + + + + + @@ -212,49 +230,49 @@ - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + @@ -263,13 +281,13 @@ - + - + @@ -285,7 +303,6 @@ - @@ -294,16 +311,17 @@ - - - - + - - + + + + + + @@ -312,23 +330,13 @@ - - - - - - - - - - diff --git a/src/test/java/com/avaje/ebean/config/DbMigrationConfigTest.java b/src/test/java/com/avaje/ebean/config/DbMigrationConfigTest.java new file mode 100644 index 000000000..061ff7987 --- /dev/null +++ b/src/test/java/com/avaje/ebean/config/DbMigrationConfigTest.java @@ -0,0 +1,23 @@ +package com.avaje.ebean.config; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.*; + + +public class DbMigrationConfigTest { + + + @Test + public void testLoad() { + + ServerConfig config = new ServerConfig(); + config.setName("h2other"); + config.loadFromProperties(); + + DbMigrationConfig migrationConfig = config.getMigrationConfig(); + + assertThat(migrationConfig.getAppName()).isEqualTo("myapp"); + assertThat(migrationConfig.getResourcePath()).isEqualTo("dbmigration/myapp"); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/DbMigrationTest.java b/src/test/java/com/avaje/ebean/dbmigration/DbMigrationTest.java new file mode 100644 index 000000000..36c61cc79 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/DbMigrationTest.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.dbmigration; + +import com.avaje.ebean.BaseTestCase; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.assertj.core.api.Assertions.*; + +public class DbMigrationTest extends BaseTestCase { + + private static final Logger logger = LoggerFactory.getLogger(DbMigrationTest.class); + + //@Ignore + @Test + public void writeCurrent() { + + logger.info("start"); + + DbOffline.asH2(); + DbMigration migration = new DbMigration(); + DbOffline.reset(); + + migration.writeCurrent(); + + assertThat(DbOffline.isSet()).isFalse(); + + logger.info("end"); + } + +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java index 89c1a5b38..3799057d6 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/migrationreader/MigrationXmlWriterTest.java @@ -12,7 +12,7 @@ public class MigrationXmlWriterTest { Logger logger = LoggerFactory.getLogger(MigrationXmlWriterTest.class); @Test - public void testWrite() throws Exception { + public void testReadWrite() throws Exception { Migration migration = MigrationXmlReader.read("/container/test-create-table.xml"); diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/MColumnTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/MColumnTest.java new file mode 100644 index 000000000..2b3f18078 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/MColumnTest.java @@ -0,0 +1,273 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AlterColumn; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.*; + + +public class MColumnTest { + + MTable table = new MTable("tab"); + + MColumn basic() { + return new MColumn("col", "integer"); + } + + ModelDiff diff() { + return new ModelDiff(); + } + + @Test + public void noDiff() throws Exception { + + ModelDiff diff = diff(); + basic().compare(diff, table, basic()); + + assertThat(diff.getCreateChanges()).isEmpty(); + assertThat(diff.getDropChanges()).isEmpty(); + } + + @Test + public void diffType() throws Exception { + + ModelDiff diff = diff(); + basic().compare(diff, table, new MColumn("col", "integer(8)")); + + assertChanges(diff); + AlterColumn alterColumn = getAlterColumn(diff); + assertThat(alterColumn.getType()).isEqualTo("integer(8)"); + } + + @Test + public void diffNotNull() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setNotnull(true); + basic().compare(diff, table, newCol); + + assertChanges(diff); + AlterColumn alterColumn = getAlterColumn(diff); + assertThat(alterColumn.isNotnull()).isEqualTo(true); + + assertThat(alterColumn.getType()).isNull(); + assertThat(alterColumn.isUnique()).isNull(); + assertThat(alterColumn.isUniqueOneToOne()).isNull(); + assertThat(alterColumn.getNewDefaultValue()).isNull(); + } + + @Test + public void diffCheckAdd() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setCheckConstraint("abc"); + basic().compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewCheckConstraint()).isEqualTo("abc"); + assertThat(getAlterColumn(diff).getOldCheckConstraint()).isNull(); + } + + @Test + public void diffCheckRemove() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + MColumn oldCol = basic(); + oldCol.setCheckConstraint("abc"); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewCheckConstraint()).isNull(); + assertThat(getAlterColumn(diff).getOldCheckConstraint()).isEqualTo("abc"); + } + + @Test + public void diffCheckChange() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setCheckConstraint("abc"); + MColumn oldCol = basic(); + oldCol.setCheckConstraint("d"); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewCheckConstraint()).isEqualTo("abc"); + assertThat(getAlterColumn(diff).getOldCheckConstraint()).isEqualTo("d"); + } + + @Test + public void diffDefaultValueAdd() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setDefaultValue("abc"); + basic().compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewDefaultValue()).isEqualTo("abc"); + assertThat(getAlterColumn(diff).getOldDefaultValue()).isNull(); + } + + @Test + public void diffDefaultValueRemove() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + MColumn oldCol = basic(); + oldCol.setDefaultValue("abc"); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewDefaultValue()).isNull(); + assertThat(getAlterColumn(diff).getOldDefaultValue()).isEqualTo("abc"); + } + + @Test + public void diffDefaultValueChange() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setDefaultValue("abc"); + MColumn oldCol = basic(); + oldCol.setDefaultValue("d"); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewDefaultValue()).isEqualTo("abc"); + assertThat(getAlterColumn(diff).getOldDefaultValue()).isEqualTo("d"); + } + + @Test + public void diffReferencesAdd() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setReferences("abc"); + basic().compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewReferences()).isEqualTo("abc"); + assertThat(getAlterColumn(diff).getOldReferences()).isNull(); + } + + + @Test + public void diffReferencesRemove() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + MColumn oldCol = basic(); + oldCol.setReferences("abc"); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewReferences()).isNull(); + assertThat(getAlterColumn(diff).getOldReferences()).isEqualTo("abc"); + } + + @Test + public void diffReferencesChange() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setReferences("abc"); + MColumn oldCol = basic(); + oldCol.setReferences("d"); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).getNewReferences()).isEqualTo("abc"); + assertThat(getAlterColumn(diff).getOldReferences()).isEqualTo("d"); + } + + @Test + public void diffUniqueAdd() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setUnique(true); + basic().compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).isUnique()).isEqualTo(true); + } + + @Test + public void diffUniqueRemove() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + MColumn oldCol = basic(); + oldCol.setUnique(true); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).isUnique()).isEqualTo(false); + } + + @Test + public void diffUniqueOneToOneAdd() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setUniqueOneToOne(true); + basic().compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).isUniqueOneToOne()).isEqualTo(true); + } + + @Test + public void diffUniqueOneToOneRemove() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + MColumn oldCol = basic(); + oldCol.setUniqueOneToOne(true); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).isUniqueOneToOne()).isEqualTo(false); + } + + @Test + public void diffHistoryExcludeAdd() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + newCol.setHistoryExclude(true); + basic().compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).isHistoryExclude()).isEqualTo(true); + } + + @Test + public void diffHistoryExcludeRemove() throws Exception { + + ModelDiff diff = diff(); + MColumn newCol = basic(); + MColumn oldCol = basic(); + oldCol.setHistoryExclude(true); + oldCol.compare(diff, table, newCol); + + assertChanges(diff); + assertThat(getAlterColumn(diff).isHistoryExclude()).isEqualTo(false); + } + + @NotNull + private AlterColumn getAlterColumn(ModelDiff diff) { + return (AlterColumn) diff.getCreateChanges().get(0); + } + + private void assertChanges(ModelDiff diff) { + assertThat(diff.getDropChanges()).isEmpty(); + assertThat(diff.getCreateChanges()).hasSize(1); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java new file mode 100644 index 000000000..13617e9e9 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java @@ -0,0 +1,110 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.AddColumn; +import com.avaje.ebean.dbmigration.migration.AlterColumn; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import org.junit.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +public class MTableTest { + + MTable base() { + MTable table = new MTable("tab"); + table.addColumn(new MColumn("id","bigint")); + table.addColumn(new MColumn("name","varchar(20)")); + table.addColumn(new MColumn("status","varchar(3)")); + + return table; + } + + MTable newTable() { + MTable table = new MTable("tab"); + table.addColumn(new MColumn("id","bigint")); + table.addColumn(new MColumn("name","varchar(20)")); + table.addColumn(new MColumn("comment","varchar(1000)")); + + return table; + } + + MTable newTableAdd2Columns() { + MTable table = new MTable("tab"); + table.addColumn(new MColumn("id","bigint")); + table.addColumn(new MColumn("name","varchar(20)")); + table.addColumn(new MColumn("status","varchar(3)")); + table.addColumn(new MColumn("comment","varchar(1000)")); + table.addColumn(new MColumn("note","varchar(2000)")); + return table; + } + + MTable newTableModifiedColumn() { + MColumn modCol = new MColumn("name", "varchar(30)");// modified type + modCol.setNotnull(true); + + MTable table = new MTable("tab"); + table.addColumn(modCol); + table.addColumn(new MColumn("id","bigint")); + table.addColumn(new MColumn("status","varchar(3)")); + return table; + } + + @Test + public void testCompare_addColumnDropColumn() throws Exception { + + ModelDiff diff = new ModelDiff(); + diff.compareTables(base(), newTable()); + + List createChanges = diff.getCreateChanges(); + assertThat(createChanges).hasSize(1); + AddColumn addColumn = (AddColumn)createChanges.get(0); + assertThat(addColumn.getColumn()).extracting("name").contains("comment"); + assertThat(addColumn.getColumn()).extracting("type").contains("varchar(1000)"); + + List dropChanges = diff.getDropChanges(); + assertThat(dropChanges).hasSize(1); + + DropColumn dropColumn = (DropColumn)dropChanges.get(0); + assertThat(dropColumn.getColumnName()).isEqualTo("status"); + assertThat(dropColumn.getTableName()).isEqualTo("tab"); + } + + @Test + public void testCompare_addTwoColumnsToSameTable() throws Exception { + + ModelDiff diff = new ModelDiff(); + diff.compareTables(base(), newTableAdd2Columns()); + + List createChanges = diff.getCreateChanges(); + assertThat(createChanges).hasSize(1); + + AddColumn addColumn = (AddColumn)createChanges.get(0); + assertThat(addColumn.getColumn()).extracting("name").contains("comment","note"); + assertThat(addColumn.getColumn()).extracting("type").contains("varchar(1000)","varchar(2000)"); + + assertThat(diff.getDropChanges()).hasSize(0); + + } + + @Test + public void testCompare_modifyColumn() throws Exception { + + ModelDiff diff = new ModelDiff(); + diff.compareTables(base(), newTableModifiedColumn()); + + List createChanges = diff.getCreateChanges(); + assertThat(createChanges).hasSize(1); + + AlterColumn alterColumn = (AlterColumn)createChanges.get(0); + assertThat(alterColumn.getColumnName()).isEqualTo("name"); + assertThat(alterColumn.getType()).isEqualTo("varchar(30)"); + assertThat(alterColumn.isNotnull()).isEqualTo(true); + assertThat(alterColumn.isUnique()).isNull(); + assertThat(alterColumn.getNewCheckConstraint()).isNull(); + assertThat(alterColumn.getNewReferences()).isNull(); + + assertThat(diff.getDropChanges()).hasSize(0); + + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/MigrationModelTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/MigrationModelTest.java new file mode 100644 index 000000000..1cc725e62 --- /dev/null +++ b/src/test/java/com/avaje/ebean/dbmigration/model/MigrationModelTest.java @@ -0,0 +1,39 @@ +package com.avaje.ebean.dbmigration.model; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.*; + +public class MigrationModelTest { + + @Test + public void testRead() throws Exception { + + MigrationModel migrationModel = new MigrationModel("dbmigration/app1"); + ModelContainer model = migrationModel.read(); + + assertThat(migrationModel.getReadVersions()).contains("1.0","1.1","2.0"); + assertThat(model.getTable("v10_table")).isNotNull(); + } + + @Test + public void testRead_leadingSlash() throws Exception { + + MigrationModel migrationModel = new MigrationModel("/dbmigration/app1"); + ModelContainer model = migrationModel.read(); + + assertThat(migrationModel.getReadVersions()).contains("1.0","1.1","2.0"); + assertThat(model.getTable("v10_table")).isNotNull(); + } + + @Test + public void testRead_trailingSlash() throws Exception { + + MigrationModel migrationModel = new MigrationModel("/dbmigration/app1/"); + ModelContainer model = migrationModel.read(); + + assertThat(migrationModel.getReadVersions()).contains("1.0","1.1","2.0"); + assertThat(model.getTable("v10_table")).isNotNull(); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java index 57fe09f4d..c60798008 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -32,7 +32,7 @@ public class ModelContainerApplyTest { model.apply(migration); MTable foo = model.getTable("foo"); - assertThat(foo.getRemarks()).isEqualTo("comment"); + assertThat(foo.getComment()).isEqualTo("comment"); assertThat(foo.getTablespace()).isEqualTo("fooSpace"); assertThat(foo.getIndexTablespace()).isEqualTo("fooIndexSpace"); assertThat(foo.getWithHistory()).isEqualTo(true); diff --git a/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTimeTest.java b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTimeTest.java new file mode 100644 index 000000000..948fee29a --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalDateTimeTest.java @@ -0,0 +1,43 @@ +package com.avaje.ebeaninternal.server.type; + +import com.avaje.ebean.config.JsonConfig; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDateTime; +import org.junit.Test; + +import java.sql.Timestamp; + +import static org.junit.Assert.*; + +public class ScalarTypeJodaLocalDateTimeTest { + + ScalarTypeJodaLocalDateTime type = new ScalarTypeJodaLocalDateTime(JsonConfig.DateTime.ISO8601); + + @Test + public void testConvertFromTimestamp() throws Exception { + + long now = System.currentTimeMillis(); + Timestamp nowTs = new Timestamp(now); + + LocalDateTime ldt1 = type.convertFromTimestamp(nowTs); + LocalDateTime ldt2 = localConvertFromTimestamp(nowTs); + + assertEquals(ldt1, ldt2); + + Timestamp ts1 = type.convertToTimestamp(ldt1); + Timestamp ts2 = localConvertToTimestamp(ldt2); + + assertEquals(ts1, ts2); + } + + + LocalDateTime localConvertFromTimestamp(Timestamp ts) { + return new LocalDateTime(ts.getTime(), DateTimeZone.getDefault()); + } + + + Timestamp localConvertToTimestamp(LocalDateTime t) { + return new Timestamp(t.toDateTime(DateTimeZone.getDefault()).getMillis()); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTimeTest.java b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTimeTest.java new file mode 100644 index 000000000..952fc765c --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypeJodaLocalTimeTest.java @@ -0,0 +1,32 @@ +package com.avaje.ebeaninternal.server.type; + +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDateTime; +import org.junit.Test; + +import java.sql.Timestamp; + +import static org.junit.Assert.assertEquals; + +public class ScalarTypeJodaLocalTimeTest { + + @Test + public void test() { + + long now = System.currentTimeMillis(); + + //DateTimeZone timeZone = DateTimeZone.getDefault(); + //ISOChronology instance = ISOChronology.getInstance(); + + LocalDateTime ldt1 = new LocalDateTime(now, DateTimeZone.getDefault()); + LocalDateTime ldt2 = new LocalDateTime(now); + + assertEquals(ldt1, ldt2); + + Timestamp ts1 = new Timestamp(ldt1.toDateTime(DateTimeZone.getDefault()).getMillis()); + Timestamp ts2 = new Timestamp(ldt2.toDateTime().getMillis()); + + assertEquals(ts1, ts2); + + } +} \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/v1.0.xml b/src/test/resources/dbmigration/app1/v1.0.xml index 2430f2933..2bf781566 100644 --- a/src/test/resources/dbmigration/app1/v1.0.xml +++ b/src/test/resources/dbmigration/app1/v1.0.xml @@ -13,7 +13,7 @@ - + @@ -21,17 +21,6 @@ - - - - - - - - - - - @@ -47,7 +36,7 @@ - + diff --git a/src/test/resources/dbmigration/app1/v1.1.xml b/src/test/resources/dbmigration/app1/v1.1.xml index ee8e19b74..bf9bef4e2 100644 --- a/src/test/resources/dbmigration/app1/v1.1.xml +++ b/src/test/resources/dbmigration/app1/v1.1.xml @@ -13,7 +13,7 @@ - + @@ -21,34 +21,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/test/resources/dbmigration/app1/v10.0.xml b/src/test/resources/dbmigration/app1/v10.0.xml deleted file mode 100644 index 382686d17..000000000 --- a/src/test/resources/dbmigration/app1/v10.0.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - create table foo; - - - drop table if exists foo; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/dbmigration/app1/v2.0.xml b/src/test/resources/dbmigration/app1/v2.0.xml index 800f3c876..5e5bb7815 100644 --- a/src/test/resources/dbmigration/app1/v2.0.xml +++ b/src/test/resources/dbmigration/app1/v2.0.xml @@ -13,39 +13,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/test/resources/dbmigration/app1/v9.0.xml b/src/test/resources/dbmigration/app1/v9.0.xml deleted file mode 100644 index 800f3c876..000000000 --- a/src/test/resources/dbmigration/app1/v9.0.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - create table foo; - - - drop table if exists foo; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/dbmigration/applications.xml b/src/test/resources/dbmigration/applications.xml deleted file mode 100644 index 9c01f1b8f..000000000 --- a/src/test/resources/dbmigration/applications.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 8a6a405f1..4d273cc87 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -23,7 +23,7 @@ ebean.autofetch.traceUsageCollection=false ebean.ddl.generate=true ebean.ddl.run=true -datasource.default=ms +datasource.default=h2 ebean.persistBatch=NONE ebean.persistBatchOnCascade=ALL @@ -46,6 +46,9 @@ ebean.cacheWarmingDelay=-1 ##ebean.transaction.rollbackOnChecked=false +ebean.migration.appName=myapp +ebean.migration.resourcePath=dbmigration/myapp + ## ------------------------------------------------------------- ## DataSources (If using default Ebean DataSourceFactory) diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 7e2ff7fee..816e4a15b 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -33,7 +33,7 @@ - +