create primary keys on directly on table creation instead of a seperate operation

This commit is contained in:
wea_ondara
2022-11-27 07:58:29 +01:00
parent 1f90bc551b
commit 3e581a9e4a
9 changed files with 56 additions and 54 deletions

View File

@@ -24,8 +24,8 @@ import java.util.List;
public class MigrationBuilder { public class MigrationBuilder {
private final List<MigrationOperation.Builder<?>> operations = new ArrayList<>(); private final List<MigrationOperation.Builder<?>> operations = new ArrayList<>();
public AddTableOperation.Builder addTable(String table, List<AddFieldOperation.Builder> fields) { public AddTableOperation.Builder addTable(String table, List<AddFieldOperation.Builder> fields, AddPrimaryKeyOperation.Builder primaryKey) {
var op = new AddTableOperation.Builder(table, fields); var op = new AddTableOperation.Builder(table, fields, primaryKey);
operations.add(op); operations.add(op);
return op; return op;
} }

View File

@@ -14,20 +14,23 @@ import java.util.List;
public class AddTableOperation implements MigrationOperation { public class AddTableOperation implements MigrationOperation {
private final String table; private final String table;
private final List<AddFieldOperation.Builder> fields; private final List<AddFieldOperation.Builder> fields;
private final AddPrimaryKeyOperation.Builder primaryKey;
@EqualsAndHashCode @EqualsAndHashCode
@ToString @ToString
public static class Builder implements MigrationOperation.Builder<AddTableOperation> { public static class Builder implements MigrationOperation.Builder<AddTableOperation> {
private final String table; private final String table;
private final List<AddFieldOperation.Builder> fields; private final List<AddFieldOperation.Builder> fields;
private final AddPrimaryKeyOperation.Builder primaryKey;
public Builder(String table, List<AddFieldOperation.Builder> fields) { public Builder(String table, List<AddFieldOperation.Builder> fields, AddPrimaryKeyOperation.Builder primaryKey) {
this.table = table; this.table = table;
this.fields = fields; this.fields = fields;
this.primaryKey = primaryKey;
} }
public AddTableOperation build() { public AddTableOperation build() {
return new AddTableOperation(table, fields); return new AddTableOperation(table, fields, primaryKey);
} }
} }
} }

View File

@@ -20,7 +20,7 @@ public class M00010101000000_MigrationsLogTableMigration implements Migration {
migrationBuilder.addTable(tableName, List.of( migrationBuilder.addTable(tableName, List.of(
new AddFieldOperation.Builder(tableName, "migration").notNull(true).sqlType(typeMapper.map(String.class.getName()).orElseThrow()), 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()) new AddFieldOperation.Builder(tableName, "version").notNull(true).sqlType(typeMapper.map(String.class.getName()).orElseThrow())
)); ), null);
migrationBuilder.addUniqueKey("U_" + tableName + "_migration", tableName, List.of("migration")); migrationBuilder.addUniqueKey("U_" + tableName + "_migration", tableName, List.of("migration"));
} }

View File

