added database value generation with identity generation

This commit is contained in:
wea_ondara
2022-11-27 08:10:52 +01:00
parent 3e581a9e4a
commit 195f3d0148
14 changed files with 118 additions and 15 deletions

View File

@@ -1,5 +1,6 @@
package jef.model; package jef.model;
import jef.model.annotations.Generated;
import jef.serializable.SerializableObject; import jef.serializable.SerializableObject;
import jef.util.Check; import jef.util.Check;
import lombok.AccessLevel; import lombok.AccessLevel;
@@ -19,12 +20,13 @@ public class DbField<T> {
private Class<T> type; private Class<T> type;
@Setter(value = AccessLevel.PACKAGE) @Setter(value = AccessLevel.PACKAGE)
private Field field; private Field field;
private boolean isModelField; private boolean isModelField;//TODO get rid of this field
private boolean isDatabaseField; private boolean isDatabaseField;//TODO get rid of this field
private DbField<?> exposingForeignKeyOf; private DbField<?> exposingForeignKeyOf;
private String name; private String name;
private boolean notNull = false; private boolean notNull = false;
private String sqlType; private String sqlType;
private Generated.Type generated = Generated.Type.NONE;
DbField(DbEntity<? extends SerializableObject> entity, String name, String typeName) { DbField(DbEntity<? extends SerializableObject> entity, String name, String typeName) {
this.entity = Check.notNull(entity, "entity"); this.entity = Check.notNull(entity, "entity");
@@ -85,6 +87,11 @@ public class DbField<T> {
this.sqlType = sqlType; this.sqlType = sqlType;
} }
public void setGenerated(Generated.Type generated) {
Check.notNull(generated, "generated");
this.generated = generated;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (!equalsCommon(o)) { if (!equalsCommon(o)) {
@@ -108,6 +115,7 @@ public class DbField<T> {
&& notNull == dbField.notNull && notNull == dbField.notNull
&& entity.getName().equals(dbField.entity.getName()) && entity.getName().equals(dbField.entity.getName())
&& typeName.equals(dbField.typeName) && typeName.equals(dbField.typeName)
&& generated == dbField.generated
// && Objects.equals(type, dbField.type) // && Objects.equals(type, dbField.type)
&& Objects.equals(exposingForeignKeyOf == null ? null : exposingForeignKeyOf.getName(), && Objects.equals(exposingForeignKeyOf == null ? null : exposingForeignKeyOf.getName(),
dbField.exposingForeignKeyOf == null ? null : dbField.exposingForeignKeyOf.getName()); dbField.exposingForeignKeyOf == null ? null : dbField.exposingForeignKeyOf.getName());
@@ -117,7 +125,7 @@ public class DbField<T> {
public int hashCode() { public int hashCode() {
return Objects.hash(entity.getName(), typeName, type, field == null ? null : field.getName(), isModelField, isDatabaseField, return Objects.hash(entity.getName(), typeName, type, field == null ? null : field.getName(), isModelField, isDatabaseField,
exposingForeignKeyOf == null ? null : exposingForeignKeyOf.getName(), exposingForeignKeyOf == null ? null : exposingForeignKeyOf.getName(),
name, notNull); name, notNull, generated);
} }
@Override @Override

View File

@@ -1,5 +1,6 @@
package jef.model; package jef.model;
import jef.model.annotations.Generated;
import jef.model.constraints.IndexConstraint; import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint; import jef.model.constraints.KeyConstraint;
import jef.model.constraints.UniqueKeyConstraint; import jef.model.constraints.UniqueKeyConstraint;
@@ -41,6 +42,11 @@ public class DbFieldBuilder<T> {
return this; return this;
} }
public DbFieldBuilder<T> generated(Generated.Type type) {
field.setGenerated(type);
return this;
}
public void isUnique() { public void isUnique() {
isUnique(true); isUnique(true);
} }

View File

@@ -196,6 +196,7 @@ public class ModelBuilder {
nf.setType(e.getType()); nf.setType(e.getType());
nf.setSqlType(e.getSqlType()); nf.setSqlType(e.getSqlType());
nf.setNotNull(e.isNotNull()); nf.setNotNull(e.isNotNull());
nf.setGenerated(e.getGenerated());
nf.setModelField(e.isModelField()); nf.setModelField(e.isModelField());
nf.setDatabaseField(e.isDatabaseField()); nf.setDatabaseField(e.isDatabaseField());
return nf; return nf;

View File

@@ -2,6 +2,7 @@ package jef.model;
import jef.model.annotations.processors.AnnotationProcessor; import jef.model.annotations.processors.AnnotationProcessor;
import jef.model.annotations.processors.ForeignKeyProcessor; import jef.model.annotations.processors.ForeignKeyProcessor;
import jef.model.annotations.processors.GeneratedProcessor;
import jef.model.annotations.processors.IndexProcessor; import jef.model.annotations.processors.IndexProcessor;
import jef.model.annotations.processors.KeyProcessor; import jef.model.annotations.processors.KeyProcessor;
import jef.model.annotations.processors.NotNullProcessor; import jef.model.annotations.processors.NotNullProcessor;
@@ -28,7 +29,8 @@ public class ModelBuilderOptions {
UniqueProcessor.INSTANCE, UniqueProcessor.INSTANCE,
IndexProcessor.INSTANCE, IndexProcessor.INSTANCE,
KeyProcessor.INSTANCE, KeyProcessor.INSTANCE,
ForeignKeyProcessor.INSTANCE ForeignKeyProcessor.INSTANCE,
GeneratedProcessor.INSTANCE
)); ));
this.contextOptions = new DbContextOptions(null);//TODO this.contextOptions = new DbContextOptions(null);//TODO
} }

View File

@@ -1,5 +1,6 @@
package jef.model; package jef.model;
import jef.model.annotations.Generated;
import jef.model.annotations.Id; import jef.model.annotations.Id;
import jef.model.annotations.Transient; import jef.model.annotations.Transient;
@@ -58,5 +59,9 @@ class PrimaryKeyInitializer {
var dbfields = idFields.stream().map(e -> entity.field(e).getField().getName()).toList(); var dbfields = idFields.stream().map(e -> entity.field(e).getField().getName()).toList();
entity.hasOne(dbfields.toArray(String[]::new)).isPrimaryKey(true); 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);
}
} }
} }

