create migrations table via built-in migration
This commit is contained in:
@@ -12,11 +12,12 @@ public class DatabaseOptions {
|
|||||||
protected final String password;
|
protected final String password;
|
||||||
|
|
||||||
protected final String migrationsPackage;
|
protected final String migrationsPackage;
|
||||||
|
protected final String migrationsTableName;
|
||||||
|
|
||||||
protected final String host;
|
protected final String host;
|
||||||
protected final String database;
|
protected final String database;
|
||||||
|
|
||||||
public DatabaseOptions(String url, String user, String password, String migrationsPackage) {
|
public DatabaseOptions(String url, String user, String password, String migrationsPackage, String migrationsTableName) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
@@ -24,6 +25,7 @@ public class DatabaseOptions {
|
|||||||
host = extractHost(url);
|
host = extractHost(url);
|
||||||
database = extractDatabase(url);
|
database = extractDatabase(url);
|
||||||
this.migrationsPackage = migrationsPackage;
|
this.migrationsPackage = migrationsPackage;
|
||||||
|
this.migrationsTableName = migrationsTableName == null || migrationsTableName.isBlank() ? "__jef_migration_log" : migrationsTableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String extractHost(String url) {
|
private static String extractHost(String url) {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package jef.platform.base.migration;
|
||||||
|
|
||||||
|
import jef.model.migration.Migration;
|
||||||
|
import jef.model.migration.MigrationBuilder;
|
||||||
|
import jef.model.migration.operation.AddFieldOperation;
|
||||||
|
import jef.platform.base.SqlTypeMapper;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode//needed for test
|
||||||
|
public class M00010101000000_MigrationsLogTableMigration implements Migration {
|
||||||
|
private final SqlTypeMapper typeMapper;
|
||||||
|
private final String tableName;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void up(MigrationBuilder migrationBuilder) {//TODO add timestamp later
|
||||||
|
migrationBuilder.addTable(tableName, List.of(
|
||||||
|
new AddFieldOperation.Builder(tableName, "migration").notNull(true).sqlType(typeMapper.map(String.class.getName()).orElseThrow()),
|
||||||
|
new AddFieldOperation.Builder(tableName, "version").notNull(true).sqlType(typeMapper.map(String.class.getName()).orElseThrow())
|
||||||
|
));
|
||||||
|
migrationBuilder.addUniqueKey("U_" + tableName + "_migration", tableName, List.of("migration"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void down(MigrationBuilder migrationBuilder) {
|
||||||
|
migrationBuilder.dropConstraint("U_" + tableName + "_migration", tableName);
|
||||||
|
migrationBuilder.dropTable(tableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,7 +121,9 @@ public abstract class MigrationApplier {
|
|||||||
|
|
||||||
protected abstract boolean migrationsTableExists() throws SQLException;
|
protected abstract boolean migrationsTableExists() throws SQLException;
|
||||||
|
|
||||||
protected abstract void createMigrationsTable() throws SQLException;//TODO use a hardcoded migration
|
protected void createMigrationsTable() throws MigrationException {
|
||||||
|
applyMigration(new M00010101000000_MigrationsLogTableMigration(sqlPlatform.getTypeMapper(), options.getMigrationsTableName()));
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract List<String> getAppliedMigrations() throws SQLException;//TODO dbset to load later
|
protected abstract List<String> getAppliedMigrations() throws SQLException;//TODO dbset to load later
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class NullMigrationApplier extends MigrationApplier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void createMigrationsTable() throws SQLException {
|
protected void createMigrationsTable() throws MigrationException {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import jef.model.migration.MigrationBuilder;
|
|||||||
import jef.model.migration.operation.MigrationOperation;
|
import jef.model.migration.operation.MigrationOperation;
|
||||||
import jef.platform.SqlPlatform;
|
import jef.platform.SqlPlatform;
|
||||||
import jef.platform.base.DatabaseOptions;
|
import jef.platform.base.DatabaseOptions;
|
||||||
|
import jef.platform.base.SqlTypeMapper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -347,5 +348,52 @@ class MigrationApplierTest {
|
|||||||
|
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
|
|
||||||
}//TODO after function rewrite
|
}//TODO after function rewrite
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createMigrationsTable_success() throws MigrationException {
|
||||||
|
//setup
|
||||||
|
var mapper = mock(SqlTypeMapper.class);
|
||||||
|
doReturn(mapper).when(platform).getTypeMapper();
|
||||||
|
|
||||||
|
var migrationTableName = "migrationtable";
|
||||||
|
doReturn(migrationTableName).when(options).getMigrationsTableName();
|
||||||
|
doNothing().when(applier).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
|
||||||
|
|
||||||
|
//test
|
||||||
|
applier.createMigrationsTable();
|
||||||
|
|
||||||
|
//assert
|
||||||
|
verify(applier, times(1)).createMigrationsTable();
|
||||||
|
verify(platform, times(1)).getTypeMapper();
|
||||||
|
verify(options, times(1)).getMigrationsTableName();
|
||||||
|
verify(applier, times(1)).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
|
||||||
|
verifyNoMoreInteractions(allMocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createMigrationsTable_applyMigrationThrowsMigrationException() throws MigrationException {
|
||||||
|
//setup
|
||||||
|
var mapper = mock(SqlTypeMapper.class);
|
||||||
|
doReturn(mapper).when(platform).getTypeMapper();
|
||||||
|
|
||||||
|
var migrationTableName = "migrationtable";
|
||||||
|
doReturn(migrationTableName).when(options).getMigrationsTableName();
|
||||||
|
|
||||||
|
var exception = new MigrationException("reason");
|
||||||
|
doThrow(exception).when(applier).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
|
||||||
|
|
||||||
|
//test
|
||||||
|
var thrown = assertThrows(MigrationException.class, () -> applier.createMigrationsTable());
|
||||||
|
|
||||||
|
//assert
|
||||||
|
assertSame(exception, thrown);
|
||||||
|
|
||||||
|
verify(applier, times(1)).createMigrationsTable();
|
||||||
|
verify(platform, times(1)).getTypeMapper();
|
||||||
|
verify(options, times(1)).getMigrationsTableName();
|
||||||
|
verify(applier, times(1)).applyMigration(new M00010101000000_MigrationsLogTableMigration(mapper, migrationTableName));
|
||||||
|
verifyNoMoreInteractions(allMocks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ class NullMigrationApplierTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createMigrationsTable() throws SQLException {
|
void createMigrationsTable() throws MigrationException {
|
||||||
NullMigrationApplier.INSTANCE.createMigrationsTable();
|
NullMigrationApplier.INSTANCE.createMigrationsTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
package jef.platform.mysql.migration;
|
package jef.platform.mysql.migration;
|
||||||
|
|
||||||
import jef.platform.base.DatabaseOptions;
|
|
||||||
import jef.model.migration.Migration;
|
import jef.model.migration.Migration;
|
||||||
import jef.model.migration.operation.AddFieldOperation;
|
|
||||||
import jef.model.migration.operation.AddTableOperation;
|
|
||||||
import jef.model.migration.operation.AddUniqueKeyOperation;
|
|
||||||
import jef.model.migration.operation.MigrationOperation;
|
|
||||||
import jef.platform.SqlPlatform;
|
import jef.platform.SqlPlatform;
|
||||||
|
import jef.platform.base.DatabaseOptions;
|
||||||
import jef.platform.base.migration.MigrationApplier;
|
import jef.platform.base.migration.MigrationApplier;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
@@ -21,7 +17,7 @@ public class MysqlMigrationApplier extends MigrationApplier {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void insertMigrationLog(Migration m) throws SQLException {
|
protected void insertMigrationLog(Migration m) throws SQLException {
|
||||||
try (var stmt = connection.prepareStatement("INSERT INTO `__jef_migration_log` (`name`, `version`) VALUES (?, ?)")) {//TODO configurable log table name
|
try (var stmt = connection.prepareStatement("INSERT INTO `" + options.getMigrationsTableName() + "` (`name`, `version`) VALUES (?, ?)")) {//TODO configurable log table name
|
||||||
stmt.setString(1, m.getClass().getSimpleName());
|
stmt.setString(1, m.getClass().getSimpleName());
|
||||||
stmt.setString(2, "0.1"); //TODO insert actual library version
|
stmt.setString(2, "0.1"); //TODO insert actual library version
|
||||||
stmt.executeUpdate();
|
stmt.executeUpdate();
|
||||||
@@ -33,7 +29,7 @@ public class MysqlMigrationApplier extends MigrationApplier {
|
|||||||
try (var stmt = connection.prepareStatement("SHOW TABLES");
|
try (var stmt = connection.prepareStatement("SHOW TABLES");
|
||||||
var res = stmt.executeQuery()) {
|
var res = stmt.executeQuery()) {
|
||||||
while (res.next()) {
|
while (res.next()) {
|
||||||
if (res.getString(1).equals("__jef_migration_log")) {
|
if (res.getString(1).equals(options.getMigrationsTableName())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,23 +37,6 @@ public class MysqlMigrationApplier extends MigrationApplier {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void createMigrationsTable() throws SQLException {
|
|
||||||
var table = "__jef_migration_log";
|
|
||||||
var ops = List.of(
|
|
||||||
new AddTableOperation.Builder(table, List.of(
|
|
||||||
new AddFieldOperation.Builder(table, "name").notNull(true).sqlType("VARCHAR(255)"),
|
|
||||||
new AddFieldOperation.Builder(table, "version").notNull(true).sqlType("VARCHAR(255)")
|
|
||||||
)),
|
|
||||||
new AddUniqueKeyOperation.Builder("U_" + table + "_name", table, List.of("name"))
|
|
||||||
);
|
|
||||||
for (MigrationOperation.Builder<? extends MigrationOperation> e : ops) {
|
|
||||||
try (var stmt = sqlPlatform.getTranslator().translate(connection, e.build())) {
|
|
||||||
stmt.executeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getAppliedMigrations() throws SQLException {
|
protected List<String> getAppliedMigrations() throws SQLException {
|
||||||
var ret = new ArrayList<String>();
|
var ret = new ArrayList<String>();
|
||||||
@@ -65,11 +44,11 @@ public class MysqlMigrationApplier extends MigrationApplier {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var stmt = connection.prepareStatement("SELECT `name` FROM `__jef_migration_log`");
|
try (var stmt = connection.prepareStatement("SELECT `migration` FROM `" + options.getMigrationsTableName() + "`");
|
||||||
var res = stmt.executeQuery()) {
|
var res = stmt.executeQuery()) {
|
||||||
while (res.next()) {
|
while (res.next()) {
|
||||||
if (res.getString(1).equals("__jef_migration_log")) {
|
if (!res.getString(1).equals(options.getMigrationsTableName())) {
|
||||||
ret.add(res.getString("name"));
|
ret.add(res.getString("migration"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class MysqlMigrationTest {
|
|||||||
generateInitialMigration();
|
generateInitialMigration();
|
||||||
compileInitialMigration();
|
compileInitialMigration();
|
||||||
Class.forName("com.mysql.cj.jdbc.Driver").getDeclaredConstructor().newInstance();
|
Class.forName("com.mysql.cj.jdbc.Driver").getDeclaredConstructor().newInstance();
|
||||||
var dboptions = new DatabaseOptions("jdbc:mysql://localhost/test", "test", "password", getClass().getSimpleName());
|
var dboptions = new DatabaseOptions("jdbc:mysql://localhost/test", "test", "password", getClass().getSimpleName(), null);
|
||||||
var ctxoptions = new DbContextOptions(dboptions);
|
var ctxoptions = new DbContextOptions(dboptions);
|
||||||
var conn = DriverManager.getConnection(dboptions.getUrl(), dboptions.getUser(), dboptions.getPassword());
|
var conn = DriverManager.getConnection(dboptions.getUrl(), dboptions.getUser(), dboptions.getPassword());
|
||||||
var sqlPlatform = new MysqlPlatform();
|
var sqlPlatform = new MysqlPlatform();
|
||||||
|
|||||||
Reference in New Issue
Block a user