diff --git a/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java b/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java
new file mode 100644
index 0000000..a2417ad
--- /dev/null
+++ b/ebean-migration/src/main/java/io/ebean/migration/MigrationContext.java
@@ -0,0 +1,41 @@
+package io.ebean.migration;
+
+import java.sql.Connection;
+
+/**
+ * The current context while a migration runs.
+ *
+ * This is used to provide meta-informations in JDBC migrations and mainly provides a read-only access
+ * to a subset of MigrationConfig.
+ *
+ * It is possible to provide an extended implementation in MigrationEngine.run(context)
,
+ * which is accessible in JdbcMigration. So you can create a EbeanMigrationContext, so that you can
+ * access the current ebean server in the JDBC migration.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public interface MigrationContext {
+ /**
+ * The current connection. Note: During migration, this connection is always the same.
+ * You must not close this connection!
+ */
+ Connection connection();
+
+ /**
+ * The migration path of SQL migrations. You can use this, to load additional SQL resources
+ * in your JDBC migration or determine, if this JDBC migration is for a particular path.
+ * This can be used if you have multiple ebean servers for different databases.
+ */
+ String migrationPath();
+
+ /**
+ * The platform of the current migration run. (e.g. sqlserver17
)
+ */
+ String platform();
+
+ /**
+ * The base platform of the current migration run. (e.g. sqlserver
)
+ */
+ String basePlatform();
+
+}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/MigrationRunner.java b/ebean-migration/src/main/java/io/ebean/migration/MigrationRunner.java
index 165febf..b3270f7 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/MigrationRunner.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/MigrationRunner.java
@@ -44,6 +44,13 @@ public List checkState(Connection connection) {
return run(connection, true);
}
+ /**
+ * Return the migrations that would be applied if the migration is run.
+ */
+ public List checkState(MigrationContext context) {
+ return run(context, true);
+ }
+
/**
* Run by creating a DB connection from driver, url, username defined in MigrationConfig.
*/
@@ -65,6 +72,13 @@ public void run(Connection connection) {
run(connection, false);
}
+ /**
+ * Run the migrations if there are any that need running.
+ */
+ public void run(MigrationContext context) {
+ run(context, false);
+ }
+
private Connection connection(DataSource dataSource) {
String username = migrationConfig.getDbUsername();
try {
@@ -86,4 +100,10 @@ private List run(Connection connection, boolean checkStateOnl
return new MigrationEngine(migrationConfig, checkStateOnly).run(connection);
}
+ /**
+ * Run the migrations if there are any that need running.
+ */
+ private List run(MigrationContext context, boolean checkStateOnly) {
+ return new MigrationEngine(migrationConfig, checkStateOnly).run(context);
+ }
}
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
new file mode 100644
index 0000000..96bc4c0
--- /dev/null
+++ b/ebean-migration/src/main/java/io/ebean/migration/runner/DefaultMigrationContext.java
@@ -0,0 +1,45 @@
+package io.ebean.migration.runner;
+
+import io.ebean.migration.MigrationConfig;
+import io.ebean.migration.MigrationContext;
+
+import java.sql.Connection;
+
+/**
+ * A default implementation of the MigrationContext.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+public 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) {
+ this.connection = connection;
+ this.migrationPath = config.getMigrationPath();
+ this.platform = config.getPlatform();
+ this.basePlatform = config.getBasePlatform();
+ }
+
+ @Override
+ public Connection connection() {
+ return connection;
+ }
+
+ @Override
+ public String migrationPath() {
+ return migrationPath;
+ }
+
+ @Override
+ public String platform() {
+ return platform;
+ }
+
+ @Override
+ public String basePlatform() {
+ return basePlatform;
+ }
+}
diff --git a/ebean-migration/src/main/java/io/ebean/migration/runner/FirstCheck.java b/ebean-migration/src/main/java/io/ebean/migration/runner/FirstCheck.java
index 72becb1..ad18657 100644
--- a/ebean-migration/src/main/java/io/ebean/migration/runner/FirstCheck.java
+++ b/ebean-migration/src/main/java/io/ebean/migration/runner/FirstCheck.java
@@ -1,6 +1,7 @@
package io.ebean.migration.runner;
import io.ebean.migration.MigrationConfig;
+import io.ebean.migration.MigrationContext;
import java.sql.Connection;
import java.sql.SQLException;
@@ -15,17 +16,17 @@ final class FirstCheck {
final MigrationConfig config;
final MigrationPlatform platform;
- final Connection connection;
+ final MigrationContext context;
final String schema;
final String table;
final String sqlTable;
boolean tableKnownToExist;
private int count;
- FirstCheck(MigrationConfig config, Connection connection, MigrationPlatform platform) {
+ FirstCheck(MigrationConfig config, MigrationContext context, MigrationPlatform platform) {
this.config = config;
this.platform = platform;
- this.connection = connection;
+ this.context = context;
this.schema = config.getDbSchema();
this.table = config.getMetaTable();
this.sqlTable = schema != null ? schema + '.' + table : table;
@@ -80,7 +81,7 @@ private int checksumFor(LocalMigrationResource local) {
}
List fastRead() throws SQLException {
- return platform.fastReadMigrations(sqlTable, connection);
+ return platform.fastReadMigrations(sqlTable, context.connection());
}
int count() {
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 55562aa..8de37fb 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
@@ -2,6 +2,7 @@
import io.avaje.applog.AppLog;
import io.ebean.migration.MigrationConfig;
+import io.ebean.migration.MigrationContext;
import io.ebean.migration.MigrationException;
import io.ebean.migration.MigrationResource;
@@ -35,51 +36,63 @@ public MigrationEngine(MigrationConfig migrationConfig, boolean checkStateOnly)
/**
* Run the migrations if there are any that need running.
+ *
+ * @param connection the connection to run on. Note the connection will be closed.
*/
public List run(Connection connection) {
try {
- long startMs = System.currentTimeMillis();
- LocalMigrationResources resources = new LocalMigrationResources(migrationConfig);
- if (!resources.readResources() && !resources.readInitResources()) {
- log.log(DEBUG, "no migrations to check");
- return emptyList();
- }
- long splitMs = System.currentTimeMillis() - startMs;
- final var platform = derivePlatform(migrationConfig, connection);
- final var firstCheck = new FirstCheck(migrationConfig, connection, platform);
- if (fastMode && firstCheck.fastModeCheck(resources.versions())) {
- long checkMs = System.currentTimeMillis() - startMs;
- log.log(INFO, "DB migrations completed in {0}ms - totalMigrations:{1} readResources:{2}ms", checkMs, firstCheck.count(), splitMs);
- return emptyList();
- }
- // ensure running with autoCommit false
- setAutoCommitFalse(connection);
-
- final MigrationTable table = initialiseMigrationTable(firstCheck, connection);
- try {
- List result = runMigrations(table, resources.versions());
- connection.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());
- int countNonTransactional = table.runNonTransactional();
- if (countNonTransactional > 0) {
- log.log(INFO, "Non-transactional DB migrations completed in {0}ms - executed:{1}", (System.currentTimeMillis() - commitMs), countNonTransactional);
- }
+ return run(new DefaultMigrationContext(migrationConfig, connection));
+ } finally {
+ close(connection);
+ }
+ }
+
+ /**
+ * Run the migrations if there are any that need running. (Does not close connection)
+ */
+ public List run(MigrationContext context) {
+
+ long startMs = System.currentTimeMillis();
+ LocalMigrationResources resources = new LocalMigrationResources(migrationConfig);
+ if (!resources.readResources() && !resources.readInitResources()) {
+ log.log(DEBUG, "no migrations to check");
+ return emptyList();
+ }
+
+ var connection = context.connection();
+ long splitMs = System.currentTimeMillis() - startMs;
+ final var platform = derivePlatform(migrationConfig, connection);
+ final var firstCheck = new FirstCheck(migrationConfig, context, platform);
+ if (fastMode && firstCheck.fastModeCheck(resources.versions())) {
+ long checkMs = System.currentTimeMillis() - startMs;
+ log.log(INFO, "DB migrations completed in {0}ms - totalMigrations:{1} readResources:{2}ms", checkMs, firstCheck.count(), splitMs);
+ return emptyList();
+ }
+ // ensure running with autoCommit false
+ setAutoCommitFalse(connection);
+
+ final MigrationTable table = initialiseMigrationTable(firstCheck, connection);
+ try {
+ List result = runMigrations(table, resources.versions());
+ connection.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());
+ int countNonTransactional = table.runNonTransactional();
+ if (countNonTransactional > 0) {
+ log.log(INFO, "Non-transactional DB migrations completed in {0}ms - executed:{1}", (System.currentTimeMillis() - commitMs), countNonTransactional);
}
- return result;
- } catch (MigrationException e) {
- rollback(connection);
- throw e;
- } catch (Throwable e) {
- log.log(ERROR, "Perform rollback due to DB migration error", e);
- rollback(connection);
- throw new MigrationException("Error running DB migrations", e);
- } finally {
- table.unlockMigrationTable();
}
+ return result;
+ } catch (MigrationException e) {
+ rollback(connection);
+ throw e;
+ } catch (Throwable e) {
+ log.log(ERROR, "Perform rollback due to DB migration error", e);
+ rollback(connection);
+ throw new MigrationException("Error running DB migrations", e);
} finally {
- close(connection);
+ table.unlockMigrationTable();
}
}
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 93550fe..8dea179 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
@@ -26,7 +26,7 @@ final class MigrationTable {
private static final int AUTO_PATCH_CHECKSUM = -1;
private final MigrationConfig config;
- private final Connection connection;
+ private final MigrationContext context;
private final boolean checkStateOnly;
private boolean earlyChecksumMode;
private final MigrationPlatform platform;
@@ -72,18 +72,18 @@ final class MigrationTable {
private int executionCount;
private boolean patchLegacyChecksums;
private MigrationMetaRow initMetaRow;
- private boolean tableKnownToExist;
+ private final boolean tableKnownToExist;
public MigrationTable(FirstCheck firstCheck, boolean checkStateOnly) {
this.config = firstCheck.config;
this.platform = firstCheck.platform;
- this.connection = firstCheck.connection;
+ this.context = firstCheck.context;
this.schema = firstCheck.schema;
this.table = firstCheck.table;
this.sqlTable = firstCheck.sqlTable;
this.tableKnownToExist = firstCheck.tableKnownToExist;
- this.scriptRunner = new MigrationScriptRunner(connection, platform);
+ this.scriptRunner = new MigrationScriptRunner(context.connection(), platform);
this.checkStateOnly = checkStateOnly;
this.earlyChecksumMode = config.isEarlyChecksumMode();
this.migrations = new LinkedHashMap<>();
@@ -142,7 +142,7 @@ private ScriptTransform createScriptTransform(MigrationConfig config) {
void createIfNeededAndLock() throws SQLException, IOException {
SQLException suppressedException = null;
if (!tableKnownToExist) {
- MigrationSchema.createIfNeeded(config, connection);
+ MigrationSchema.createIfNeeded(config, context.connection());
if (!tableExists()) {
try {
createTable();
@@ -174,14 +174,14 @@ void createIfNeededAndLock() throws SQLException, IOException {
* contain all the executed migrations in that case.
*/
private void obtainLockWithWait() throws SQLException {
- platform.lockMigrationTable(sqlTable, connection);
+ platform.lockMigrationTable(sqlTable, context.connection());
}
/**
* Release a lock on the migration table (MySql, MariaDB only).
*/
void unlockMigrationTable() {
- platform.unlockMigrationTable(sqlTable, connection);
+ platform.unlockMigrationTable(sqlTable, context.connection());
}
/**
@@ -191,12 +191,13 @@ void unlockMigrationTable() {
* executed during the wait for the lock.
*/
private void readExistingMigrations() throws SQLException {
- for (MigrationMetaRow metaRow : platform.readExistingMigrations(sqlTable, connection)) {
+ for (MigrationMetaRow metaRow : platform.readExistingMigrations(sqlTable, context.connection())) {
addMigration(metaRow.version(), metaRow);
}
}
void createTable() throws IOException, SQLException {
+ Connection connection = context.connection();
try {
scriptRunner.runScript(createTableDdl(), "create migration table");
createInitMetaRow().executeInsert(connection, insertSql);
@@ -253,6 +254,7 @@ private ClassLoader classLoader() {
* Return true if the table exists.
*/
boolean tableExists() throws SQLException {
+ Connection connection = context.connection();
String migTable = table;
DatabaseMetaData metaData = connection.getMetaData();
if (metaData.storesUpperCaseIdentifiers()) {
@@ -351,7 +353,7 @@ boolean skipMigration(int checksum, int checksum2, LocalMigrationResource local,
} else if (patchLegacyChecksums && (existing.checksum() == checksum2 || checksum2 == AUTO_PATCH_CHECKSUM)) {
if (!checkStateOnly) {
log.log(INFO, "Auto patch migration, set early mode checksum on {0} to {1,number} from {2,number}", local.location(), checksum, existing.checksum());
- existing.resetChecksum(checksum, connection, updateChecksumSql);
+ existing.resetChecksum(checksum, context.connection(), updateChecksumSql);
}
return true;
@@ -373,7 +375,7 @@ boolean skipMigration(int checksum, int checksum2, LocalMigrationResource local,
private boolean patchResetChecksum(MigrationMetaRow existing, int newChecksum) throws SQLException {
if (isResetOnVersion(existing.version())) {
if (!checkStateOnly) {
- existing.resetChecksum(newChecksum, connection, updateChecksumSql);
+ existing.resetChecksum(newChecksum, context.connection(), updateChecksumSql);
}
return true;
} else {
@@ -405,7 +407,7 @@ private void executeMigration(LocalMigrationResource local, String script, int c
}
if (existing != null) {
existing.rerun(checksum, exeMillis, envUserName, runOn);
- existing.executeUpdate(connection, updateSql);
+ existing.executeUpdate(context.connection(), updateSql);
} else {
insertIntoHistory(local, checksum, exeMillis);
}
@@ -424,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(connection);
+ migration.migrate(context.connection());
} else {
log.log(DEBUG, "run migration {0}", local.location());
scriptRunner.runScript(script, "run migration version: " + local.version());
@@ -435,7 +437,7 @@ private long executeMigration(LocalMigrationResource local, String script) throw
private void insertIntoHistory(LocalMigrationResource local, int checksum, long exeMillis) throws SQLException {
MigrationMetaRow metaRow = createMetaRow(local, checksum, exeMillis);
- metaRow.executeInsert(connection, insertSql);
+ metaRow.executeInsert(context.connection(), insertSql);
addMigration(local.key(), metaRow);
}
@@ -525,7 +527,7 @@ List runAll(List localVersions) throw
}
if (patchLegacyChecksums && !checkStateOnly) {
// only patch the legacy checksums once
- initMetaRow.resetChecksum(EARLY_MODE_CHECKSUM, connection, updateChecksumSql);
+ initMetaRow.resetChecksum(EARLY_MODE_CHECKSUM, context.connection(), updateChecksumSql);
}
return checkMigrations;
}
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 99723e6..81b72c5 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
@@ -44,7 +44,7 @@ public void shutdown() {
private MigrationTable migrationTable(Connection conn) {
- var fc = new FirstCheck(config, conn, platform);
+ var fc = new FirstCheck(config, new DefaultMigrationContext(config, conn), platform);
return new MigrationTable(fc, false);
}
diff --git a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java
index f916dd0..4b1e6bb 100644
--- a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java
+++ b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java
@@ -12,7 +12,7 @@ public class MigrationTable2Test {
private static MigrationTable migrationTable(MigrationConfig config) {
- var fc = new FirstCheck(config, null, new MigrationPlatform());
+ var fc = new FirstCheck(config, new DefaultMigrationContext(config, null), new MigrationPlatform());
return new MigrationTable(fc, false);
}
diff --git a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableAsyncTest.java b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableAsyncTest.java
index 38bc0b6..a6795d8 100644
--- a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableAsyncTest.java
+++ b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableAsyncTest.java
@@ -204,7 +204,7 @@ private void runTest(boolean withExisting) throws SQLException, InterruptedExcep
}
private static MigrationTable migrationTable(MigrationPlatform platform, Connection connection) {
- var fc = new FirstCheck(config, connection, platform);
+ var fc = new FirstCheck(config, new DefaultMigrationContext(config, connection), platform);
return new MigrationTable(fc, false);
}
diff --git a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableCreateTableRaceTest.java b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableCreateTableRaceTest.java
index b5d226c..99b3d83 100644
--- a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableCreateTableRaceTest.java
+++ b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableCreateTableRaceTest.java
@@ -51,7 +51,7 @@ void testRaceCondition_expect_loserOfCreateTableCanPerformTableExistsCheck() thr
try (Connection conn = dataSource.getConnection()) {
dropTable(conn);
- var fc = new FirstCheck(config, conn, platform);
+ var fc = new FirstCheck(config, new DefaultMigrationContext(config, conn), platform);
MigrationTable table = new MigrationTable(fc, false);
table.createTable();
try {
diff --git a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableTestDb2.java b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableTestDb2.java
index 4777ed4..5a35392 100644
--- a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableTestDb2.java
+++ b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTableTestDb2.java
@@ -44,7 +44,7 @@ public void testMigrationTableBase() throws Exception {
config.setMigrationPath("dbmig");
try (Connection conn = dataSource.getConnection()) {
- var fc = new FirstCheck(config, conn, platform);
+ var fc = new FirstCheck(config, new DefaultMigrationContext(config, conn), platform);
MigrationTable table = new MigrationTable(fc, false);
table.createIfNeededAndLock();
table.unlockMigrationTable();