View File

@@ -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
;
}
}

View File

@@ -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<? extends SerializableObject> 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());
}
}
}
}

View File

@@ -1,5 +1,7 @@
package jef.model.migration.operation; package jef.model.migration.operation;
import jef.model.annotations.Generated;
import jef.util.Check;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
@@ -14,6 +16,7 @@ public class AddFieldOperation implements MigrationOperation {
protected final String field; protected final String field;
protected final String sqlType; protected final String sqlType;
protected final boolean notNull; protected final boolean notNull;
protected final Generated.Type generated;
@EqualsAndHashCode @EqualsAndHashCode
@ToString @ToString
@@ -22,6 +25,7 @@ public class AddFieldOperation implements MigrationOperation {
protected final String field; protected final String field;
protected String sqlType; protected String sqlType;
protected boolean notNull; protected boolean notNull;
protected Generated.Type generated;
public Builder(String table, String field) { public Builder(String table, String field) {
this.table = table; this.table = table;
@@ -38,8 +42,14 @@ public class AddFieldOperation implements MigrationOperation {
return this; return this;
} }
public Builder generated(Generated.Type generated) {
Check.notNull(generated, "generated");
this.generated = generated;
return this;
}
public AddFieldOperation build() { public AddFieldOperation build() {
return new AddFieldOperation(table, field, sqlType, notNull); return new AddFieldOperation(table, field, sqlType, notNull, generated);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package jef.model.migration.operation; package jef.model.migration.operation;
import jef.model.annotations.Generated;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
@@ -10,8 +11,8 @@ import lombok.ToString;
public class UpdateFieldOperation extends AddFieldOperation { public class UpdateFieldOperation extends AddFieldOperation {
private final String newName; private final String newName;
public UpdateFieldOperation(String table, String field, String newName, String sqlType, boolean notNull) { public UpdateFieldOperation(String table, String field, String newName, String sqlType, boolean notNull, Generated.Type generated) {
super(table, field, sqlType, notNull); super(table, field, sqlType, notNull, generated);
this.newName = newName; this.newName = newName;
} }
@@ -30,7 +31,7 @@ public class UpdateFieldOperation extends AddFieldOperation {
} }
public UpdateFieldOperation build() { public UpdateFieldOperation build() {
return new UpdateFieldOperation(table, field, newName, sqlType, notNull); return new UpdateFieldOperation(table, field, newName, sqlType, notNull, generated);
} }
} }
} }

View File

@@ -158,7 +158,7 @@ public class ModelBuilderCloneTest {
} }
assertEquals(o.getFields().size(), c.getFields().size()); 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 of = o.getFields().get(j);
var cf = c.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("ex: " + of.isNotNull());
System.out.println("ac: " + cf.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())) { if (!of.getEntity().getName().equals(cf.getEntity().getName())) {
System.out.println("entities differ: entity " + o.getName() + ", field " + of.getName()); System.out.println("entities differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.getEntity().getName()); System.out.println("ex: " + of.getEntity().getName());

View File

@@ -1,5 +1,6 @@
package jef.model.migration.creator; package jef.model.migration.creator;
import jef.model.annotations.Generated;
import jef.model.constraints.ForeignKeyConstraint; import jef.model.constraints.ForeignKeyConstraint;
import jef.model.migration.Migration; import jef.model.migration.Migration;
import jef.model.migration.MigrationBuilder; import jef.model.migration.MigrationBuilder;
@@ -110,9 +111,12 @@ public class MigrationBuilderGenerator {
} }
private String addFieldOpOptional(AddFieldOperation op) { private String addFieldOpOptional(AddFieldOperation op) {
return "\n" if (op.getGenerated() != Generated.Type.NONE) {
+ " .notNull(" + op.isNotNull() + ")\n" imports.add(Generated.class);
+ " .sqlType(\"" + op.getSqlType() + "\")"; }
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) { private String addForeignKeyOp(AddForeignKeyOperation op) {

View File

@@ -145,7 +145,8 @@ public class MigrationCreator {
.filter(DbField::isDatabaseField) .filter(DbField::isDatabaseField)
.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))
.generated(e.getGenerated()))
.toList(), .toList(),
Optional.ofNullable(toEntity.getPrimaryKey()) Optional.ofNullable(toEntity.getPrimaryKey())
.map(e -> new AddPrimaryKeyOperation.Builder(e.getName(), toEntity.getName(), e.getFields().stream().map(DbField::getName).toList())) .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); handledTo.add(toField);
steps.add(new AddFieldOperation.Builder(toField.getEntity().getName(), toField.getName()) steps.add(new AddFieldOperation.Builder(toField.getEntity().getName(), toField.getName())
.sqlType(getSqlType(toField)) .sqlType(getSqlType(toField))
.notNull(toField.isNotNull())); .notNull(toField.isNotNull())
.generated(toField.getGenerated()));
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import jef.model.DbEntity;
import jef.model.DbEntityBuilder; import jef.model.DbEntityBuilder;
import jef.model.DbField; import jef.model.DbField;
import jef.model.ModelBuilder; import jef.model.ModelBuilder;
import jef.model.annotations.Generated;
import jef.model.constraints.ForeignKeyConstraint; import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint; import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint; import jef.model.constraints.KeyConstraint;
@@ -53,10 +54,14 @@ public class ModelBuilderGenerator {
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n" java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .name(\"" + entity.getName() + "\");\n"; + indent + " .name(\"" + entity.getName() + "\");\n";
for (DbField<?> field : entity.getFields()) { for (DbField<?> field : entity.getFields()) {
if (field.getGenerated() != Generated.Type.NONE) {
imports.add(Generated.class);
}
java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n" java += indent + "mb.entity(\"" + entity.getTypeName() + "\")\n"
+ indent + " .field(\"" + field.getName() + "\", \"" + field.getTypeName() + "\")" + indent + " .field(\"" + field.getName() + "\", \"" + field.getTypeName() + "\")"
+ "\n" + indent + " .sqlType(" + getSqlType(field) + ")" + "\n" + indent + " .sqlType(" + getSqlType(field) + ")"
+ (field.isNotNull() ? "\n" + indent + " .isNotNull()" : "") + (field.isNotNull() ? "\n" + indent + " .isNotNull()" : "")
+ (field.getGenerated() != Generated.Type.NONE ? "\n" + indent + " .generated(Generated.Type." + field.getGenerated() + ")" : "")
+ "\n" + indent + " .isDatabaseField(" + field.isDatabaseField() + ")" + "\n" + indent + " .isDatabaseField(" + field.isDatabaseField() + ")"
+ "\n" + indent + " .isModelField(" + field.isModelField() + ");\n"; + "\n" + indent + " .isModelField(" + field.isModelField() + ");\n";
} }

View File

@@ -1,5 +1,6 @@
package jef.platform.mysql.migration; package jef.platform.mysql.migration;
import jef.model.annotations.Generated;
import jef.model.migration.operation.AddFieldOperation; import jef.model.migration.operation.AddFieldOperation;
import jef.model.migration.operation.AddForeignKeyOperation; import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddIndexOperation; import jef.model.migration.operation.AddIndexOperation;
@@ -59,6 +60,8 @@ public class MysqlMigrationOperationTranslator implements MigrationOperationTran
return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`" return connection.prepareStatement("ALTER TABLE `" + op.getTable() + "`"
+ " ADD COLUMN `" + op.getField() + "` " + " ADD COLUMN `" + op.getField() + "` "
+ op.getSqlType() + (op.isNotNull() ? " NOT NULL" : "") //TODO add after field specification + 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"); + " LAST");
} }
@@ -100,7 +103,10 @@ public class MysqlMigrationOperationTranslator implements MigrationOperationTran
return connection.prepareStatement("CREATE TABLE `" + op.getTable() + "` (" return connection.prepareStatement("CREATE TABLE `" + op.getTable() + "` ("
+ op.getFields().stream().map(e -> { + op.getFields().stream().map(e -> {
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" : "")
+ (f.getGenerated() == Generated.Type.IDENTITY ? " AUTO_INCREMENT" : "");
// + (op.getGenerated() == Generated.Type.COMPUTED ? "AUTO_INCREMENT " : "")//TODO
}).collect(Collectors.joining(", ")) }).collect(Collectors.joining(", "))
+ (op.getPrimaryKey() != null + (op.getPrimaryKey() != null
? ", PRIMARY KEY (" + op.getPrimaryKey().build().getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")" ? ", PRIMARY KEY (" + op.getPrimaryKey().build().getFields().stream().map(e -> "`" + e + "`").collect(Collectors.joining(", ")) + ")"