diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index a0acc863f..b2ea28090 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -70,12 +70,6 @@ ${ebean-datasource.version} - - io.ebean - ebean-migration - 12.1.4 - - com.fasterxml.jackson.core diff --git a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java index d8e6541bb..7ea8e56e9 100644 --- a/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java +++ b/ebean-api/src/main/java/io/ebean/config/DatabaseConfig.java @@ -3,6 +3,7 @@ package io.ebean.config; import com.fasterxml.jackson.core.JsonFactory; import io.avaje.config.Config; import io.ebean.DatabaseFactory; +import io.ebean.EbeanVersion; import io.ebean.PersistenceContextScope; import io.ebean.Query; import io.ebean.Transaction; @@ -28,12 +29,13 @@ import io.ebean.event.changelog.ChangeLogPrepare; import io.ebean.event.changelog.ChangeLogRegister; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; -import io.ebean.migration.MigrationRunner; import io.ebean.util.StringHelper; import javax.persistence.EnumType; import javax.sql.DataSource; import java.time.Clock; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -277,6 +279,25 @@ public class DatabaseConfig { private String ddlSeedSql; + private String ddlHeader; + + /** + * Mode used to check non-null columns added via migration have a default value specified etc. + */ + private boolean ddlStrictMode = true; + + /** + * Comma and equals delimited key/value placeholders to replace in DDL scripts. + */ + private String ddlPlaceholders; + + /** + * Map of key/value placeholders to replace in DDL scripts. + */ + private Map ddlPlaceholderMap; + + private boolean runMigration; + /** * When true L2 bean cache use is skipped after a write has occurred on a transaction. */ @@ -322,11 +343,6 @@ public class DatabaseConfig { */ private String dbSchema; - /** - * The db migration config (migration resource path etc). - */ - private DbMigrationConfig migrationConfig = new DbMigrationConfig(); - /** * The ClassLoadConfig used to detect Joda, Java8, Jackson etc and create plugin instances given a className. */ @@ -1212,20 +1228,6 @@ public class DatabaseConfig { this.dbSchema = dbSchema; } - /** - * 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 Geometry SRID. */ @@ -2026,7 +2028,15 @@ public class DatabaseConfig { * as it is often the only thing we need to configure for migrations. */ public void setRunMigration(boolean runMigration) { - migrationConfig.setRunMigration(runMigration); + this.runMigration = runMigration; + } + + /** + * Return true if the DB migration should run on server start. + */ + public boolean isRunMigration() { + final String run = System.getProperty("ebean.migration.run"); + return (run != null) ? Boolean.parseBoolean(run) : runMigration; } /** @@ -2134,6 +2144,67 @@ public class DatabaseConfig { return ddlExtra; } + /** + * Set the header to use with DDL generation. + */ + public void setDdlHeader(String ddlHeader) { + this.ddlHeader = ddlHeader; + } + + /** + * Return the header to use with DDL generation. + */ + public String getDdlHeader() { + if (ddlHeader != null && !ddlHeader.isEmpty()) { + String header = ddlHeader.replace("${version}", EbeanVersion.getVersion()); + header = header.replace("${timestamp}", ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT)); + return header; + } + return ddlHeader; + } + + /** + * Return true if strict mode is used which includes a check that non-null columns have a default value. + */ + public boolean isDdlStrictMode() { + return ddlStrictMode; + } + + /** + * Set to false to turn off strict mode allowing non-null columns to not have a default value. + */ + public void setDdlStrictMode(boolean ddlStrictMode) { + this.ddlStrictMode = ddlStrictMode; + } + + /** + * Return a comma and equals delimited placeholders that are substituted in DDL scripts. + */ + public String getDdlPlaceholders() { + return ddlPlaceholders; + } + + /** + * Set a comma and equals delimited placeholders that are substituted in DDL scripts. + */ + public void setDdlPlaceholders(String ddlPlaceholders) { + this.ddlPlaceholders = ddlPlaceholders; + } + + /** + * Return a map of placeholder values that are substituted in DDL scripts. + */ + public Map getDdlPlaceholderMap() { + return ddlPlaceholderMap; + } + + /** + * Set a map of placeholder values that are substituted in DDL scripts. + */ + public void setDdlPlaceholderMap(Map ddlPlaceholderMap) { + this.ddlPlaceholderMap = ddlPlaceholderMap; + } + /** * Return true if the class path search should be disabled. */ @@ -2699,13 +2770,8 @@ public class DatabaseConfig { * Load the configuration settings from the properties file. */ protected void loadSettings(PropertiesWrapper p) { - dbSchema = p.get("dbSchema", dbSchema); - if (dbSchema != null) { - migrationConfig.setDefaultDbSchema(dbSchema); - } profilingConfig.loadSettings(p, name); - migrationConfig.loadSettings(p, name); platformConfig.loadSettings(p); if (platformConfig.isAllQuotedIdentifiers()) { adjustNamingConventionForAllQuoted(); @@ -2807,12 +2873,16 @@ public class DatabaseConfig { jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime); jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate); + runMigration = p.getBoolean("migration.run", runMigration); ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate); ddlRun = p.getBoolean("ddl.run", ddlRun); ddlExtra = p.getBoolean("ddl.extra", ddlExtra); ddlCreateOnly = p.getBoolean("ddl.createOnly", ddlCreateOnly); ddlInitSql = p.get("ddl.initSql", ddlInitSql); ddlSeedSql = p.get("ddl.seedSql", ddlSeedSql); + ddlStrictMode = p.getBoolean("ddl.strictMode", ddlStrictMode); + ddlPlaceholders = p.get("ddl.placeholders", ddlPlaceholders); + ddlHeader = p.get("ddl.header", ddlHeader); // read tenant-configuration from config: // tenant.mode = NONE | DB | SCHEMA | CATALOG | PARTITION @@ -3034,17 +3104,6 @@ public class DatabaseConfig { this.queryPlanTTLSeconds = queryPlanTTLSeconds; } - /** - * Run the DB migration against the DataSource. - */ - public DataSource runDbMigration(DataSource dataSource) { - if (migrationConfig.isRunMigration()) { - MigrationRunner runner = migrationConfig.createRunner(getClassLoadConfig().getClassLoader(), properties); - runner.run(dataSource); - } - return dataSource; - } - /** * Create a new PlatformConfig based of the one held but with overridden properties by reading * properties with the given path and prefix. diff --git a/ebean-api/src/main/java/io/ebean/config/DbMigrationConfig.java b/ebean-api/src/main/java/io/ebean/config/DbMigrationConfig.java deleted file mode 100644 index 1790b05f5..000000000 --- a/ebean-api/src/main/java/io/ebean/config/DbMigrationConfig.java +++ /dev/null @@ -1,508 +0,0 @@ -package io.ebean.config; - -import io.ebean.EbeanVersion; -import io.ebean.annotation.Platform; -import io.ebean.migration.MigrationConfig; -import io.ebean.migration.MigrationRunner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Map; -import java.util.Properties; - -import static io.ebean.util.StringHelper.replace; - -/** - * Configuration for the DB migration processing. - */ -public class DbMigrationConfig { - - protected static final Logger logger = LoggerFactory.getLogger(DbMigrationConfig.class); - - protected MigrationConfig runnerConfig = new MigrationConfig(); - - /** - * The database platform to generate migration DDL for. - */ - protected Platform platform; - - /** - * Resource path for the migration xml and sql. - */ - protected String migrationPath = "dbmigration"; - - protected String migrationInitPath = "dbinit"; - - /** - * Subdirectory the model xml files go into. - */ - protected String modelPath = "model"; - - protected String applySuffix = ".sql"; - - /** - * Set this to "V" to be compatible with FlywayDB. - */ - protected String applyPrefix = ""; - - protected String modelSuffix = ".model.xml"; - - /** - * For running migration the DB table that holds migration execution status. - */ - protected String metaTable = "db_migration"; - - /** - * Flag set to true means to run any outstanding migrations on startup. - */ - protected boolean runMigration; - - /** - * Comma and equals delimited key/value placeholders to replace in DDL scripts. - */ - protected String runPlaceholders; - - /** - * Map of key/value placeholders to replace in DDL scripts. - */ - protected Map runPlaceholderMap; - - /** - * DB schema used for the migration (and testing). - */ - protected String dbSchema; - - /** - * Set to true if we consider this the 'default schema' (Postgres schema that matches DB username) - */ - protected boolean defaultDbSchema; - - /** - * DB user used to run the DB migration. - */ - protected String dbUsername; - - /** - * DB password used to run the DB migration. - */ - protected String dbPassword; - - protected String patchInsertOn; - - protected String patchResetChecksumOn; - - /** - * Mode used to check non-null columns added via migration have a default value specified etc. - */ - protected boolean strictMode = true; - - /** - * Contains the DDL-header information. - */ - protected String ddlHeader; - - /** - * Return the DB platform to generate migration DDL for. - *

- * We typically need to explicitly specify this as migration can often be generated - * when running against H2. - */ - public Platform getPlatform() { - return platform; - } - - /** - * Set the DB platform to generate migration DDL for. - */ - public void setPlatform(Platform platform) { - this.platform = platform; - } - - /** - * Return the path for normal migrations or dbinit migrations. - * - * @param dbinitMigration When true return the path for dbinit migrations. - */ - public String getMigrationPath(boolean dbinitMigration) { - return dbinitMigration ? migrationInitPath : migrationPath; - } - - /** - * Return the resource path for db migrations. - */ - public String getMigrationPath() { - return migrationPath; - } - - /** - * Set the resource path for db migrations. - *

- * The default of "dbmigration" is reasonable in most cases. You may look to set this - * to be something like "dbmigration/myapp" where myapp gives it a unique resource path - * in the case there are multiple Database applications in the single classpath. - *

- */ - public void setMigrationPath(String migrationPath) { - this.migrationPath = migrationPath; - } - - /** - * Return the relative path for the model files (defaults to model). - */ - public String getModelPath() { - return modelPath; - } - - /** - * Set the relative path for the model files. - */ - public void setModelPath(String modelPath) { - this.modelPath = modelPath; - } - - /** - * Return the model suffix (defaults to model.xml) - */ - public String getModelSuffix() { - return modelSuffix; - } - - /** - * Set the model suffix. - */ - public void setModelSuffix(String modelSuffix) { - this.modelSuffix = modelSuffix; - } - - /** - * Return the apply script suffix (defaults to sql). - */ - public String getApplySuffix() { - return applySuffix; - } - - /** - * Set the apply script suffix (defaults to sql). - */ - public void setApplySuffix(String applySuffix) { - this.applySuffix = applySuffix; - } - - /** - * Return the apply prefix. - */ - public String getApplyPrefix() { - return applyPrefix; - } - - /** - * Set the apply prefix. This might be set to "V" for use with FlywayDB. - */ - public void setApplyPrefix(String applyPrefix) { - this.applyPrefix = applyPrefix; - } - - /** - * Return the table name that holds the migration run details - * (used by DB Migration runner only). - */ - public String getMetaTable() { - return metaTable; - } - - /** - * Set the table name that holds the migration run details - * (used by DB Migration runner only). - */ - public void setMetaTable(String metaTable) { - this.metaTable = metaTable; - } - - /** - * Return a comma and equals delimited placeholders that are substituted in SQL scripts when running migration - * (used by DB Migration runner only). - */ - public String getRunPlaceholders() { - // environment properties take precedence - String placeholders = readEnvironment("ddl.migration.placeholders"); - if (placeholders != null) { - return placeholders; - } - return runPlaceholders; - } - - /** - * Set a comma and equals delimited placeholders that are substituted in SQL scripts when running migration - * (used by DB Migration runner only). - */ - public void setRunPlaceholders(String runPlaceholders) { - this.runPlaceholders = runPlaceholders; - } - - /** - * Return a map of placeholder values that are substituted in SQL scripts when running migration - * (used by DB Migration runner only). - */ - public Map getRunPlaceholderMap() { - return runPlaceholderMap; - } - - /** - * Set a map of placeholder values that are substituted when running migration - * (used by DB Migration runner only). - */ - public void setRunPlaceholderMap(Map runPlaceholderMap) { - this.runPlaceholderMap = runPlaceholderMap; - } - - /** - * Return true if the DB migration should be run on startup. - */ - public boolean isRunMigration() { - // environment properties take precedence - String run = readEnvironment("ddl.migration.run"); - if (run != null) { - return "true".equalsIgnoreCase(run.trim()); - } - return runMigration; - } - - /** - * Set to true to run the DB migration on startup. - */ - public void setRunMigration(boolean runMigration) { - this.runMigration = runMigration; - } - - /** - * Return the DB username to use for running DB migrations. - */ - public String getDbUsername() { - // environment properties take precedence - String user = readEnvironment("ddl.migration.user"); - if (user != null) { - return user; - } - return dbUsername; - } - - /** - * Set the DB username to use for running DB migrations. - */ - public void setDbUsername(String dbUsername) { - this.dbUsername = dbUsername; - } - - /** - * Return the DB password to use for running DB migrations. - */ - public String getDbPassword() { - String user = readEnvironment("ddl.migration.password"); - if (user != null) { - return user; - } - return dbPassword; - } - - /** - * Set the DB password to use for running DB migrations. - */ - public void setDbPassword(String dbPassword) { - this.dbPassword = dbPassword; - } - - /** - * Return the DB schema to use (for migration, testing etc). - */ - public String getDbSchema() { - String schema = readEnvironment("ddl.migration.schema"); - if (schema != null) { - return schema; - } - return dbSchema; - } - - /** - * Set the Db schema to use. - */ - public void setDbSchema(String dbSchema) { - this.dbSchema = dbSchema; - } - - /** - * Set the Db schema if it hasn't already been defined. - */ - public void setDefaultDbSchema(String dbSchema) { - this.defaultDbSchema = true; - this.dbSchema = dbSchema; - } - - /** - * Return true if this is considered the default DB schema (Postgres schema matching DB username). - */ - public boolean isDefaultDbSchema() { - return defaultDbSchema; - } - - /** - * Return migration versions that should be added to history without running. - */ - public String getPatchInsertOn() { - return patchInsertOn; - } - - /** - * Set migration versions that should be added to history without running. - *

- * Value can be a string containing comma delimited list of version numbers. - *

- */ - public void setPatchInsertOn(String patchInsertOn) { - this.patchInsertOn = patchInsertOn; - } - - /** - * Return migration versions that should have their checksum reset and not run. - */ - public String getPatchResetChecksumOn() { - return patchResetChecksumOn; - } - - /** - * Returns a DDL header prepend for each DDL. E.g. for copyright headers - * You can use placeholders like ${version} or ${timestamp} in properties file. - */ - public String getDdlHeader() { - if (ddlHeader != null && !ddlHeader.isEmpty()) { - ddlHeader = replace(ddlHeader, "${version}", EbeanVersion.getVersion()); - ddlHeader = replace(ddlHeader, "${timestamp}", ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT )); - } - return ddlHeader; - } - - /** - * Set the header prepended to the DDL. - */ - public void setDdlHeader(String ddlHeader) { - this.ddlHeader = ddlHeader; - } - - /** - * Set migration versions that should have their checksum reset and not run. - *

- * Value can be a string containing comma delimited list of version numbers. - *

- */ - public void setPatchResetChecksumOn(String patchResetChecksumOn) { - this.patchResetChecksumOn = patchResetChecksumOn; - } - - /** - * Return true if strict mode is used which includes a check that non-null columns have a default value. - */ - public boolean isStrictMode() { - String envValue = readEnvironment("ddl.migration.strictMode"); - if (!isEmpty(envValue)) { - return Boolean.parseBoolean(envValue.trim()); - } - return strictMode; - } - - /** - * Set to false to turn off strict mode allowing non-null columns to not have a default value. - */ - public void setStrictMode(boolean strictMode) { - this.strictMode = strictMode; - } - - /** - * Return the underlying migration runner configuration allowing for more advanced settings. - */ - public MigrationConfig getRunnerConfig() { - return runnerConfig; - } - - /** - * Load the settings from the PropertiesWrapper. - */ - public void loadSettings(PropertiesWrapper properties, String serverName) { - - migrationPath = properties.get("migration.migrationPath", migrationPath); - migrationInitPath = properties.get("migration.migrationInitPath", migrationInitPath); - modelPath = properties.get("migration.modelPath", modelPath); - applyPrefix = properties.get("migration.applyPrefix", applyPrefix); - applySuffix = properties.get("migration.applySuffix", applySuffix); - modelSuffix = properties.get("migration.modelSuffix", modelSuffix); - - platform = properties.getEnum(Platform.class, "migration.platform", platform); - patchInsertOn = properties.get("migration.patchInsertOn", patchInsertOn); - patchResetChecksumOn = properties.get("migration.patchResetChecksumOn", patchResetChecksumOn); - - runMigration = properties.getBoolean("migration.run", runMigration); - metaTable = properties.get("migration.metaTable", metaTable); - runPlaceholders = properties.get("migration.placeholders", runPlaceholders); - dbSchema = properties.get("migration.dbSchema", dbSchema); - - //Do not set user and pass from "datasource.db.username" - //There is a null test in MigrationRunner::getConnection to handle this - //String adminUser = properties.get("datasource." + serverName + ".username", dbUsername); - String adminUser = properties.get("datasource." + serverName + ".adminusername", dbUsername); - dbUsername = properties.get("migration.dbusername", adminUser); - - //String adminPwd = properties.get("datasource." + serverName + ".password", dbPassword); - String adminPwd = properties.get("datasource." + serverName + ".adminpassword", dbPassword); - dbPassword = properties.get("migration.dbpassword", adminPwd); - ddlHeader = properties.get("ddl.header", ddlHeader); - } - - /** - * Return the system or environment property. - */ - protected String readEnvironment(String key) { - - String val = System.getProperty(key); - if (val == null) { - val = System.getenv(key); - } - return val; - } - - /** - * Return true if the string is null or empty. - */ - protected boolean isEmpty(String val) { - return val == null || val.trim().isEmpty(); - } - - /** - * Create the MigrationRunner to run migrations if necessary. - */ - public MigrationRunner createRunner(ClassLoader classLoader, Properties properties) { - - runnerConfig.setMetaTable(metaTable); - runnerConfig.setApplySuffix(applySuffix); - runnerConfig.setMigrationPath(migrationPath); - runnerConfig.setMigrationInitPath(migrationInitPath); - runnerConfig.setRunPlaceholderMap(runPlaceholderMap); - runnerConfig.setRunPlaceholders(runPlaceholders); - runnerConfig.setDbUsername(getDbUsername()); - runnerConfig.setDbPassword(getDbPassword()); - runnerConfig.setDbSchema(getDbSchema()); - if (defaultDbSchema) { - runnerConfig.setSetCurrentSchema(false); - } - runnerConfig.setClassLoader(classLoader); - if (patchInsertOn != null) { - runnerConfig.setPatchInsertOn(patchInsertOn); - } - if (patchResetChecksumOn != null) { - runnerConfig.setPatchResetChecksumOn(patchResetChecksumOn); - } - if (properties != null) { - runnerConfig.load(properties); - } - return new MigrationRunner(runnerConfig); - } -} diff --git a/ebean-core/pom.xml b/ebean-core/pom.xml index f80758628..4511ccfa9 100644 --- a/ebean-core/pom.xml +++ b/ebean-core/pom.xml @@ -54,11 +54,30 @@ 1.0
- + + io.avaje + classpath-scanner + 4.2 + + + + io.ebean + ebean-migration-auto + 1.0 + + + + + io.ebean + ebean-migration + 12.2.0 + test + + io.ebean ebean-ddlgen - 12.5.0 + 12.5.2A test diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index f683538ec..ae6549457 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -61,6 +61,7 @@ import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetricVisitor; import io.ebean.meta.QueryPlanInit; import io.ebean.meta.QueryPlanRequest; +import io.ebean.migration.auto.AutoMigrationRunner; import io.ebean.plugin.BeanType; import io.ebean.plugin.Plugin; import io.ebean.plugin.Property; @@ -397,8 +398,17 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { * Start any services after registering with the ClusterManager. */ public void start() { - if (TenantMode.DB != config.getTenantMode()) { - config.runDbMigration(config.getDataSource()); + if (config.isRunMigration() && TenantMode.DB != config.getTenantMode()) { + final AutoMigrationRunner migrationRunner = config.service(AutoMigrationRunner.class); + if (migrationRunner == null) { + throw new IllegalStateException("No AutoMigrationRunner found. Probably ebean-migration is not in the classpath?"); + } + final String dbSchema = config.getDbSchema(); + if (dbSchema != null) { + migrationRunner.setDefaultDbSchema(dbSchema); + } + migrationRunner.loadProperties(config.getProperties()); + migrationRunner.run(config.getDataSource()); } } diff --git a/ebean-core/src/test/java/io/ebean/config/DbMigrationConfigTest.java b/ebean-core/src/test/java/io/ebean/config/DbMigrationConfigTest.java index 95464ec72..80f3e7c64 100644 --- a/ebean-core/src/test/java/io/ebean/config/DbMigrationConfigTest.java +++ b/ebean-core/src/test/java/io/ebean/config/DbMigrationConfigTest.java @@ -3,7 +3,7 @@ package io.ebean.config; import org.junit.Test; import java.util.Properties; - +import io.ebean.migration.MigrationConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -13,12 +13,13 @@ public class DbMigrationConfigTest { @Test public void testLoad() { - ServerConfig config = new ServerConfig(); + DatabaseConfig config = new DatabaseConfig(); config.setName("h2other"); config.loadFromProperties(); config.setDefaultServer(false); - DbMigrationConfig migrationConfig = config.getMigrationConfig(); + MigrationConfig migrationConfig = new MigrationConfig(); + migrationConfig.load(config.getProperties()); assertThat(migrationConfig.getMigrationPath()).isEqualTo("dbmigration/myapp"); } @@ -27,20 +28,18 @@ public class DbMigrationConfigTest { public void loadProperties_migration() { Properties properties = new Properties(); - properties.setProperty("ebean.migration.dbusername", "banana"); - properties.setProperty("ebean.migration.dbpassword", "apple"); + properties.setProperty("ebean.migration.username", "banana"); + properties.setProperty("ebean.migration.password", "apple"); properties.setProperty("ebean.migration.patchInsertOn", "1.3,my_views"); properties.setProperty("ebean.migration.patchResetChecksumOn", "foo"); - PropertiesWrapper wrapper = new PropertiesWrapper("ebean", "db", properties, null); - - DbMigrationConfig migrationConfig = new DbMigrationConfig(); - migrationConfig.loadSettings(wrapper, "db"); + MigrationConfig migrationConfig = new MigrationConfig(); + migrationConfig.load(properties); assertEquals(migrationConfig.getDbUsername(),"banana"); assertEquals(migrationConfig.getDbPassword(),"apple"); - assertEquals(migrationConfig.getPatchInsertOn(),"1.3,my_views"); - assertEquals(migrationConfig.getPatchResetChecksumOn(),"foo"); + assertThat(migrationConfig.getPatchInsertOn()).containsOnly("1.3","my_views"); + assertThat(migrationConfig.getPatchResetChecksumOn()).containsOnly("foo"); } @Test @@ -50,29 +49,12 @@ public class DbMigrationConfigTest { properties.setProperty("datasource.db.username", "banana"); properties.setProperty("datasource.db.password", "apple"); - PropertiesWrapper wrapper = new PropertiesWrapper("ebean", "db", properties, null); - - DbMigrationConfig migrationConfig = new DbMigrationConfig(); - migrationConfig.loadSettings(wrapper, "db"); + MigrationConfig migrationConfig = new MigrationConfig(); + migrationConfig.load(properties); // runnerConfig will fall back itsel to the correct password assertEquals(migrationConfig.getDbUsername(),null); assertEquals(migrationConfig.getDbPassword(),null); } - @Test - public void loadProperties_datasource_adminusername() { - - Properties properties = new Properties(); - properties.setProperty("datasource.db.adminusername", "banana"); - properties.setProperty("datasource.db.adminpassword", "apple"); - - PropertiesWrapper wrapper = new PropertiesWrapper("ebean", "db", properties, null); - - DbMigrationConfig migrationConfig = new DbMigrationConfig(); - migrationConfig.loadSettings(wrapper, "db"); - - assertEquals(migrationConfig.getDbUsername(),"banana"); - assertEquals(migrationConfig.getDbPassword(),"apple"); - } } diff --git a/ebean-ddlgen/pom.xml b/ebean-ddlgen/pom.xml index 6339c9cb3..bafb0c6d9 100644 --- a/ebean-ddlgen/pom.xml +++ b/ebean-ddlgen/pom.xml @@ -10,6 +10,7 @@ ebean ddl generation DDL and DB Migration generation ebean-ddlgen + @@ -19,6 +20,12 @@ 1.0 + + io.ebean + ebean-migration + 12.2.0 + + io.ebean ebean-core diff --git a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java index d2c800ff2..fe546ab27 100644 --- a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java +++ b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DdlGenerator.java @@ -2,7 +2,6 @@ package io.ebeaninternal.dbmigration; import io.ebean.annotation.Platform; import io.ebean.config.DatabaseConfig; -import io.ebean.config.DbMigrationConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.ddlrunner.DdlRunner; import io.ebean.ddlrunner.ScriptTransform; @@ -76,7 +75,7 @@ public class DdlGenerator implements SpiDdlGenerator { this.runDdl = config.isDdlRun(); this.ddlAutoCommit = databasePlatform.isDdlAutoCommit(); } - this.scriptTransform = createScriptTransform(config.getMigrationConfig()); + this.scriptTransform = createScriptTransform(config); this.baseDir = initBaseDir(); } @@ -366,8 +365,8 @@ public class DdlGenerator implements SpiDdlGenerator { /** * Create the ScriptTransform for placeholder key/value replacement. */ - private ScriptTransform createScriptTransform(DbMigrationConfig config) { - return ScriptTransform.build(config.getRunPlaceholders(), config.getRunPlaceholderMap()); + private ScriptTransform createScriptTransform(DatabaseConfig config) { + return ScriptTransform.build(config.getDdlPlaceholders(), config.getDdlPlaceholderMap()); } } diff --git a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 7fd496b22..1c790c607 100644 --- a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -6,8 +6,8 @@ import io.ebean.EbeanServer; import io.ebean.annotation.Platform; import io.ebean.config.DatabaseConfig; import io.ebean.config.DbConstraintNaming; -import io.ebean.config.DbMigrationConfig; import io.ebean.config.PlatformConfig; +import io.ebean.config.PropertiesWrapper; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.config.dbplatform.clickhouse.ClickHousePlatform; import io.ebean.config.dbplatform.cockroach.CockroachPlatform; @@ -28,7 +28,6 @@ import io.ebean.config.dbplatform.sqlite.SQLitePlatform; import io.ebean.config.dbplatform.sqlserver.SqlServer16Platform; import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform; import io.ebean.dbmigration.DbMigration; -import io.ebean.migration.MigrationVersion; import io.ebeaninternal.api.DbOffline; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; @@ -52,6 +51,7 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Properties; import static io.ebeaninternal.api.PlatformMatch.matchPlatform; @@ -93,11 +93,12 @@ public class DefaultDbMigration implements DbMigration { protected SpiEbeanServer server; - protected DbMigrationConfig migrationConfig; - protected String pathToResources = "src/main/resources"; - protected String migrationPath; + protected String migrationPath = "dbmigration"; + protected String migrationInitPath = "dbinit"; + protected String modelPath = "model"; + protected String modelSuffix = ".model.xml"; protected DatabasePlatform databasePlatform; @@ -112,7 +113,7 @@ public class DefaultDbMigration implements DbMigration { protected Boolean strictMode; protected Boolean includeGeneratedFileComment; protected String header; - protected String applyPrefix; + protected String applyPrefix = ""; protected String version; protected String name; protected String generatePendingDrop; @@ -169,12 +170,17 @@ public class DefaultDbMigration implements DbMigration { if (this.databaseConfig == null) { this.databaseConfig = config; } - if (migrationConfig == null) { - this.migrationConfig = databaseConfig.getMigrationConfig(); - } if (constraintNaming == null) { this.constraintNaming = databaseConfig.getConstraintNaming(); } + + Properties properties = config.getProperties(); + if (properties != null) { + PropertiesWrapper props = new PropertiesWrapper("ebean", config.getName(), properties, null); + migrationPath = props.get("migration.migrationPath", migrationPath); + migrationInitPath = props.get("migration.migrationInitPath", migrationInitPath); + pathToResources = props.get("migration.pathToResources", pathToResources); + } } @Override @@ -466,7 +472,7 @@ public class DefaultDbMigration implements DbMigration { sb.append("R__"); } sb.append(scriptName.replace(' ', '_')); - sb.append(migrationConfig.getApplySuffix()); + sb.append(".sql"); return sb.toString(); } @@ -529,7 +535,7 @@ public class DefaultDbMigration implements DbMigration { this.migrated = new ModelContainer(); } else { this.modelDir = getModelDirectory(migrationDir); - MigrationModel migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix()); + MigrationModel migrationModel = new MigrationModel(modelDir, modelSuffix); this.migrated = migrationModel.read(dbinitMigration); } } @@ -605,7 +611,6 @@ public class DefaultDbMigration implements DbMigration { * Return true if the next pending drop changeSet should be generated as the next migration. */ private String generatePendingDrop() { - String nextDrop = System.getProperty("ddl.migration.pendingDropsFor"); if (nextDrop != null) { return nextDrop; @@ -625,13 +630,13 @@ public class DefaultDbMigration implements DbMigration { version = (nextVersion != null) ? nextVersion : initialVersion; } - String fullVersion = migrationConfig.getApplyPrefix() + version; + String fullVersion = applyPrefix + version; String name = getName(); if (name != null) { fullVersion += "__" + toUnderScore(name); } else if (dropsFor != null) { - fullVersion += "__" + toUnderScore("dropsFor_" + MigrationVersion.trim(dropsFor)); + fullVersion += "__" + toUnderScore("dropsFor_" + trimDropsFor(dropsFor)); } else if (version.equals(initialVersion)) { fullVersion += "__initial"; @@ -639,6 +644,18 @@ public class DefaultDbMigration implements DbMigration { return fullVersion; } + String trimDropsFor(String dropsFor) { + if (dropsFor.startsWith("V") || dropsFor.startsWith("v")) { + dropsFor = dropsFor.substring(1); + } + int commentStart = dropsFor.indexOf("__"); + if (commentStart > -1) { + // trim off the trailing comment + dropsFor = dropsFor.substring(0, commentStart); + } + return dropsFor; + } + /** * Replace spaces with underscores. */ @@ -650,7 +667,6 @@ public class DefaultDbMigration implements DbMigration { * Write any extra platform ddl. */ private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException { - DdlOptions options = new DdlOptions(addForeignKeySkipCheck); for (Pair pair : platforms) { DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read(), options); @@ -661,15 +677,14 @@ public class DefaultDbMigration implements DbMigration { } private PlatformDdlWriter createDdlWriter(DatabasePlatform platform) { - return new PlatformDdlWriter(platform, databaseConfig, migrationConfig, lockTimeoutSeconds); + return new PlatformDdlWriter(platform, databaseConfig, lockTimeoutSeconds); } /** * Write the migration xml. */ private boolean writeMigrationXml(Migration dbMigration, File resourcePath, String fullVersion) { - - String modelFile = fullVersion + migrationConfig.getModelSuffix(); + String modelFile = fullVersion + modelSuffix; File file = new File(resourcePath, modelFile); if (file.exists()) { return false; @@ -691,18 +706,12 @@ public class DefaultDbMigration implements DbMigration { // not explicitly set so use the platform of the server databasePlatform = server.getDatabasePlatform(); } - if (migrationConfig != null) { + if (databaseConfig != null) { if (strictMode != null) { - migrationConfig.setStrictMode(strictMode); - } - if (applyPrefix != null) { - migrationConfig.setApplyPrefix(applyPrefix); - } - if (migrationPath != null) { - migrationConfig.setMigrationPath(migrationPath); + databaseConfig.setDdlStrictMode(strictMode); } if (header != null) { - migrationConfig.setDdlHeader(header); + databaseConfig.setDdlHeader(header); } } } @@ -756,7 +765,6 @@ public class DefaultDbMigration implements DbMigration { * Return the system or environment property. */ private String readEnvironment(String key) { - String val = System.getProperty(key); if (val == null) { val = System.getenv(key); @@ -782,7 +790,7 @@ public class DefaultDbMigration implements DbMigration { String msg = String.format("Error - path to resources %s does not exist. Absolute path is %s", pathToResources, resourceRootDir.getAbsolutePath()); throw new UnknownResourcePathException(msg); } - String resourcePath = migrationConfig.getMigrationPath(dbinitMigration); + String resourcePath = getMigrationPath(dbinitMigration); // expect to be a path to something like - src/main/resources/dbmigration/model File path = new File(resourceRootDir, resourcePath); @@ -794,15 +802,18 @@ public class DefaultDbMigration implements DbMigration { return path; } + private String getMigrationPath(boolean dbinitMigration) { + return dbinitMigration ? migrationInitPath : migrationPath; + } + /** * Return the model directory (relative to the migration directory). */ private File getModelDirectory(File migrationDirectory) { - String modelPath = migrationConfig.getModelPath(); if (modelPath == null || modelPath.isEmpty()) { return migrationDirectory; } - File modelDir = new File(migrationDirectory, migrationConfig.getModelPath()); + File modelDir = new File(migrationDirectory, modelPath); if (!modelDir.exists() && !modelDir.mkdirs()) { logInfo("Warning - Unable to ensure migration model directory exists at %s", modelDir.getAbsolutePath()); } diff --git a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java index 9da8ad4bf..41e91274e 100644 --- a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -208,7 +208,7 @@ public class BaseTableDdl implements TableDdl { this.historyTableSuffix = config.getHistoryTableSuffix(); this.platformDdl = platformDdl; this.platformDdl.configure(config); - this.strictMode = config.getMigrationConfig().isStrictMode(); + this.strictMode = config.isDdlStrictMode(); DbHistorySupport hist = platformDdl.getPlatform().getHistorySupport(); if (hist == null) { this.historySupport = HistorySupport.NONE; diff --git a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java index 627fc6b3e..11189be94 100644 --- a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java +++ b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java @@ -36,6 +36,8 @@ public class CurrentModel { private final boolean jaxbPresent; + private final String ddlHeader; + private ModelContainer model; private ChangeSet changeSet; @@ -67,6 +69,7 @@ public class CurrentModel { this.databasePlatform = server.getDatabasePlatform(); this.constraintNaming = constraintNaming; this.platformTypes = platformTypes; + this.ddlHeader = server.getServerConfig().getDdlHeader(); this.jaxbPresent = Detect.isJAXBPresent(server.getServerConfig()); } @@ -94,12 +97,10 @@ public class CurrentModel { public ModelContainer read() { if (model == null) { model = new ModelContainer(); - ModelBuildContext context = new ModelBuildContext(model, databasePlatform, constraintNaming, platformTypes); ModelBuildBeanVisitor visitor = new ModelBuildBeanVisitor(context); VisitAllUsing visit = new VisitAllUsing(visitor, server); visit.visitAllBeans(); - // adjust the foreign keys on the 'draft' tables context.adjustDraftReferences(); } @@ -129,20 +130,16 @@ public class CurrentModel { createDdl(); StringBuilder ddl = new StringBuilder(2000); - String header = server.getServerConfig().getMigrationConfig().getDdlHeader(); - if (header != null && !header.isEmpty()) { - ddl.append(header).append('\n'); + if (ddlHeader != null && !ddlHeader.isEmpty()) { + ddl.append(ddlHeader).append('\n'); } - if (jaxbPresent) { addExtraDdl(ddl, ExtraDdlXmlReader.readBuiltin(), "-- init script "); } - ddl.append(write.apply().getBuffer()); ddl.append(write.applyForeignKeys().getBuffer()); ddl.append(write.applyHistoryView().getBuffer()); ddl.append(write.applyHistoryTrigger().getBuffer()); - return ddl.toString(); } @@ -166,13 +163,11 @@ public class CurrentModel { createDdl(); StringBuilder ddl = new StringBuilder(2000); - String header = server.getServerConfig().getMigrationConfig().getDdlHeader(); - if (header != null && !header.isEmpty()) { - ddl.append(header).append('\n'); + if (ddlHeader != null && !ddlHeader.isEmpty()) { + ddl.append(ddlHeader).append('\n'); } ddl.append(write.dropAllForeignKeys().getBuffer()); ddl.append(write.dropAll().getBuffer()); - return ddl.toString(); } @@ -180,12 +175,9 @@ public class CurrentModel { * Create all the DDL based on the changeSet. */ private void createDdl() throws IOException { - if (write == null) { ChangeSet createChangeSet = getChangeSet(); - write = new DdlWrite(new MConfiguration(), model, ddlOptions); - DdlHandler handler = handler(); handler.generateProlog(write); handler.generate(write, createChangeSet); @@ -204,11 +196,9 @@ public class CurrentModel { * Convert the model into a ChangeSet. */ private ChangeSet asChangeSet() { - // empty diff so changes will effectively all be create ModelDiff diff = new ModelDiff(); diff.compareTo(model); - return diff.getApplyChangeSet(); } diff --git a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java index 31c389bc9..f56384477 100644 --- a/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java +++ b/ebean-ddlgen/src/main/java/io/ebeaninternal/dbmigration/model/PlatformDdlWriter.java @@ -1,7 +1,6 @@ package io.ebeaninternal.dbmigration.model; import io.ebean.config.DatabaseConfig; -import io.ebean.config.DbMigrationConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; @@ -29,16 +28,13 @@ public class PlatformDdlWriter { private final DatabaseConfig databaseConfig; - private final DbMigrationConfig config; - private final PlatformDdl platformDdl; private final int lockTimeoutSeconds; - public PlatformDdlWriter(DatabasePlatform platform, DatabaseConfig dbConfig, DbMigrationConfig config, int lockTimeoutSeconds) { + public PlatformDdlWriter(DatabasePlatform platform, DatabaseConfig dbConfig, int lockTimeoutSeconds) { this.platformDdl = PlatformDdlBuilder.create(platform); this.databaseConfig = dbConfig; - this.config = config; this.lockTimeoutSeconds = lockTimeoutSeconds; } @@ -78,9 +74,8 @@ public class PlatformDdlWriter { * Write the ddl files. */ protected void writePlatformDdl(DdlWrite write, File resourcePath, String fullVersion) throws IOException { - if (!write.isApplyEmpty()) { - try (FileWriter applyWriter = createWriter(resourcePath, fullVersion, config.getApplySuffix())) { + try (FileWriter applyWriter = createWriter(resourcePath, fullVersion, ".sql")) { writeApplyDdl(applyWriter, write); applyWriter.flush(); } @@ -88,7 +83,6 @@ public class PlatformDdlWriter { } protected FileWriter createWriter(File path, String fullVersion, String suffix) throws IOException { - File applyFile = new File(path, fullVersion + suffix); return new FileWriter(applyFile); } @@ -97,9 +91,8 @@ public class PlatformDdlWriter { * Write the 'Apply' DDL buffers to the writer. */ protected void writeApplyDdl(Writer writer, DdlWrite write) throws IOException { - - String header = config.getDdlHeader(); - if (header != null) { + String header = databaseConfig.getDdlHeader(); + if (header != null && !header.isEmpty()) { writer.append(header).append('\n'); } // merge the apply buffers in the appropriate order diff --git a/ebean-ddlgen/src/test/java/io/ebeaninternal/dbmigration/DefaultDbMigrationTest.java b/ebean-ddlgen/src/test/java/io/ebeaninternal/dbmigration/DefaultDbMigrationTest.java new file mode 100644 index 000000000..642b3dc2c --- /dev/null +++ b/ebean-ddlgen/src/test/java/io/ebeaninternal/dbmigration/DefaultDbMigrationTest.java @@ -0,0 +1,20 @@ +package io.ebeaninternal.dbmigration; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class DefaultDbMigrationTest { + + private final DefaultDbMigration migration = new DefaultDbMigration(); + + @Test + public void trimDropsFor() { + assertEquals("1.2", migration.trimDropsFor("V1.2__hello")); + assertEquals("1.2", migration.trimDropsFor("v1.2__hello")); + assertEquals("1.2", migration.trimDropsFor("v1.2")); + assertEquals("junk1.2", migration.trimDropsFor("junk1.2")); + assertEquals("junk1.2", migration.trimDropsFor("junk1.2__")); + assertEquals("junk1.2", migration.trimDropsFor("junk1.2__more")); + } +} diff --git a/ebean-postgis/pom.xml b/ebean-postgis/pom.xml index 5922583b4..42d6042bc 100644 --- a/ebean-postgis/pom.xml +++ b/ebean-postgis/pom.xml @@ -75,7 +75,7 @@ io.ebean ebean-test - 12.5.1 + 12.5.3-SNAPSHOT test diff --git a/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java b/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java index 391de1191..7cb2d50ff 100644 --- a/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java +++ b/ebean-test/src/main/java/io/ebean/test/config/platform/Config.java @@ -156,12 +156,12 @@ class Config { } private void setMigrationRun() { - config.getMigrationConfig().setRunMigration(true); + config.setRunMigration(true); setProperty("ebean." + db + ".migration.run", "true"); } private void disableMigrationRun() { - System.setProperty("ddl.migration.run", "false"); + System.setProperty("ebean.migration.run", "false"); } /**