@@ -179,7 +179,7 @@ class MigrationApplierTest {
var operationBuilder = new MigrationOperation.Builder[]{null}; var operationBuilder = new MigrationOperation.Builder[]{null};
var operation = new MigrationOperation[]{null}; var operation = new MigrationOperation[]{null};
doAnswer(invok -> { doAnswer(invok -> {
operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of()); operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of(), null);
operation[0] = operationBuilder[0].build(); operation[0] = operationBuilder[0].build();
return null; return null;
}).when(migration).up(any(MigrationBuilder.class)); }).when(migration).up(any(MigrationBuilder.class));
@@ -207,7 +207,7 @@ class MigrationApplierTest {
var operationBuilder = new MigrationOperation.Builder[]{null}; var operationBuilder = new MigrationOperation.Builder[]{null};
var operation = new MigrationOperation[]{null}; var operation = new MigrationOperation[]{null};
doAnswer(invok -> { doAnswer(invok -> {
operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of()); operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of(), null);
operation[0] = operationBuilder[0].build(); operation[0] = operationBuilder[0].build();
return null; return null;
}).when(migration).up(any(MigrationBuilder.class)); }).when(migration).up(any(MigrationBuilder.class));
@@ -238,7 +238,7 @@ class MigrationApplierTest {
var operationBuilder = new MigrationOperation.Builder[]{null}; var operationBuilder = new MigrationOperation.Builder[]{null};
var operation = new MigrationOperation[]{null}; var operation = new MigrationOperation[]{null};
doAnswer(invok -> { doAnswer(invok -> {
operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of()); operationBuilder[0] = ((MigrationBuilder) invok.getArguments()[0]).addTable("test", List.of(), null);
operation[0] = operationBuilder[0].build(); operation[0] = operationBuilder[0].build();
return null; return null;
}).when(migration).up(any(MigrationBuilder.class)); }).when(migration).up(any(MigrationBuilder.class));

View File

@@ -152,11 +152,18 @@ public class MigrationBuilderGenerator {
private String addTableOp(AddTableOperation op) { private String addTableOp(AddTableOperation op) {
imports.add(List.class); imports.add(List.class);
imports.add(AddFieldOperation.class); imports.add(AddFieldOperation.class);
imports.add(AddFieldOperation.Builder.class); if (op.getPrimaryKey() != null) {
imports.add(AddPrimaryKeyOperation.class);
}
return "mb.addTable(\"" + op.getTable() + "\", List.of(\n" return "mb.addTable(\"" + op.getTable() + "\", List.of(\n"
+ op.getFields().stream() + op.getFields().stream()
/**/.map(f -> " new AddFieldOperation.Builder(\"" + op.getTable() + "\", \"" + f.build().getField() + "\")" + addFieldOpOptional(f.build()).replace("\n", "\n ")) /**/.map(f -> " new AddFieldOperation.Builder(\"" + op.getTable() + "\", \"" + f.build().getField() + "\")" + addFieldOpOptional(f.build()).replace("\n", "\n "))
/**/.collect(Collectors.joining(",\n")) + "\n" /**/.collect(Collectors.joining(",\n")) + "),\n"
+ (op.getPrimaryKey() != null
? " new AddPrimaryKeyOperation.Builder(\"" + op.getPrimaryKey().build().getName() + "\", \"" + op.getPrimaryKey().build().getTable() + "\", List.of("
+ op.getPrimaryKey().build().getFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", "))
+ ")"
: " null")
+ "));"; + "));";
} }

View File

