diff --git a/ebean-migration/src/main/java/io/ebean/migration/ConfigurationAware.java b/ebean-migration/src/main/java/io/ebean/migration/ConfigurationAware.java
deleted file mode 100644
index 13dc622..0000000
--- a/ebean-migration/src/main/java/io/ebean/migration/ConfigurationAware.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.ebean.migration;
-
-/**
- * Marks a class as configuration aware (JdbcMigrations) Configuration aware
- * classes get the migration configuration injected upon creation. The
- * implementer is responsible for correctly storing the provided
- * MigrationConfig (usually in a field).
- *
- * @author Roland Praml, FOCONIS AG
- *
- */
-public interface ConfigurationAware {
-
- /**
- * Set the configuration being used.
- */
- void setMigrationConfig(MigrationConfig config);
-}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/JdbcMigration.java b/ebean-migration/src/main/java/io/ebean/migration/JdbcMigration.java
index 4e07bc3..99bf021 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/JdbcMigration.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/JdbcMigration.java
@@ -1,16 +1,26 @@
package io.ebean.migration;
import java.sql.Connection;
+import java.sql.SQLException;
/**
* Interface to be implemented by Jdbc Java Migrations. By default the migration
* version and description will be extracted from the class name. The checksum of this migration
- * (for validation) will also be null, unless the migration also implements the
- * MigrationChecksumProvider, in which case it can be returned programmatically.
+ * will be 0 by default
*
- * When the JdbcMigration implements ConfigurationAware, the master
- * {@link MigrationConfig} is automatically injected upon creation, which is
- * useful for getting placeholder and schema information.
+ * Note: Instances of JdbcMigration should be stateless, as the migrate method may
+ * run multiple times in multi-tenant setups.
+ *
+ * There are several ways, how the JdbcMigrations are found.
+ *
+ * - ServiceLoader this is the default behaviour
+ * in this case add all your migration class names in META-INF/services/io.ebean.migration.JdbcMigration and/or in your
+ * module info.
+ * - Using jdbcMigrations property
+ * you can specify all migrations in the jdbcMigrations property
+ * - Using own jdbcMigrationFactory
+ * you can write your own jdbcMigrationFactory that provides JdbcMigrations
+ *
*
* @author Roland Praml, FOCONIS AG
*/
@@ -18,11 +28,43 @@ public interface JdbcMigration extends MigrationChecksumProvider {
/**
* Execute the migration using the connection.
+ *
+ * Note: This API has changed with ebean-migration 13.12, as the initialization has changed.
+ * See https://github.com/ebean-orm/ebean-migration/issues/90 for migration advice.
*/
- void migrate(Connection connection);
+ void migrate(MigrationContext context) throws SQLException;
@Override
default int getChecksum() {
return 0;
}
+
+ /**
+ * Returns the name of the JdbcMigration. Note, the name is used to determine the version and comment,
+ * that is written to the migration table, so the returned value must be a parseable {@link MigrationVersion}
+ * (example: V1_2_1__comment)
+ *
+ * By default, the simple classname will be returned, so the file name can be used.
+ */
+ default String getName() {
+ return getClass().getSimpleName();
+ }
+
+ /**
+ * Returns the version of the JdbcMigration. By default, it is parsed from {@link #getName()}
+ */
+ default MigrationVersion getVersion() {
+ return MigrationVersion.parse(getName());
+ }
+
+ /**
+ * Determines, if this migration can be used for that migrationContext.
+ * Here, platform checks or other things can be implemented.
+ * You should not write to database at this stage.
+ *
+ * By default, true is returned.
+ */
+ default boolean matches(MigrationContext context) {
+ return true;
+ }
}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/JdbcMigrationFactory.java b/ebean-migration/src/main/java/io/ebean/migration/JdbcMigrationFactory.java
deleted file mode 100644
index a5eabe3..0000000
--- a/ebean-migration/src/main/java/io/ebean/migration/JdbcMigrationFactory.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.ebean.migration;
-
-/**
- * Factory to create and initialise a JdbcMigration.
- *
- * @author Roland Praml, FOCONIS AG
- */
-public interface JdbcMigrationFactory {
-
- /**
- * Create a JDBC based migration given the class name.
- */
- JdbcMigration createInstance(String className);
-}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java b/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java
index d33696f..a091448 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java
@@ -3,9 +3,13 @@
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.ServiceLoader;
import java.util.Set;
/**
@@ -32,7 +36,17 @@ public class MigrationConfig {
private boolean setCurrentSchema = true;
private boolean allowErrorInRepeatable;
- private JdbcMigrationFactory jdbcMigrationFactory = new DefaultMigrationFactory();
+ /**
+ * Run migration automatically when ebean starts (Requires MigrationPlugin from ebean-migration-db)
+ */
+ private boolean pluginRun;
+
+ /**
+ * Holds a Collection/Iterable of JdbcMigrations. All migrations (JDBC and SQL) are
+ * extecuted in the order defined by their version numbers.
+ * By default, JdbcMigrations are loaded via ServiceLoader.
+ */
+ private Iterable jdbcMigrations = new DefaultJdbcMigrations();
/**
* Versions that we want to insert into migration history without actually running.
@@ -416,17 +430,45 @@ public void setClassLoader(ClassLoader classLoader) {
}
/**
- * Return the jdbcMigrationFactory.
+ * Return the jdbcMigrations.
*/
- public JdbcMigrationFactory getJdbcMigrationFactory() {
- return jdbcMigrationFactory;
+ public Iterable getJdbcMigrations() {
+ return jdbcMigrations;
}
/**
- * Set the jdbcMigrationFactory.
+ * Set the jdbcMigrations. If not set, the ServiceLoader is used.
+ * JdbcMigrations can be either defined with the property jdbcMigrations
+ * to a fully qualified class name implementing Iterable<JdbcMigration> or by
+ * specifying a comma separated list of {@link JdbcMigration}s in the jdbcMigrations property.
+ *
+ * Note: If you plan to run migrations in multi-tenant env in multiple threads, the provided factory
+ * must be thread safe!
*/
- public void setJdbcMigrationFactory(JdbcMigrationFactory jdbcMigrationFactory) {
- this.jdbcMigrationFactory = jdbcMigrationFactory;
+ public void setJdbcMigrations(Iterable jdbcMigrationFactory) {
+ this.jdbcMigrations = jdbcMigrationFactory;
+ }
+
+ /**
+ * Helper method to set migrations with the jdbcMigrations property.
+ * You can either pass ONE MigrationCollection or a list of JdbcMigrations.
+ */
+ @SuppressWarnings("unchecked")
+ public void setJdbcMigrations(String... classNames) {
+ if (classNames.length == 1) {
+ Object candidate = newInstance(classNames[0].trim());
+ if (candidate instanceof JdbcMigration) {
+ setJdbcMigrations(List.of((JdbcMigration) candidate));
+ } else {
+ setJdbcMigrations((Iterable) candidate);
+ }
+ } else {
+ List migrations = new ArrayList<>(classNames.length);
+ for (String className : classNames) {
+ migrations.add(newInstance(className.trim()));
+ }
+ setJdbcMigrations(migrations);
+ }
}
/**
@@ -457,6 +499,20 @@ public void setMinVersionFailMessage(String minVersionFailMessage) {
this.minVersionFailMessage = minVersionFailMessage;
}
+ /**
+ * Sets, if migration should automatically run when ebean starts.
+ */
+ public void setPluginRun(boolean pluginRun) {
+ this.pluginRun = pluginRun;
+ }
+
+ /**
+ * run migration on ebean start
+ */
+ public boolean isPluginRun() {
+ return pluginRun;
+ }
+
/**
* Load configuration from standard properties.
*/
@@ -483,6 +539,12 @@ public void load(Properties props) {
runPlaceholders = property("placeholders", runPlaceholders);
minVersion = property("minVersion", minVersion);
minVersionFailMessage = property("minVersionFailMessage", minVersionFailMessage);
+ pluginRun = property("plugin.run", pluginRun);
+
+ String jdbcMigrations = property("jdbcMigrations");
+ if (jdbcMigrations != null) {
+ setJdbcMigrations(jdbcMigrations.split(","));
+ }
String patchInsertOn = property("patchInsertOn");
if (patchInsertOn != null) {
@@ -498,6 +560,16 @@ public void load(Properties props) {
}
}
+ @SuppressWarnings("unchecked")
+ public T newInstance(String className) {
+ try {
+ Class> cls = Class.forName(className, true, getClassLoader());
+ return (T) cls.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Error constructing " + className, e);
+ }
+ }
+
private boolean property(String key, boolean value) {
String val = property(key);
return val != null ? Boolean.parseBoolean(val) : value;
@@ -611,25 +683,14 @@ public void setFastMode(boolean fastMode) {
}
/**
- * Default factory. Uses the migration's class loader and injects the config if necessary.
- *
- * @author Roland Praml, FOCONIS AG
+ * Default implementation for service-loader. Note: As ServiceLoader is not thread safe,
+ * it is better to return a new iterator each time.
*/
- public class DefaultMigrationFactory implements JdbcMigrationFactory {
+ private class DefaultJdbcMigrations implements Iterable {
@Override
- public JdbcMigration createInstance(String className) {
- try {
- Class> clazz = Class.forName(className, true, MigrationConfig.this.getClassLoader());
- JdbcMigration migration = (JdbcMigration) clazz.getDeclaredConstructor().newInstance();
- if (migration instanceof ConfigurationAware) {
- ((ConfigurationAware) migration).setMigrationConfig(MigrationConfig.this);
- }
- return migration;
- } catch (Exception e) {
- throw new IllegalArgumentException(className + " is not a valid JdbcMigration", e);
- }
+ public Iterator iterator() {
+ return ServiceLoader.load(JdbcMigration.class, getClassLoader()).iterator();
}
}
-
}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java b/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java
index a2417ad..8422f34 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java
@@ -1,6 +1,7 @@
package io.ebean.migration;
import java.sql.Connection;
+import java.sql.SQLException;
/**
* The current context while a migration runs.
@@ -38,4 +39,10 @@ public interface MigrationContext {
*/
String basePlatform();
+ /**
+ * Indicates that all migrations are done and the underlying connection or transaction should perform a commit.
+ *
+ * NOTE:
+ */
+ void commit() throws SQLException;
}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/runner/DefaultMigrationContext.java b/ebean-migration/src/main/java/io/ebean/migration/runner/DefaultMigrationContext.java
index 96bc4c0..4df93c2 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/runner/DefaultMigrationContext.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/runner/DefaultMigrationContext.java
@@ -4,19 +4,20 @@
import io.ebean.migration.MigrationContext;
import java.sql.Connection;
+import java.sql.SQLException;
/**
* A default implementation of the MigrationContext.
*
* @author Roland Praml, FOCONIS AG
*/
-public class DefaultMigrationContext implements MigrationContext {
+class DefaultMigrationContext implements MigrationContext {
private final Connection connection;
private final String migrationPath;
private final String platform;
private final String basePlatform;
- public DefaultMigrationContext(MigrationConfig config, Connection connection) {
+ DefaultMigrationContext(MigrationConfig config, Connection connection) {
this.connection = connection;
this.migrationPath = config.getMigrationPath();
this.platform = config.getPlatform();
@@ -42,4 +43,9 @@ public String platform() {
public String basePlatform() {
return basePlatform;
}
+
+ @Override
+ public void commit() throws SQLException {
+ connection.commit();
+ }
}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/runner/LocalMigrationResources.java b/ebean-migration/src/main/java/io/ebean/migration/runner/LocalMigrationResources.java
index cd605bb..eeb7c15 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/runner/LocalMigrationResources.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/runner/LocalMigrationResources.java
@@ -4,14 +4,17 @@
import io.avaje.classpath.scanner.core.Scanner;
import io.ebean.migration.JdbcMigration;
import io.ebean.migration.MigrationConfig;
+import io.ebean.migration.MigrationContext;
import io.ebean.migration.MigrationVersion;
-import java.io.*;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.UncheckedIOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.function.Predicate;
import static java.lang.System.Logger.Level.DEBUG;
@@ -25,7 +28,7 @@ final class LocalMigrationResources {
private final List versions = new ArrayList<>();
private final MigrationConfig migrationConfig;
private final ClassLoader classLoader;
- private final boolean searchForJdbcMigrations;
+ private final Iterable jdbcMigrations;
/**
* Construct with configuration options.
@@ -33,53 +36,78 @@ final class LocalMigrationResources {
LocalMigrationResources(MigrationConfig migrationConfig) {
this.migrationConfig = migrationConfig;
this.classLoader = migrationConfig.getClassLoader();
- this.searchForJdbcMigrations = migrationConfig.getJdbcMigrationFactory() != null;
+ this.jdbcMigrations = migrationConfig.getJdbcMigrations();
}
/**
* Read the init migration resources (usually only 1) returning true if there are versions.
*/
boolean readInitResources() {
- return readResourcesForPath(migrationConfig.getMigrationInitPath());
+ readResourcesForPath(migrationConfig.getMigrationInitPath());
+ Collections.sort(versions);
+ return !versions.isEmpty();
}
/**
- * Read all the migration resources (SQL scripts) returning true if there are versions.
+ * Read all the migration resources (SQL scripts and JDBC migrations) returning true if there are versions.
*/
- boolean readResources() {
+ boolean readResources(MigrationContext context) {
if (readFromIndex()) {
// automatically enable earlyChecksumMode when using index file with pre-computed checksums
migrationConfig.setEarlyChecksumMode(true);
- return true;
+ } else {
+ readResourcesForPath(migrationConfig.getMigrationPath());
}
- return readResourcesForPath(migrationConfig.getMigrationPath());
+ // after we read the SQL migrations from index or classpath scan, we
+ // read jdbcMigrations and sort them.
+ readJdbcMigrations(context);
+ Collections.sort(versions);
+ return !versions.isEmpty();
}
+ /**
+ * Returns true, if an index file was found. Although, if file was empty, so we do not fall back
+ * to classpath scan!
+ */
private boolean readFromIndex() {
final var base = "/" + migrationConfig.getMigrationPath() + "/";
final var basePlatform = migrationConfig.getBasePlatform();
final var indexName = "idx_" + basePlatform + ".migrations";
URL idx = resource(base + indexName);
if (idx != null) {
- return loadFromIndexFile(idx, base);
+ loadFromIndexFile(idx, base);
+ return true;
}
idx = resource(base + basePlatform + '/' + indexName);
if (idx != null) {
- return loadFromIndexFile(idx, base + basePlatform + '/');
+ loadFromIndexFile(idx, base + basePlatform + '/');
+ return true;
}
final var platform = migrationConfig.getPlatform();
idx = resource(base + platform + indexName);
if (idx != null) {
- return loadFromIndexFile(idx, base + platform + '/');
+ loadFromIndexFile(idx, base + platform + '/');
+ return true;
}
return false;
}
+ private void readJdbcMigrations(MigrationContext context) {
+ if (jdbcMigrations != null) {
+ for (JdbcMigration jdbcMigration : jdbcMigrations) {
+ if (jdbcMigration.matches(context)) {
+ versions.add(new LocalJdbcMigrationResource(jdbcMigration.getVersion(), jdbcMigration.getName(), jdbcMigration));
+ }
+ }
+ }
+ }
+
private URL resource(String base) {
return LocalMigrationResources.class.getResource(base);
}
- private boolean loadFromIndexFile(URL idx, String base) {
+ private void loadFromIndexFile(URL idx, String base) {
+ log.log(DEBUG, "Loading index from {0}", idx);
try (var reader = new LineNumberReader(new InputStreamReader(idx.openStream()))) {
String line;
while ((line = reader.readLine()) != null) {
@@ -95,93 +123,63 @@ private boolean loadFromIndexFile(URL idx, String base) {
}
}
}
-
- return !versions.isEmpty();
-
} catch (IOException e) {
throw new UncheckedIOException("Error reading idx file", e);
}
}
- private boolean readResourcesForPath(String path) {
+ private void readResourcesForPath(String path) {
// try to load from base platform first
final String basePlatform = migrationConfig.getBasePlatform();
if (basePlatform != null && loadedFrom(path, basePlatform)) {
- return true;
+ return;
}
// try to load from specific platform
final String platform = migrationConfig.getPlatform();
if (platform != null && loadedFrom(path, platform)) {
- return true;
+ return;
}
- addResources(scanForBoth(path));
- Collections.sort(versions);
- return !versions.isEmpty();
+ addResources(scanForMigrations(path));
}
/**
* Return true if migrations were loaded from platform specific location.
*/
private boolean loadedFrom(String path, String platform) {
- addResources(scanForBoth(path + "/" + platform));
+ addResources(scanForMigrations(path + "/" + platform));
if (versions.isEmpty()) {
return false;
}
log.log(DEBUG, "platform migrations for {0}", platform);
- if (searchForJdbcMigrations) {
- addResources(scanForJdbcOnly(path));
- }
- Collections.sort(versions);
return true;
}
/**
- * Scan only for JDBC migrations.
+ * Scan for SQL migrations.
*/
- private List scanForJdbcOnly(String path) {
- return new Scanner(classLoader).scanForResources(path, new JdbcOnly());
+ private List scanForMigrations(String path) {
+ return new Scanner(classLoader).scanForResources(path, name -> name.endsWith(".sql"));
}
/**
- * Scan for both SQL and JDBC migrations.
+ * adds the script migrations found from classpath scan.
*/
- private List scanForBoth(String path) {
- return new Scanner(classLoader).scanForResources(path, new Match(searchForJdbcMigrations));
- }
-
private void addResources(List resourceList) {
if (!resourceList.isEmpty()) {
log.log(DEBUG, "resources: {0}", resourceList);
}
for (Resource resource : resourceList) {
String filename = resource.name();
- if (filename.endsWith(".sql")) {
- versions.add(createScriptMigration(resource, filename));
- } else if (searchForJdbcMigrations && filename.endsWith(".class")) {
- versions.add(createJdbcMigration(resource, filename));
- }
+ assert filename.endsWith(".sql");
+ String mainName = filename.substring(0, filename.length() - 4);
+ versions.add(createScriptMigration(resource, mainName));
}
}
- /**
- * Return a programmatic JDBC migration.
- */
- private LocalMigrationResource createJdbcMigration(Resource resource, String filename) {
- int pos = filename.lastIndexOf(".class");
- String mainName = filename.substring(0, pos);
- MigrationVersion migrationVersion = MigrationVersion.parse(mainName);
- String className = resource.location().replace('/', '.');
- className = className.substring(0, className.length() - 6);
- JdbcMigration instance = migrationConfig.getJdbcMigrationFactory().createInstance(className);
- return new LocalJdbcMigrationResource(migrationVersion, resource.location(), instance);
- }
-
/**
* Create a script based migration.
*/
- private LocalMigrationResource createScriptMigration(Resource resource, String filename) {
- int pos = filename.lastIndexOf(".sql");
- String mainName = filename.substring(0, pos);
+ private LocalMigrationResource createScriptMigration(Resource resource, String mainName) {
MigrationVersion migrationVersion = MigrationVersion.parse(mainName);
return new LocalDdlMigrationResource(migrationVersion, resource.location(), resource);
}
@@ -193,30 +191,4 @@ List versions() {
return versions;
}
- /**
- * Filter used to find the migration scripts.
- */
- private static final class Match implements Predicate {
-
- private final boolean searchJdbc;
-
- Match(boolean searchJdbc) {
- this.searchJdbc = searchJdbc;
- }
-
- @Override
- public boolean test(String name) {
- return name.endsWith(".sql") || (searchJdbc && name.endsWith(".class") && !name.contains("$"));
- }
- }
-
- /**
- * Filter to find JDBC migrations only.
- */
- private static final class JdbcOnly implements Predicate {
- @Override
- public boolean test(String name) {
- return name.endsWith(".class") && !name.contains("$");
- }
- }
}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationEngine.java b/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationEngine.java
index 8de37fb..804e520 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationEngine.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationEngine.java
@@ -10,12 +10,23 @@
import java.sql.SQLException;
import java.util.List;
-import static java.lang.System.Logger.Level.*;
+import static java.lang.System.Logger.Level.DEBUG;
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.INFO;
import static java.lang.System.Logger.Level.WARNING;
import static java.util.Collections.emptyList;
/**
- * Actually runs the migrations.
+ * Actually runs the migrations and executes JDBC-mgirations.
+ *
+ * MigrationEngine needs a context that provides information for the current database,
+ * where the migration should run. When running on an existing {@link Connection}, a
+ * {@link DefaultMigrationContext} is created, but it is also possible, to pass a
+ * MigrationContext, that holds an ebeanserver. In this case, it is possible to use
+ * features like dto-queries in JDBC-migrations.
+ *
+ * In the other case, if only a raw jdbc-connection is used, you may not have access
+ * to these features and JDBC-migrations have to be done the traditional way.
*/
public class MigrationEngine {
@@ -54,7 +65,7 @@ public List run(MigrationContext context) {
long startMs = System.currentTimeMillis();
LocalMigrationResources resources = new LocalMigrationResources(migrationConfig);
- if (!resources.readResources() && !resources.readInitResources()) {
+ if (!resources.readResources(context) && !resources.readInitResources()) {
log.log(DEBUG, "no migrations to check");
return emptyList();
}
@@ -74,7 +85,7 @@ public List run(MigrationContext context) {
final MigrationTable table = initialiseMigrationTable(firstCheck, connection);
try {
List result = runMigrations(table, resources.versions());
- connection.commit();
+ context.commit();
if (!checkStateOnly) {
long commitMs = System.currentTimeMillis();
log.log(INFO, "DB migrations completed in {0}ms - executed:{1} totalMigrations:{2} mode:{3}", (commitMs - startMs), table.count(), table.size(), table.mode());
diff --git a/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java b/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java
index 8dea179..090f4aa 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java
@@ -426,7 +426,7 @@ private long executeMigration(LocalMigrationResource local, String script) throw
if (local instanceof LocalJdbcMigrationResource) {
JdbcMigration migration = ((LocalJdbcMigrationResource) local).migration();
log.log(INFO, "Executing jdbc migration version: {0} - {1}", local.version(), migration);
- migration.migrate(context.connection());
+ migration.migrate(context);
} else {
log.log(DEBUG, "run migration {0}", local.location());
scriptRunner.runScript(script, "run migration version: " + local.version());
diff --git a/ebean-migration/src/main/java/module-info.java b/ebean-migration/src/main/java/module-info.java
index d20abf8..e0f8e22 100644
--- a/ebean-migration/src/main/java/module-info.java
+++ b/ebean-migration/src/main/java/module-info.java
@@ -8,5 +8,6 @@
requires transitive io.ebean.ddl.runner;
requires io.ebean.migration.auto;
+ uses io.ebean.migration.JdbcMigration;
provides io.ebean.migration.auto.AutoMigrationRunner with io.ebean.migration.AutoRunner;
}
diff --git a/ebean-migration/src/test/java/dbmig/V1_2_1__test.java b/ebean-migration/src/test/java/dbmig/V1_2_1__test.java
index 9b917c2..cc87495 100644
--- a/ebean-migration/src/test/java/dbmig/V1_2_1__test.java
+++ b/ebean-migration/src/test/java/dbmig/V1_2_1__test.java
@@ -2,32 +2,24 @@
import java.sql.Connection;
-import io.ebean.migration.ConfigurationAware;
import io.ebean.migration.JdbcMigration;
import io.ebean.migration.MigrationConfig;
+import io.ebean.migration.MigrationContext;
/**
* Sample migration.
*
* @author Roland Praml, FOCONIS AG
- *
*/
-public class V1_2_1__test implements JdbcMigration, ConfigurationAware{
-
- private MigrationConfig config;
+public class V1_2_1__test implements JdbcMigration {
public static class MyDto {
String id;
}
-
- @Override
- public void setMigrationConfig(MigrationConfig config) {
- this.config = config;
- }
@Override
- public void migrate(Connection connection) {
- System.out.println("Executing migration on " + connection);
+ public void migrate(MigrationContext context) {
+ System.out.println("Executing migration on " + context);
}
@Override
diff --git a/ebean-migration/src/test/java/io/ebean/migration/MigrationRunnerTest.java b/ebean-migration/src/test/java/io/ebean/migration/MigrationRunnerTest.java
index 8738b2f..00eb6ea 100644
--- a/ebean-migration/src/test/java/io/ebean/migration/MigrationRunnerTest.java
+++ b/ebean-migration/src/test/java/io/ebean/migration/MigrationRunnerTest.java
@@ -1,5 +1,6 @@
package io.ebean.migration;
+import dbmig.V1_2_1__test;
import io.ebean.datasource.DataSourceConfig;
import io.ebean.datasource.DataSourceFactory;
import io.ebean.datasource.DataSourcePool;
@@ -221,6 +222,8 @@ public void run_with_min_version() {
try {
MigrationConfig config = createMigrationConfig();
config.setMigrationPath("dbmig");
+ config.setJdbcMigrations(List.of(new V1_2_1__test()));
+
config.setMinVersion("1.3"); // dbmig must run, if DB is empty!
new MigrationRunner(config).run(dataSource);
@@ -300,7 +303,7 @@ public void run_with_skipMigration() throws SQLException {
// assert migrations are in the migration table
try (final Connection connection = dataSource.getConnection()) {
final List names = migrationNames(connection);
- assertThat(names).contains("", "hello", "initial", "add_m3", "test", "m2_view");
+ assertThat(names).containsExactly("", "hello", "initial", "add_m3", "serviceLoaded", "m2_view");
}
// assert the migrations didn't actually run (create the tables etc)
diff --git a/ebean-migration/src/test/java/io/ebean/migration/ServiceLoaderMigration.java b/ebean-migration/src/test/java/io/ebean/migration/ServiceLoaderMigration.java
new file mode 100644
index 0000000..c23dbc7
--- /dev/null
+++ b/ebean-migration/src/test/java/io/ebean/migration/ServiceLoaderMigration.java
@@ -0,0 +1,24 @@
+package io.ebean.migration;
+
+import java.sql.Connection;
+
+/**
+ * @author Roland Praml, FOCONIS AG
+ */
+public class ServiceLoaderMigration implements JdbcMigration {
+
+ @Override
+ public String getName() {
+ return "1.4.1__serviceLoaded";
+ }
+
+ @Override
+ public void migrate(MigrationContext context) {
+
+ }
+
+ @Override
+ public boolean matches(MigrationContext context) {
+ return "dbmig".equals(context.migrationPath());
+ }
+}
diff --git a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable1Test.java b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable1Test.java
index 81b72c5..d7880c0 100644
--- a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable1Test.java
+++ b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable1Test.java
@@ -1,5 +1,6 @@
package io.ebean.migration.runner;
+import dbmig.V1_2_1__test;
import io.ebean.migration.MigrationConfig;
import io.ebean.migration.MigrationRunner;
import io.ebean.datasource.DataSourceConfig;
@@ -52,6 +53,7 @@ private MigrationTable migrationTable(Connection conn) {
public void testMigrationTableBase() throws Exception {
config.setMigrationPath("dbmig");
+ config.setJdbcMigrations(List.of(new V1_2_1__test()));
MigrationRunner runner = new MigrationRunner(config);
runner.run(dataSource);
diff --git a/ebean-migration/src/test/resources/META-INF/services/io.ebean.migration.JdbcMigration b/ebean-migration/src/test/resources/META-INF/services/io.ebean.migration.JdbcMigration
new file mode 100644
index 0000000..6615228
--- /dev/null
+++ b/ebean-migration/src/test/resources/META-INF/services/io.ebean.migration.JdbcMigration
@@ -0,0 +1 @@
+io.ebean.migration.ServiceLoaderMigration
diff --git a/pom.xml b/pom.xml
index f561816..400119b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@
ebean-migration-auto
ebean-migration
+ ebean-migration-db