#2101 - Refactor to make ebean-migration optional dependency

This commit is contained in:
rob bygrave
2020-11-18 21:52:32 +13:00
parent b61ae74d3d
commit 35580fa62b
15 changed files with 228 additions and 652 deletions
-6
View File
@@ -70,12 +70,6 @@
<version>${ebean-datasource.version}</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>12.1.4</version>
</dependency>
<!-- Jackson core used internally by Ebean -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
@@ -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<String, String> 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<String, String> getDdlPlaceholderMap() {
return ddlPlaceholderMap;
}
/**
* Set a map of placeholder values that are substituted in DDL scripts.
*/
public void setDdlPlaceholderMap(Map<String, String> 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.
@@ -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<String, String> 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.
* <p>
* 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.
* <p>
* 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.
* </p>
*/
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<String, String> 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<String, String> 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.
* <p>
* Value can be a string containing comma delimited list of version numbers.
* </p>
*/
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.
* <p>
* Value can be a string containing comma delimited list of version numbers.
* </p>
*/
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);
}
}
+21 -2
View File
@@ -54,11 +54,30 @@
<version>1.0</version>
</dependency>
<!-- keep testing in core using ebean-ddlgen -->
<dependency>
<groupId>io.avaje</groupId>
<artifactId>classpath-scanner</artifactId>
<version>4.2</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration-auto</artifactId>
<version>1.0</version>
</dependency>
<!-- keep testing in core using ebean-ddlgen & ebean-migration -->
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>12.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddlgen</artifactId>
<version>12.5.0</version>
<version>12.5.2A</version>
<scope>test</scope>
</dependency>
@@ -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());
}
}
@@ -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");
}
}
+7
View File
@@ -10,6 +10,7 @@
<name>ebean ddl generation</name>
<description>DDL and DB Migration generation</description>
<artifactId>ebean-ddlgen</artifactId>
<!-- <version>12.5.2A</version>-->
<dependencies>
@@ -19,6 +20,12 @@
<version>1.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-migration</artifactId>
<version>12.2.0</version>
</dependency>
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-core</artifactId>
@@ -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());
}
}
@@ -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());
}
@@ -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;
@@ -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();
}
@@ -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
@@ -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"));
}
}
+1 -1
View File
@@ -75,7 +75,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-test</artifactId>
<version>12.5.1</version>
<version>12.5.3-SNAPSHOT</version>
<scope>test</scope>
</dependency>
@@ -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");
}
/**