From 195f3d0148c5c68f4468e310c18102d42dcd57c0 Mon Sep 17 00:00:00 2001 From: wea_ondara Date: Sun, 27 Nov 2022 08:10:52 +0100 Subject: [PATCH] added database value generation with identity generation --- core/src/main/java/jef/model/DbField.java | 14 +++++++--- .../main/java/jef/model/DbFieldBuilder.java | 6 +++++ .../src/main/java/jef/model/ModelBuilder.java | 1 + .../java/jef/model/ModelBuilderOptions.java | 4 ++- .../java/jef/model/PrimaryKeyInitializer.java | 5 ++++ .../java/jef/model/annotations/Generated.java | 21 +++++++++++++++ .../processors/GeneratedProcessor.java | 27 +++++++++++++++++++ .../operation/AddFieldOperation.java | 12 ++++++++- .../operation/UpdateFieldOperation.java | 7 ++--- .../java/jef/model/ModelBuilderCloneTest.java | 7 ++++- .../creator/MigrationBuilderGenerator.java | 10 ++++--- .../migration/creator/MigrationCreator.java | 6 +++-- .../creator/ModelBuilderGenerator.java | 5 ++++ .../MysqlMigrationOperationTranslator.java | 8 +++++- 14 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/jef/model/annotations/Generated.java create mode 100644 core/src/main/java/jef/model/annotations/processors/GeneratedProcessor.java diff --git a/core/src/main/java/jef/model/DbField.java b/core/src/main/java/jef/model/DbField.java index e155db0..56870f0 100644 --- a/core/src/main/java/jef/model/DbField.java +++ b/core/src/main/java/jef/model/DbField.java @@ -1,5 +1,6 @@ package jef.model; +import jef.model.annotations.Generated; import jef.serializable.SerializableObject; import jef.util.Check; import lombok.AccessLevel; @@ -19,12 +20,13 @@ public class DbField { private Class type; @Setter(value = AccessLevel.PACKAGE) private Field field; - private boolean isModelField; - private boolean isDatabaseField; + private boolean isModelField;//TODO get rid of this field + private boolean isDatabaseField;//TODO get rid of this field private DbField exposingForeignKeyOf; private String name; private boolean notNull = false; private String sqlType; + private Generated.Type generated = Generated.Type.NONE; DbField(DbEntity entity, String name, String typeName) { this.entity = Check.notNull(entity, "entity"); @@ -85,6 +87,11 @@ public class DbField { this.sqlType = sqlType; } + public void setGenerated(Generated.Type generated) { + Check.notNull(generated, "generated"); + this.generated = generated; + } + @Override public boolean equals(Object o) { if (!equalsCommon(o)) { @@ -108,6 +115,7 @@ public class DbField { && notNull == dbField.notNull && entity.getName().equals(dbField.entity.getName()) && typeName.equals(dbField.typeName) + && generated == dbField.generated // && Objects.equals(type, dbField.type) && Objects.equals(exposingForeignKeyOf == null ? null : exposingForeignKeyOf.getName(), dbField.exposingForeignKeyOf == null ? null : dbField.exposingForeignKeyOf.getName()); @@ -117,7 +125,7 @@ public class DbField { public int hashCode() { return Objects.hash(entity.getName(), typeName, type, field == null ? null : field.getName(), isModelField, isDatabaseField, exposingForeignKeyOf == null ? null : exposingForeignKeyOf.getName(), - name, notNull); + name, notNull, generated); } @Override diff --git a/core/src/main/java/jef/model/DbFieldBuilder.java b/core/src/main/java/jef/model/DbFieldBuilder.java index dc2fdaf..23eaf72 100644 --- a/core/src/main/java/jef/model/DbFieldBuilder.java +++ b/core/src/main/java/jef/model/DbFieldBuilder.java @@ -1,5 +1,6 @@ package jef.model; +import jef.model.annotations.Generated; import jef.model.constraints.IndexConstraint; import jef.model.constraints.KeyConstraint; import jef.model.constraints.UniqueKeyConstraint; @@ -41,6 +42,11 @@ public class DbFieldBuilder { return this; } + public DbFieldBuilder generated(Generated.Type type) { + field.setGenerated(type); + return this; + } + public void isUnique() { isUnique(true); } diff --git a/core/src/main/java/jef/model/ModelBuilder.java b/core/src/main/java/jef/model/ModelBuilder.java index 5ad9734..4551e0f 100644 --- a/core/src/main/java/jef/model/ModelBuilder.java +++ b/core/src/main/java/jef/model/ModelBuilder.java @@ -196,6 +196,7 @@ public class ModelBuilder { nf.setType(e.getType()); nf.setSqlType(e.getSqlType()); nf.setNotNull(e.isNotNull()); + nf.setGenerated(e.getGenerated()); nf.setModelField(e.isModelField()); nf.setDatabaseField(e.isDatabaseField()); return nf; diff --git a/core/src/main/java/jef/model/ModelBuilderOptions.java b/core/src/main/java/jef/model/ModelBuilderOptions.java index 363ddfa..f230a27 100644 --- a/core/src/main/java/jef/model/ModelBuilderOptions.java +++ b/core/src/main/java/jef/model/ModelBuilderOptions.java @@ -2,6 +2,7 @@ package jef.model; import jef.model.annotations.processors.AnnotationProcessor; import jef.model.annotations.processors.ForeignKeyProcessor; +import jef.model.annotations.processors.GeneratedProcessor; import jef.model.annotations.processors.IndexProcessor; import jef.model.annotations.processors.KeyProcessor; import jef.model.annotations.processors.NotNullProcessor; @@ -28,7 +29,8 @@ public class ModelBuilderOptions { UniqueProcessor.INSTANCE, IndexProcessor.INSTANCE, KeyProcessor.INSTANCE, - ForeignKeyProcessor.INSTANCE + ForeignKeyProcessor.INSTANCE, + GeneratedProcessor.INSTANCE )); this.contextOptions = new DbContextOptions(null);//TODO } diff --git a/core/src/main/java/jef/model/PrimaryKeyInitializer.java b/core/src/main/java/jef/model/PrimaryKeyInitializer.java index a674788..594a15e 100644 --- a/core/src/main/java/jef/model/PrimaryKeyInitializer.java +++ b/core/src/main/java/jef/model/PrimaryKeyInitializer.java @@ -1,5 +1,6 @@ package jef.model; +import jef.model.annotations.Generated; import jef.model.annotations.Id; import jef.model.annotations.Transient; @@ -58,5 +59,9 @@ class PrimaryKeyInitializer { var dbfields = idFields.stream().map(e -> entity.field(e).getField().getName()).toList(); entity.hasOne(dbfields.toArray(String[]::new)).isPrimaryKey(true); } + + if (idFields.size() == 1) {//auto enable identity generation for primary key + entity.field(idFields.get(0)).generated(Generated.Type.IDENTITY); + } } } diff --git a/core/src/main/java/jef/model/annotations/Generated.java b/core/src/main/java/jef/model/annotations/Generated.java new file mode 100644 index 0000000..3ab3aed --- /dev/null +++ b/core/src/main/java/jef/model/annotations/Generated.java @@ -0,0 +1,21 @@ +package jef.model.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Generated { + Type value() default Type.IDENTITY; + + enum Type { + NONE,//no value is generated + IDENTITY,//value is generated on insert +// COMPUTED,//value is generated on insert and update + ; + } +} diff --git a/core/src/main/java/jef/model/annotations/processors/GeneratedProcessor.java b/core/src/main/java/jef/model/annotations/processors/GeneratedProcessor.java new file mode 100644 index 0000000..61d2335 --- /dev/null +++ b/core/src/main/java/jef/model/annotations/processors/GeneratedProcessor.java @@ -0,0 +1,27 @@ +package jef.model.annotations.processors; + +import jef.model.DbEntityBuilder; +import jef.model.DbFieldBuilder; +import jef.model.ModelBuilder; +import jef.model.annotations.Generated; +import jef.serializable.SerializableObject; + +public class GeneratedProcessor implements AnnotationProcessor { + public static final GeneratedProcessor INSTANCE = new GeneratedProcessor(); + + @Override + public void apply(ModelBuilder mb) { + for (DbEntityBuilder entity : mb.entities()) { + for (DbFieldBuilder field : entity.fields()) { + if (field.getField().getField() == null) { + continue; + } + var annotation = field.getField().getField().getAnnotation(Generated.class); + if (annotation == null) { + continue; + } + field.generated(annotation.value()); + } + } + } +} diff --git a/core/src/main/java/jef/model/migration/operation/AddFieldOperation.java b/core/src/main/java/jef/model/migration/operation/AddFieldOperation.java index 3cac0dc..49dbe6c 100644 --- a/core/src/main/java/jef/model/migration/operation/AddFieldOperation.java +++ b/core/src/main/java/jef/model/migration/operation/AddFieldOperation.java @@ -1,5 +1,7 @@ package jef.model.migration.operation; +import jef.model.annotations.Generated; +import jef.util.Check; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -14,6 +16,7 @@ public class AddFieldOperation implements MigrationOperation { protected final String field; protected final String sqlType; protected final boolean notNull; + protected final Generated.Type generated; @EqualsAndHashCode @ToString @@ -22,6 +25,7 @@ public class AddFieldOperation implements MigrationOperation { protected final String field; protected String sqlType; protected boolean notNull; + protected Generated.Type generated; public Builder(String table, String field) { this.table = table; @@ -38,8 +42,14 @@ public class AddFieldOperation implements MigrationOperation { return this; } + public Builder generated(Generated.Type generated) { + Check.notNull(generated, "generated"); + this.generated = generated; + return this; + } + public AddFieldOperation build() { - return new AddFieldOperation(table, field, sqlType, notNull); + return new AddFieldOperation(table, field, sqlType, notNull, generated); } } } diff --git a/core/src/main/java/jef/model/migration/operation/UpdateFieldOperation.java b/core/src/main/java/jef/model/migration/operation/UpdateFieldOperation.java index c8cb4df..6b84916 100644 --- a/core/src/main/java/jef/model/migration/operation/UpdateFieldOperation.java +++ b/core/src/main/java/jef/model/migration/operation/UpdateFieldOperation.java @@ -1,5 +1,6 @@ package jef.model.migration.operation; +import jef.model.annotations.Generated; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -10,8 +11,8 @@ import lombok.ToString; public class UpdateFieldOperation extends AddFieldOperation { private final String newName; - public UpdateFieldOperation(String table, String field, String newName, String sqlType, boolean notNull) { - super(table, field, sqlType, notNull); + public UpdateFieldOperation(String table, String field, String newName, String sqlType, boolean notNull, Generated.Type generated) { + super(table, field, sqlType, notNull, generated); this.newName = newName; } @@ -30,7 +31,7 @@ public class UpdateFieldOperation extends AddFieldOperation { } public UpdateFieldOperation build() { - return new UpdateFieldOperation(table, field, newName, sqlType, notNull); + return new UpdateFieldOperation(table, field, newName, sqlType, notNull, generated); } } } diff --git a/core/src/test/java/jef/model/ModelBuilderCloneTest.java b/core/src/test/java/jef/model/ModelBuilderCloneTest.java index a2c02c0..e8b493b 100644 --- a/core/src/test/java/jef/model/ModelBuilderCloneTest.java +++ b/core/src/test/java/jef/model/ModelBuilderCloneTest.java @@ -158,7 +158,7 @@ public class ModelBuilderCloneTest { } assertEquals(o.getFields().size(), c.getFields().size()); - for (int j = 0; j < o.getIndexes().size(); j++) { + for (int j = 0; j < o.getFields().size(); j++) { var of = o.getFields().get(j); var cf = c.getFields().get(j); @@ -177,6 +177,11 @@ public class ModelBuilderCloneTest { System.out.println("ex: " + of.isNotNull()); System.out.println("ac: " + cf.isNotNull()); } + if (of.getGenerated() != cf.getGenerated()) { + System.out.println("is generated differ: entity " + o.getName() + ", field " + of.getName()); + System.out.println("ex: " + of.getGenerated()); + System.out.println("ac: " + cf.getGenerated()); + } if (!of.getEntity().getName().equals(cf.getEntity().getName())) { System.out.println("entities differ: entity " + o.getName() + ", field " + of.getName()); System.out.println("ex: " + of.getEntity().getName()); diff --git a/migration-creator/src/main/java/jef/model/migration/creator/MigrationBuilderGenerator.java b/migration-creator/src/main/java/jef/model/migration/creator/MigrationBuilderGenerator.java index 94e8b4b..6284737 100644 --- a/migration-creator/src/main/java/jef/model/migration/creator/MigrationBuilderGenerator.java +++ b/migration-creator/src/main/java/jef/model/migration/creator/MigrationBuilderGenerator.java @@ -1,5 +1,6 @@ package jef.model.migration.creator; +import jef.model.annotations.Generated; import jef.model.constraints.ForeignKeyConstraint; import jef.model.migration.Migration; import jef.model.migration.MigrationBuilder; @@ -110,9 +111,12 @@ public class MigrationBuilderGenerator { } private String addFieldOpOptional(AddFieldOperation op) { - return "\n" - + " .notNull(" + op.isNotNull() + ")\n" - + " .sqlType(\"" + op.getSqlType() + "\")"; + if (op.getGenerated() != Generated.Type.NONE) { + imports.add(Generated.class); + } + return "\n .notNull(" + op.isNotNull() + ")" + + "\n .sqlType(\"" + op.getSqlType() + "\")" + + (op.getGenerated() != Generated.Type.NONE ? "\n .generated(Generated.Type." + op.getGenerated() + ")" : ""); } private String addForeignKeyOp(AddForeignKeyOperation op) { diff --git a/migration-creator/src/main/java/jef/model/migration/creator/MigrationCreator.java b/migration-creator/src/main/java/jef/model/migration/creator/MigrationCreator.java index 9692679..aef7578 100644 --- a/migration-creator/src/main/java/jef/model/migration/creator/MigrationCreator.java +++ b/migration-creator/src/main/java/jef/model/migration/creator/MigrationCreator.java @@ -145,7 +145,8 @@ public class MigrationCreator { .filter(DbField::isDatabaseField) .map(e -> new AddFieldOperation.Builder(toEntity.getName(), e.getName()) .notNull(e.isNotNull()) - .sqlType(getSqlType(e))) + .sqlType(getSqlType(e)) + .generated(e.getGenerated())) .toList(), Optional.ofNullable(toEntity.getPrimaryKey()) .map(e -> new AddPrimaryKeyOperation.Builder(e.getName(), toEntity.getName(), e.getFields().stream().map(DbField::getName).toList())) @@ -255,7 +256,8 @@ public class MigrationCreator { handledTo.add(toField); steps.add(new AddFieldOperation.Builder(toField.getEntity().getName(), toField.getName()) .sqlType(getSqlType(toField)) - .notNull(toField.isNotNull())); + .notNull(toField.isNotNull()) + .generated(toField.getGenerated())); } } } diff --git a/migration-creator/src/main/java/jef/model/migration/creator/ModelBuilderGenerator.java b/migration-creator/src/main/java/jef/model/migration/creator/ModelBuilderGenerator.java index 3f0f44f..75a7a8b 100644 --- a/migration-creator/src/main/java/jef/model/migration/creator/ModelBuilderGenerator.java +++ b/migration-creator/src/main/java/jef/model/migration/creator/ModelBuilderGenerator.java @@ -5,6 +5,7 @@ import jef.model.DbEntity; import jef.model.DbEntityBuilder; import jef.model.DbField; import jef.model.ModelBuilder; +import jef.model.annotations.Generated; import jef.model.constraints.ForeignKeyConstraint; import jef.model.constraints.IndexConstraint; import jef.model.constraints.KeyConstraint; @@ -53,10 +54,14 @@ public class ModelBuilderGenerator { java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n" + indent + " .name(\"" + entity.getName() + "\");\n"; for (DbField field : entity.getFields()) { + if (field.getGenerated() != Generated.Type.NONE) { + imports.add(Generated.class); + } java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n" + indent + " .field(\"" + field.getName() + "\", \"" + field.getTypeName() + "\")" + "\n" + indent + " .sqlType(" + getSqlType(field) + ")" + (field.isNotNull() ? "\n" + indent + " .isNotNull()" : "") + + (field.getGenerated() != Generated.Type.NONE ? "\n" + indent + " .generated(Generated.Type." + field.getGenerated() + ")" : "") + "\n" + indent + " .isDatabaseField(" + field.isDatabaseField() + ")" + "\n" + indent + " .isModelField(" + field.isModelField() + ");\n"; } diff --git a/mysql/src/main/java/jef/platform/mysql/migration/MysqlMigrationOperationTranslator.java b/mysql/src/main/java/jef/platform/mysql/migration/MysqlMigrationOperationTranslator.java index efe28fd..f995b77 100644 --- a/mysql/src/main/java/jef/platform/mysql/migration/MysqlMigrationOperationTranslator.java +++ b/mysql/src/main/java/jef/platform/mysql/migration/MysqlMigrationOperationTranslator.java @@ -1,5 +1,6 @@ package jef.platform.mysql.migration; +import jef.model.annotations.Generated; import jef.model.migration.operation.AddFieldOperation; import jef.model.migration.operation.AddForeignKeyOperation; import jef.model.migration.operation.AddIndexOperation; @@ -59,6 +60,8 @@ public class MysqlMigrationOperationTranslator implements MigrationOperationTran return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`" + " ADD COLUMN `" + op.getField() + "` " + op.getSqlType() + (op.isNotNull() ? " NOT NULL" : "") //TODO add after field specification + + (op.getGenerated() == Generated.Type.IDENTITY ? "AUTO_INCREMENT " : "") +// + (op.getGenerated() == Generated.Type.COMPUTED ? "AUTO_INCREMENT " : "")//TODO + " LAST"); } @@ -100,7 +103,10 @@ public class MysqlMigrationOperationTranslator implements MigrationOperationTran return connection.prepareStatement("CREATE TABLE `" + op.getTable() + "` (" + op.getFields().stream().map(e -> { var f = e.build(); - return "`" + f.getField() + "` " + f.getSqlType() + (f.isNotNull() ? " NOT NULL" : ""); + return "`" + f.getField() + "` " + f.getSqlType() + + (f.isNotNull() ? " NOT NULL" : "") + + (f.getGenerated() == Generated.Type.IDENTITY ? " AUTO_INCREMENT" : ""); +// + (op.getGenerated() == Generated.Type.COMPUTED ? "AUTO_INCREMENT " : "")//TODO }).collect(Collectors.joining(", ")) + (op.getPrimaryKey() != null ? ", PRIMARY KEY (" + op.getPrimaryKey().build().getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")"