@@ -146,7 +146,10 @@ public class MigrationCreator {
.map(e -> new AddFieldOperation.Builder(toEntity.getName(), e.getName()) .map(e -> new AddFieldOperation.Builder(toEntity.getName(), e.getName())
.notNull(e.isNotNull()) .notNull(e.isNotNull())
.sqlType(getSqlType(e))) .sqlType(getSqlType(e)))
.toList() .toList(),
Optional.ofNullable(toEntity.getPrimaryKey())
.map(e -> new AddPrimaryKeyOperation.Builder(e.getName(), toEntity.getName(), e.getFields().stream().map(DbField::getName).toList()))
.orElse(null)
)); ));
} }
} }
@@ -265,7 +268,9 @@ public class MigrationCreator {
var involvedFields = toEntity.getFields(); var involvedFields = toEntity.getFields();
var involvedForeignKeys = originalEntity.getPrimaryKey() != null var involvedForeignKeys = originalEntity.getPrimaryKey() != null
&& originalEntity.getPrimaryKey().getFields().stream().anyMatch(involvedFields::contains); && originalEntity.getPrimaryKey().getFields().stream().anyMatch(involvedFields::contains);
if (involvedForeignKeys) { var alreadyCreatedDuringTableCreation = steps.stream().anyMatch(e -> e instanceof AddTableOperation.Builder ato
&& ato.build().getTable().equals(originalEntity.getPrimaryKey().getEntity().getName()));
if (involvedForeignKeys && !alreadyCreatedDuringTableCreation) {
steps.add(new AddPrimaryKeyOperation.Builder( steps.add(new AddPrimaryKeyOperation.Builder(
originalEntity.getPrimaryKey().getName(), originalEntity.getPrimaryKey().getName(),
originalEntity.getPrimaryKey().getEntity().getName(), originalEntity.getPrimaryKey().getEntity().getName(),
@@ -347,6 +352,12 @@ public class MigrationCreator {
private void addPrimaryKeyDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder<?>> steps) { private void addPrimaryKeyDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder<?>> steps) {
for (var fromEntity : fromReduced.getEntities()) { for (var fromEntity : fromReduced.getEntities()) {
var toEntity = toReduced.getEntity(fromEntity.getTypeName());
if (toEntity == null) {
//table will be dropped -> no need to drop the primary key separately
continue;
}
var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null); var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = fromEntity.getFields(); var involvedFields = fromEntity.getFields();

View File

@@ -46,19 +46,17 @@ public class MigrationCreatorAddEntityTest extends MigrationCreatorTestBase {
} }
private void validateUp(MigrationCreator.Result res) { private void validateUp(MigrationCreator.Result res) {
assertEquals(3, res.getStepsUp().size()); assertEquals(2, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream() assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddTableOperation o .filter(e -> e instanceof AddTableOperation o
&& o.getTable().equals("AddedEntity") && o.getTable().equals("AddedEntity")
&& o.getFields().size() == 2 && o.getFields().size() == 2
&& o.getFields().stream().filter(f -> f.build().getField().equals("id")).count() == 1 && o.getFields().stream().filter(f -> f.build().getField().equals("id")).count() == 1
&& o.getFields().stream().filter(f -> f.build().getField().equals("addedField")).count() == 1) && o.getFields().stream().filter(f -> f.build().getField().equals("addedField")).count() == 1
.count()); && o.getPrimaryKey() != null
assertEquals(1, res.getStepsUp().stream() && o.getPrimaryKey().build().getTable().equals("AddedEntity")
.filter(e -> e instanceof AddPrimaryKeyOperation o && o.getPrimaryKey().build().getFields().size() == 1
&& o.getTable().equals("AddedEntity") && o.getPrimaryKey().build().getFields().get(0).equals("id"))
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("id"))
.count()); .count());
assertEquals(1, res.getStepsUp().stream() assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o .filter(e -> e instanceof AddForeignKeyOperation o
@@ -72,17 +70,12 @@ public class MigrationCreatorAddEntityTest extends MigrationCreatorTestBase {
} }
private void validateDown(MigrationCreator.Result res) { private void validateDown(MigrationCreator.Result res) {
assertEquals(3, res.getStepsDown().size()); assertEquals(2, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream() assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o .filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("AddedEntity") && o.getTable().equals("AddedEntity")
&& o.getName().equals("FK_AddedEntity_AddedEntity_addedField")) && o.getName().equals("FK_AddedEntity_AddedEntity_addedField"))
.count()); .count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("AddedEntity")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsDown().stream() assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropTableOperation o .filter(e -> e instanceof DropTableOperation o
&& o.getTable().equals("AddedEntity")) && o.getTable().equals("AddedEntity"))

View File

@@ -6,7 +6,6 @@ import jef.model.ModelBuilder;
import jef.model.annotations.Clazz; import jef.model.annotations.Clazz;
import jef.model.annotations.Id; import jef.model.annotations.Id;
import jef.model.migration.operation.AddForeignKeyOperation; import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation; import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.DropConstraintOperation; import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropTableOperation; import jef.model.migration.operation.DropTableOperation;
@@ -39,12 +38,16 @@ public class MigrationCreatorInitialMigrationTest extends MigrationCreatorTestBa
} }
private void validateUp(MigrationCreator.Result res) { private void validateUp(MigrationCreator.Result res) {
assertEquals(5, res.getStepsUp().size()); assertEquals(3, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream() assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddTableOperation o .filter(e -> e instanceof AddTableOperation o
&& o.getTable().equals("objects1") && o.getTable().equals("objects1")
&& o.getFields().size() == 1 && o.getFields().size() == 1
&& o.getFields().get(0).build().getField().equals("i")) && o.getFields().get(0).build().getField().equals("i")
&& o.getPrimaryKey() != null
&& o.getPrimaryKey().build().getTable().equals("objects1")
&& o.getPrimaryKey().build().getFields().size() == 1
&& o.getPrimaryKey().build().getFields().get(0).equals("i"))
.count()); .count());
assertEquals(1, res.getStepsUp().stream() assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddTableOperation o .filter(e -> e instanceof AddTableOperation o
@@ -55,19 +58,11 @@ public class MigrationCreatorInitialMigrationTest extends MigrationCreatorTestBa
&& o.getFields().stream().filter(f -> f.build().getField().equals("i")).count() == 1 && o.getFields().stream().filter(f -> f.build().getField().equals("i")).count() == 1
&& o.getFields().stream().filter(f -> f.build().getField().equals("l")).count() == 1 && o.getFields().stream().filter(f -> f.build().getField().equals("l")).count() == 1
&& o.getFields().stream().filter(f -> f.build().getField().equals("o")).count() == 1 && o.getFields().stream().filter(f -> f.build().getField().equals("o")).count() == 1
&& o.getFields().stream().filter(f -> f.build().getField().equals("nestedI")).count() == 1) && o.getFields().stream().filter(f -> f.build().getField().equals("nestedI")).count() == 1
.count()); && o.getPrimaryKey() != null
assertEquals(1, res.getStepsUp().stream() && o.getPrimaryKey().build().getTable().equals("TestClass")
.filter(e -> e instanceof AddPrimaryKeyOperation o && o.getPrimaryKey().build().getFields().size() == 1
&& o.getTable().equals("objects1") && o.getPrimaryKey().build().getFields().get(0).equals("i"))
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("TestClass")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count()); .count());
assertEquals(1, res.getStepsUp().stream() assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o .filter(e -> e instanceof AddForeignKeyOperation o
@@ -81,22 +76,12 @@ public class MigrationCreatorInitialMigrationTest extends MigrationCreatorTestBa
} }
private void validateDown(MigrationCreator.Result res) { private void validateDown(MigrationCreator.Result res) {
assertEquals(5, res.getStepsDown().size()); assertEquals(3, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream() assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o .filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("TestClass") && o.getTable().equals("TestClass")
&& o.getName().equals("FK_TestClass_objects1_nestedI")) && o.getName().equals("FK_TestClass_objects1_nestedI"))
.count()); .count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("TestClass")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("objects1")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsDown().stream() assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropTableOperation o .filter(e -> e instanceof DropTableOperation o

View File

@@ -102,7 +102,10 @@ public class MysqlMigrationOperationTranslator implements MigrationOperationTran
var f = e.build(); var f = e.build();
return "`" + f.getField() + "` " + f.getSqlType() + (f.isNotNull() ? " NOT NULL" : ""); return "`" + f.getField() + "` " + f.getSqlType() + (f.isNotNull() ? " NOT NULL" : "");
}).collect(Collectors.joining(", ")) }).collect(Collectors.joining(", "))
+ ")"); //TODO default collocation from database config or operation, field collation, primary key, constraints + (op.getPrimaryKey() != null
? ", PRIMARY KEY (" + op.getPrimaryKey().build().getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")"
: "")
+ ")"); //TODO default collocation from database config or operation, field collation, constraints
} }
private static PreparedStatement translateAddUniqueKeyOperation(Connection connection, MigrationOperation operation) throws SQLException { private static PreparedStatement translateAddUniqueKeyOperation(Connection connection, MigrationOperation operation) throws SQLException {