added migration generation

This commit is contained in:
wea_ondara
2022-08-14 12:41:44 +02:00
parent 23e046ef7e
commit b11ae150b3
85 changed files with 4206 additions and 121 deletions

View File

@@ -1,4 +1,6 @@
package jef.model;
public abstract class DbContext {
public void onModelCreate(ModelBuilder mb) {
}
}

View File

@@ -8,9 +8,12 @@ import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.PrimaryKeyConstraint;
import jef.model.constraints.UniqueConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.serializable.SerializableFunction;
import jef.serializable.SerializableObject;
import jef.util.Check;
import jef.util.Util;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@@ -18,33 +21,66 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
public class DbEntity<T extends SerializableObject> {
private final Class<T> type;
private final String typeName;
@Setter(value = AccessLevel.PACKAGE)
private Class<T> type;
private final List<DbField<?>> fields;
private String name;
private PrimaryKeyConstraint primaryKey;
private final List<ForeignKeyConstraint> foreignKeys = new ArrayList<>();
private final List<UniqueConstraint> uniqueKeys = new ArrayList<>();
private final List<UniqueKeyConstraint> uniqueKeys = new ArrayList<>();
private final List<KeyConstraint> keys = new ArrayList<>();
private final List<IndexConstraint> indexes = new ArrayList<>();
DbEntity(Class<T> type) {
this(type, new ArrayList<>());
//only used for migrations
DbEntity(String typeName) {
this.type = (Class) Util.tryGet(() -> Class.forName(typeName)).orElse(null);
this.typeName = typeName;
this.fields = new ArrayList<>();
var split = typeName.split("\\.");
this.name = split[split.length - 1];
}
DbEntity(Class<T> type, List<DbField<?>> fields) {
this.type = type;
this.fields = fields;
this.name = type.getSimpleName();
DbEntity(Class<T> type) {
this(type, type.getSimpleName());
}
DbEntity(Class<T> type, String name) {
this(type, name, new ArrayList<>());
}
DbEntity(Class<T> type, String name, List<DbField<?>> fields) {
this.type = Check.notNull(type, "type");
this.typeName = type.getName();
this.fields = Check.notNull(fields, "fields");
this.name = Check.notNull(name, "name");
}
public List<DbField<?>> getFields() {
return Collections.unmodifiableList(fields);
}
public List<ForeignKeyConstraint> getForeignKeys() {
return Collections.unmodifiableList(foreignKeys);
}
public List<UniqueKeyConstraint> getUniqueKeys() {
return Collections.unmodifiableList(uniqueKeys);
}
public List<KeyConstraint> getKeys() {
return Collections.unmodifiableList(keys);
}
public List<IndexConstraint> getIndexes() {
return Collections.unmodifiableList(indexes);
}
/**
* Returns the database model for the requested property or null if not present.
*
@@ -62,6 +98,10 @@ public class DbEntity<T extends SerializableObject> {
return (DbField<R>) fields.stream().filter(e -> e.getField().equals(field)).findFirst().orElse(null);
}
public <R> DbField<R> getField(String name) {
return (DbField<R>) fields.stream().filter(e -> e.getName().equals(name)).findFirst().orElse(null);
}
public <R> DbField<R> getOrCreateField(SerializableFunction<T, R> getter) {
try {
var prop = getField(getter);
@@ -93,6 +133,19 @@ public class DbEntity<T extends SerializableObject> {
}
}
public <R> DbField<R> getOrCreateField(String name, String typeName) {
try {
var prop = (DbField<R>) fields.stream().filter(e -> e.getName().equals(name)).findFirst().orElse(null);
if (prop == null) {
prop = new DbField<>(this, name, typeName);
fields.add(prop);
}
return prop;
} catch (Exception e) {
throw new RuntimeException("Invalid expression", e);
}
}
public <R> DbField<R> addIfAbsent(DbField<R> field) {
try {
var prop = (DbField<R>) fields.stream().filter(e -> e.getName().equals(field.getName())).findFirst().orElse(null);
@@ -106,6 +159,26 @@ public class DbEntity<T extends SerializableObject> {
}
}
public boolean dropField(DbField<?> field) {
return this.fields.remove(field);
}
public boolean dropForeignKey(ForeignKeyConstraint foreignKey) {
return this.foreignKeys.remove(foreignKey);
}
public boolean dropUniqueKey(UniqueKeyConstraint uniqueKey) {
return this.uniqueKeys.remove(uniqueKey);
}
public boolean dropKey(KeyConstraint key) {
return this.keys.remove(key);
}
public boolean dropIndex(IndexConstraint index) {
return this.indexes.remove(index);
}
private <R> String extractFieldName(SerializableFunction<T, R> getter) {
try {
var expr = new AsmParser(getter).parse();
@@ -125,15 +198,9 @@ public class DbEntity<T extends SerializableObject> {
}
}
public void addUniqueContstraint(UniqueConstraint uniqueConstraint) {
if (!uniqueKeys.contains(uniqueConstraint)) {
uniqueKeys.add(uniqueConstraint);
}
}
public void addIndex(IndexConstraint index) {
if (!indexes.contains(index)) {
indexes.add(index);
public void addUniqueKey(UniqueKeyConstraint uniqueKeyConstraint) {
if (!uniqueKeys.contains(uniqueKeyConstraint)) {
uniqueKeys.add(uniqueKeyConstraint);
}
}
@@ -143,11 +210,37 @@ public class DbEntity<T extends SerializableObject> {
}
}
public void addIndex(IndexConstraint index) {
if (!indexes.contains(index)) {
indexes.add(index);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DbEntity<?> dbEntity = (DbEntity<?>) o;
return typeName.equals(dbEntity.typeName)
&& fields.equals(dbEntity.fields)
&& name.equals(dbEntity.name)
&& Objects.equals(primaryKey, dbEntity.primaryKey)
&& foreignKeys.equals(dbEntity.foreignKeys)
&& uniqueKeys.equals(dbEntity.uniqueKeys)
&& keys.equals(dbEntity.keys)
&& indexes.equals(dbEntity.indexes);
}
@Override
public int hashCode() {
return Objects.hash(typeName, type, fields, name, primaryKey, foreignKeys, uniqueKeys, keys, indexes);
}
@Override
public String toString() {
return "DbEntity{" +
"type=" + type +
", name='" + name + '\'' +
'}';
"type=" + type +
", name='" + name + '\'' +
'}';
}
}

View File

@@ -2,8 +2,10 @@ package jef.model;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.UniqueConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.serializable.SerializableObject;
import jef.util.Check;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@@ -11,28 +13,43 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
@Getter
@Setter
public class DbField<T> {
private final DbEntity<? extends SerializableObject> entity;
private final Class<T> type;
private final Field field;
private final String typeName;
@Setter(value = AccessLevel.PACKAGE)
private Class<T> type;
@Setter(value = AccessLevel.PACKAGE)
private Field field;
private boolean isModelField;
private boolean isDatabaseField;
private DbField<?> foreignKeyModelLink;
private String name;
private boolean notNull = false;
DbField(DbEntity<? extends SerializableObject> entity, String name, String typeName) {
this.entity = Check.notNull(entity, "entity");
this.type = null;
this.typeName = Check.notNull(typeName, "typeName");
this.field = null;
this.name = Check.notNull(name, "name");
this.isModelField = false;
this.isDatabaseField = true;
}
DbField(DbEntity<? extends SerializableObject> entity, Class<T> type, Field field) {
this(entity, type, field, field.getName());
}
DbField(DbEntity<? extends SerializableObject> entity, Class<T> type, Field field, String name) {
this.entity = entity;
this.type = type;
this.entity = Check.notNull(entity, "entity");
this.type = Check.notNull(type, "type");
this.typeName = type.getName();
this.field = field;
this.name = name;
this.name = Check.notNull(name, "name");
this.isModelField = field != null;
this.isDatabaseField = !Collection.class.isAssignableFrom(type) && !SerializableObject.class.isAssignableFrom(type);
}
@@ -48,9 +65,9 @@ public class DbField<T> {
public void setUnique(boolean unique) {
var constr = entity.getUniqueKeys().stream().filter(u -> u.getFields().size() == 1 && u.getFields().get(0) == this).findFirst();
if (!constr.isPresent() && unique) {
entity.getUniqueKeys().add(new UniqueConstraint(entity, new ArrayList<>(List.of(this))));
entity.addUniqueKey(new UniqueKeyConstraint(entity, new ArrayList<>(List.of(this))));
} else if (constr.isPresent() && !unique) {
entity.getUniqueKeys().remove(constr.get());
entity.dropUniqueKey(constr.get());
} //else do nothing
}
@@ -61,9 +78,9 @@ public class DbField<T> {
public void setIndex(boolean indexed) {
var constr = entity.getIndexes().stream().filter(u -> u.getFields().size() == 1 && u.getFields().get(0) == this).findFirst();
if (!constr.isPresent() && indexed) {
entity.getIndexes().add(new IndexConstraint(entity, new ArrayList<>(List.of(this))));
entity.addIndex(new IndexConstraint(entity, new ArrayList<>(List.of(this))));
} else if (constr.isPresent() && !indexed) {
entity.getIndexes().remove(constr.get());
entity.dropIndex(constr.get());
} //else do nothing
}
@@ -74,18 +91,68 @@ public class DbField<T> {
public void setKey(boolean keyed) {
var constr = entity.getKeys().stream().filter(u -> u.getFields().size() == 1 && u.getFields().get(0) == this).findFirst();
if (!constr.isPresent() && keyed) {
entity.getKeys().add(new KeyConstraint(entity, new ArrayList<>(List.of(this))));
entity.addKey(new KeyConstraint(entity, new ArrayList<>(List.of(this))));
} else if (constr.isPresent() && !keyed) {
entity.getKeys().remove(constr.get());
entity.dropKey(constr.get());
} //else do nothing
}
public DbField<T> setModelField(boolean modelField) {
isModelField = modelField;
return this;
}
public DbField<T> setDatabaseField(boolean databaseField) {
isDatabaseField = databaseField;
return this;
}
public DbField<T> setNotNull(boolean notNull) {
this.notNull = notNull;
return this;
}
@Override
public boolean equals(Object o) {
if (!equalsCommon(o)) {
return false;
}
DbField<?> dbField = (DbField<?>) o;
return name.equals(dbField.name);
// && Objects.equals(field == null ? null : field.getName(), dbField.field == null ? null : dbField.field.getName());
}
public boolean equalsExceptName(Object o) {
return equalsCommon(o);
}
private boolean equalsCommon(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DbField<?> dbField = (DbField<?>) o;
return isModelField == dbField.isModelField
&& isDatabaseField == dbField.isDatabaseField
&& notNull == dbField.notNull
&& entity.getName().equals(dbField.entity.getName())
&& typeName.equals(dbField.typeName)
// && Objects.equals(type, dbField.type)
&& Objects.equals(foreignKeyModelLink == null ? null : foreignKeyModelLink.getName(),
dbField.foreignKeyModelLink == null ? null : dbField.foreignKeyModelLink.getName());
}
@Override
public int hashCode() {
return Objects.hash(entity.getName(), typeName, type, field == null ? null : field.getName(), isModelField, isDatabaseField,
foreignKeyModelLink == null ? null : foreignKeyModelLink.getName(),
name, notNull);
}
@Override
public String toString() {
return "DbField{" +
"name=" + name +
", type=" + type.getSimpleName() + (notNull ? "" : "?") +
", entity=" + entity +
'}';
"name=" + name +
", typeName=" + typeName + (notNull ? "" : "?") +
", entity=" + entity +
'}';
}
}

View File

@@ -19,12 +19,13 @@ class EntityInitializer {
throw new ModelException("DbSet " + ctxfield.getName() + " is missing the " + Clazz.class.getSimpleName() + " annotation");
}
var dbsetClazz = (Class<? extends SerializableObject>) clazzAnnotation.clazz();
initEntity(mb, dbsetClazz);
initEntity(mb, dbsetClazz, ctxfield.getName());
}
}
static void initEntity(ModelBuilder mb, Class<? extends SerializableObject> clazz) {
static void initEntity(ModelBuilder mb, Class<? extends SerializableObject> clazz, String name) {
var entity = mb.getOrCreateEntity(clazz);
entity.setName(name);
var fields = ReflectionUtil.getFieldsRecursive(clazz);
for (var f : fields) {

View File

@@ -33,7 +33,7 @@ class ForeignKeyInitializer {
}
var otherEntity = mb.getEntity((Class<? extends SerializableObject>) clazzAnnotation.clazz());
if (otherEntity == null) {
EntityInitializer.initEntity(mb, (Class<? extends SerializableObject>) clazzAnnotation.clazz());
EntityInitializer.initEntity(mb, (Class<? extends SerializableObject>) clazzAnnotation.clazz(), f.getName());
otherEntity = mb.getEntity((Class<? extends SerializableObject>) clazzAnnotation.clazz());
PrimaryKeyInitializer.initPrimaryKeys(mb, otherEntity);
}
@@ -88,7 +88,7 @@ class ForeignKeyInitializer {
} else if (SerializableObject.class.isAssignableFrom(f.getType())) {
var otherEntity = mb.getEntity((Class<? extends SerializableObject>) f.getType());
if (otherEntity == null) {
EntityInitializer.initEntity(mb, (Class<? extends SerializableObject>) f.getType());
EntityInitializer.initEntity(mb, (Class<? extends SerializableObject>) f.getType(), f.getName());
otherEntity = mb.getEntity((Class<? extends SerializableObject>) f.getType());
PrimaryKeyInitializer.initPrimaryKeys(mb, otherEntity);
}

View File

@@ -6,16 +6,23 @@ import jef.model.annotations.processors.IndexProcessor;
import jef.model.annotations.processors.KeyProcessor;
import jef.model.annotations.processors.NotNullProcessor;
import jef.model.annotations.processors.UniqueProcessor;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.PrimaryKeyConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.serializable.SerializableObject;
import jef.util.Check;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class ModelBuilder {
private static final List<AnnotationProcessor> annotationProcessors = new ArrayList<>();
static {
static {//TODO move this to a passable config class
annotationProcessors.add(NotNullProcessor.INSTANCE);
annotationProcessors.add(UniqueProcessor.INSTANCE);
annotationProcessors.add(IndexProcessor.INSTANCE);
@@ -23,7 +30,13 @@ public class ModelBuilder {
annotationProcessors.add(ForeignKeyProcessor.INSTANCE);
}
public static ModelBuilder from(Class<? extends DbContext> context) {
/**
* Initializes a ModelBuilder and configures the entities found in the context Class according to annotations.
*
* @param context the context to use for initialization
* @return an initialized ModelBuilder
*/
public static ModelBuilder from(Class<? extends DbContext> context) { //TODO pass optional config here
try {
return from0(context);
} catch (Exception e) {
@@ -40,15 +53,34 @@ public class ModelBuilder {
for (AnnotationProcessor processor : annotationProcessors) {
processor.apply(mb);
}
var instance = context.getDeclaredConstructor().newInstance();//TODO find constructor, //TODO optionally with config
instance.onModelCreate(mb);
return mb;
}
private final List<DbEntity<? extends SerializableObject>> entities;
public ModelBuilder(List<DbEntity<? extends SerializableObject>> entities) {
this.entities = entities;
/**
* Initializes an empty ModelBuilder
*/
public ModelBuilder() {
this(List.of());
}
/**
* Initializes a new ModelBuilder with the provided entities.
*/
public ModelBuilder(List<DbEntity<? extends SerializableObject>> entities) {
this.entities = new ArrayList<>(entities);
}
/**
* Returns an unmodifiable List of all entities.
*
* @return an unmodifiable List of all entities
*/
public List<DbEntity<? extends SerializableObject>> getEntities() {
return Collections.unmodifiableList(entities);
}
@@ -61,22 +93,157 @@ public class ModelBuilder {
* @return the database model for the requested class or null if not present.
*/
public <T extends SerializableObject> DbEntity<T> getEntity(Class<T> clazz) {
return (DbEntity<T>) entities.stream().filter(e -> e.getType() == clazz).findFirst().orElse(null);
Check.notNull(clazz, "clazz");
return getEntity(clazz.getName());
}
/**
* Returns the database model for the requested class or creates a new one if none exists.
* Returns the database model for the requested type name or null if not present.
*
* @param typeName the class name (including the package name) of the model class
* @param <T> the type of model class
* @return the database model for the requested class or null if not present.
*/
public <T extends SerializableObject> DbEntity<T> getEntity(String typeName) {
Check.notNull(typeName, "typeName");
return (DbEntity<T>) entities.stream().filter(e -> e.getTypeName().equals(typeName)).findFirst().orElse(null);
}
/**
* Returns the DbEntity for the requested class or creates a new empty DbEntity if none exists.
*
* @param clazz the class of the model class
* @param <T> the type of model class
* @return the database model for the requested class or the newly created one if none existed.
* @return the DbEntity for the requested class or the newly created empty DbEntity if none existed.
*/
public <T extends SerializableObject> DbEntity<T> getOrCreateEntity(Class<T> clazz) {
var entity = getEntity(clazz);
Check.notNull(clazz, "clazz");
return getOrCreateEntity(clazz.getName());
}
/**
* Returns the DbEntity for the requested class or creates a new empty DbEntity if none exists.
*
* @param typeName the class name (including package name) of the model class
* @param <T> the type of model class
* @return the DbEntity for the requested class or the newly created empty DbEntity if none existed.
*/
public <T extends SerializableObject> DbEntity<T> getOrCreateEntity(String typeName) {
Check.notNull(typeName, "typeName");
var entity = (DbEntity<T>) getEntity(typeName);
if (entity == null) {
entity = new DbEntity<>(clazz);
entity = new DbEntity<>(typeName);
entities.add(entity);
}
return entity;
}
/**
* Drops the entity from this ModelBuilder. This function also drops referencing foreign keys.
*
* @param entity the entity to drop
* @param <T> the type of the entity
* @return whether the entity was present in this ModelBuilder
*/
public <T extends SerializableObject> boolean dropEntity(DbEntity<T> entity) {
Check.notNull(entity, "entity");
var removed = this.entities.remove(entity);
if (!removed) {
return false;
}
for (DbEntity<? extends SerializableObject> e : this.entities) {
e.getForeignKeys().stream()
.filter(fk -> fk.getReferencedEntity() == entity)
.toList()
.forEach(e::dropForeignKey);
}
return removed;
}
public ModelBuilder clone() {
//copy entities
var entities = new ArrayList<DbEntity>();
for (int i = 0; i < this.entities.size(); i++) {
var old = this.entities.get(i);
var entity = (DbEntity<?>) new DbEntity(old.getTypeName());
entity.setName(old.getName());
entity.setType((Class) old.getType());
//add fields
old.getFields().stream().map(e -> {
var nf = new DbField(entity, e.getName(), e.getTypeName());
nf.setField(e.getField());
nf.setType(e.getType());
nf.setNotNull(e.isNotNull());
nf.setModelField(e.isModelField());
nf.setDatabaseField(e.isDatabaseField());
return nf;
})
.forEach(entity::addIfAbsent);
//apply exposed foreign keys
old.getFields().stream().filter(e -> e.getForeignKeyModelLink() != null).forEach(e -> {
var nf = entity.getFields().stream().filter(f -> f.getName().equals(e.getName())).findFirst().get(); //get(): should always be there
nf.setForeignKeyModelLink(old.getFields().stream().filter(f -> f.getName().equals(e.getForeignKeyModelLink().getName())).findFirst().get()); //get(): should always be there
});
entity.setName(old.getName());
if (old.getPrimaryKey() != null) {
var newPkFields = old.getPrimaryKey().getFields().stream()
.map(pkfield -> entity.getFields().stream().filter(f -> f.getName().equals(pkfield.getName())).findFirst().get()) //get(): should always be there
.toList();
entity.setPrimaryKey(new PrimaryKeyConstraint(entity, (List) newPkFields));
}
entities.add(entity);
}
//copy keys
for (int i = 0; i < entities.size(); i++) {
var entity = entities.get(i);
var old = this.entities.get(i);
for (ForeignKeyConstraint foreignKey : old.getForeignKeys()) {
var fkFields = foreignKey.getFields().stream()
.map(fkField -> entity.getFields().stream().filter(f -> ((DbField) f).getName().equals(fkField.getName())).findFirst().get()) //get(): should always be there
.toList();
var refEntity = entities.stream().filter(e -> e.getName().equals(foreignKey.getReferencedEntity().getName())).findFirst().get();//should always be there
var fkRefFields = foreignKey.getReferencedFields().stream()
.map(fkField -> refEntity.getFields().stream().filter(f -> ((DbField) f).getName().equals(fkField.getName())).findFirst().get()) //get(): should always be there
.toList();
entities.get(i).addForeignKey(new ForeignKeyConstraint(entity, (List) fkFields, refEntity, (List) fkRefFields, foreignKey.getOnUpdate(), foreignKey.getOnDelete()));
}
for (UniqueKeyConstraint uniqueKey : old.getUniqueKeys()) {
var newUkFields = uniqueKey.getFields().stream()
.map(ukField -> entity.getFields().stream().filter(f -> ((DbField) f).getName().equals(ukField.getName())).findFirst().get()) //get(): should always be there
.toList();
entity.addUniqueKey(new UniqueKeyConstraint(entity, (List) newUkFields));
}
for (KeyConstraint key : old.getKeys()) {
var newKFields = key.getFields().stream()
.map(kField -> entity.getFields().stream().filter(f -> ((DbField) f).getName().equals(kField.getName())).findFirst().get()) //get(): should always be there
.toList();
entity.addKey(new KeyConstraint(entity, (List) newKFields));
}
for (IndexConstraint index : old.getIndexes()) {
var newIFields = index.getFields().stream()
.map(iField -> entity.getFields().stream().filter(f -> ((DbField) f).getName().equals(iField.getName())).findFirst().get()) //get(): should always be there
.toList();
entity.addIndex(new IndexConstraint(entity, (List) newIFields));
}
}
return new ModelBuilder((List) entities);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ModelBuilder that = (ModelBuilder) o;
return entities.equals(that.entities);
}
@Override
public int hashCode() {
return Objects.hash(entities);
}
}

View File

@@ -3,13 +3,13 @@ package jef.model.annotations.processors;
import jef.model.DbEntity;
import jef.model.DbField;
import jef.model.annotations.Unique;
import jef.model.constraints.UniqueConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.serializable.SerializableObject;
import java.lang.annotation.Annotation;
import java.util.List;
public class UniqueProcessor extends KeyProcessorBase<UniqueConstraint, Unique> {
public class UniqueProcessor extends KeyProcessorBase<UniqueKeyConstraint, Unique> {
public static final UniqueProcessor INSTANCE = new UniqueProcessor();
private UniqueProcessor() {
@@ -17,13 +17,13 @@ public class UniqueProcessor extends KeyProcessorBase<UniqueConstraint, Unique>
}
@Override
protected UniqueConstraint initConstraint(DbEntity<? extends SerializableObject> entity, List<DbField<?>> fields) {
return new UniqueConstraint(entity, fields);
protected UniqueKeyConstraint initConstraint(DbEntity<? extends SerializableObject> entity, List<DbField<?>> fields) {
return new UniqueKeyConstraint(entity, fields);
}
@Override
protected void addConstraint(UniqueConstraint constr) {
constr.getEntity().addUniqueContstraint(constr);
protected void addConstraint(UniqueKeyConstraint constr) {
constr.getEntity().addUniqueKey(constr);
}
@Override

View File

@@ -0,0 +1,37 @@
package jef.model.constraints;
import jef.model.DbEntity;
import jef.model.DbField;
import jef.util.Check;
import lombok.Getter;
import java.util.List;
import java.util.Objects;
@Getter
public abstract class ConstraintBase implements Constraint {
protected final DbEntity<?> entity;
protected final List<DbField<?>> fields;
public ConstraintBase(DbEntity<?> entity, List<DbField<?>> fields) {
this.entity = Check.notNull(entity, "entity");
this.fields = Check.notNull(fields, "fields");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConstraintBase that = (ConstraintBase) o;
//only compare names
return entity.getName().equals(that.entity.getName())
&& fields.stream().map(DbField::getName).toList().equals(that.fields.stream().map(DbField::getName).toList());
}
@Override
public int hashCode() {
//only hash names
return Objects.hash(entity.getName(), fields.stream().map(DbField::getName).toList());
}
}

View File

@@ -2,24 +2,47 @@ package jef.model.constraints;
import jef.model.DbEntity;
import jef.model.DbField;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class ForeignKeyConstraint implements Constraint {
private final DbEntity<?> entity;
private final List<DbField<?>> fields;
public class ForeignKeyConstraint extends ConstraintBase {
private final DbEntity<?> referencedEntity;
private final List<DbField<?>> referencedFields;
private final Action onUpdate;
private final Action onDelete;
public ForeignKeyConstraint(DbEntity<?> entity, List<DbField<?>> fields, DbEntity<?> referencedEntity, List<DbField<?>> referencedFields, Action onUpdate, Action onDelete) {
super(entity, fields);
this.referencedEntity = referencedEntity;
this.referencedFields = referencedFields;
this.onUpdate = onUpdate;
this.onDelete = onDelete;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ForeignKeyConstraint that = (ForeignKeyConstraint) o;
//only compare names
return referencedEntity.getName().equals(that.referencedEntity.getName())
&& referencedFields.stream().map(DbField::getName).toList().equals(that.referencedFields.stream().map(DbField::getName).toList())
&& onUpdate == that.onUpdate
&& onDelete == that.onDelete;
}
@Override
public int hashCode() {
//only hash names
return Objects.hash(super.hashCode(), referencedEntity.getName(), referencedFields.stream().map(DbField::getName).toList(), onUpdate, onDelete);
}
@Override
public String getName() {
return "FK_" + entity.getName() + "_" + referencedEntity.getName() + "_" + fields.stream().map(DbField::getName).collect(Collectors.joining("_"));
@@ -28,7 +51,7 @@ public class ForeignKeyConstraint implements Constraint {
@Override
public String toString() {
return "CONSTRAINT " + getName() + " FOREIGN KEY (" + fields.stream().map(DbField::getName).collect(Collectors.joining(", ")) + ") "
+ "REFERENCES " + referencedEntity.getName() + "(" + referencedFields.stream().map(DbField::getName).collect(Collectors.joining(", ")) + ")";
+ "REFERENCES " + referencedEntity.getName() + "(" + referencedFields.stream().map(DbField::getName).collect(Collectors.joining(", ")) + ")";
}
public enum Action {
@@ -36,5 +59,6 @@ public class ForeignKeyConstraint implements Constraint {
RESTRICT,
SET_NULL,
NO_ACTION,
;
}
}

View File

@@ -2,7 +2,6 @@ package jef.model.constraints;
import jef.model.DbEntity;
import jef.model.DbField;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@@ -10,11 +9,11 @@ import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class IndexConstraint implements Constraint {
private final DbEntity<?> entity;
private final List<DbField<?>> fields;
@EqualsAndHashCode(callSuper = true)
public class IndexConstraint extends ConstraintBase {
public IndexConstraint(DbEntity<?> entity, List<DbField<?>> fields) {
super(entity, fields);
}
@Override
public String getName() {

View File

@@ -2,7 +2,6 @@ package jef.model.constraints;
import jef.model.DbEntity;
import jef.model.DbField;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@@ -10,11 +9,11 @@ import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class KeyConstraint implements Constraint {
private final DbEntity<?> entity;
private final List<DbField<?>> fields;
@EqualsAndHashCode(callSuper = true)
public class KeyConstraint extends ConstraintBase {
public KeyConstraint(DbEntity<?> entity, List<DbField<?>> fields) {
super(entity, fields);
}
@Override
public String getName() {

View File

@@ -2,7 +2,6 @@ package jef.model.constraints;
import jef.model.DbEntity;
import jef.model.DbField;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@@ -10,11 +9,11 @@ import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class PrimaryKeyConstraint implements Constraint {
private final DbEntity<?> entity;
private final List<DbField<?>> fields;
@EqualsAndHashCode(callSuper = true)
public class PrimaryKeyConstraint extends ConstraintBase {
public PrimaryKeyConstraint(DbEntity<?> entity, List<DbField<?>> fields) {
super(entity, fields);
}
@Override
public String getName() {

View File

@@ -2,7 +2,6 @@ package jef.model.constraints;
import jef.model.DbEntity;
import jef.model.DbField;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@@ -10,11 +9,11 @@ import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class UniqueConstraint implements Constraint {
private final DbEntity<?> entity;
private final List<DbField<?>> fields;
@EqualsAndHashCode(callSuper = true)
public class UniqueKeyConstraint extends ConstraintBase {
public UniqueKeyConstraint(DbEntity<?> entity, List<DbField<?>> fields) {
super(entity, fields);
}
@Override
public String getName() {

View File

@@ -0,0 +1,7 @@
package jef.model.migration;
public interface Migration {
void up(MigrationBuilder migrationBuilder);
void down(MigrationBuilder migrationBuilder);
}

View File

@@ -0,0 +1,102 @@
package jef.model.migration;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.migration.operation.AddFieldOperation;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddIndexOperation;
import jef.model.migration.operation.AddKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.AddUniqueKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropFieldOperation;
import jef.model.migration.operation.DropTableOperation;
import jef.model.migration.operation.MigrationOperation;
import jef.model.migration.operation.RenameFieldOperation;
import jef.model.migration.operation.RenameTableOperation;
import jef.model.migration.operation.UpdateFieldOperation;
import java.util.ArrayList;
import java.util.List;
public class MigrationBuilder {
private final List<MigrationOperation.Builder<?>> operations = new ArrayList<>();
public AddTableOperation.Builder addTable(String table, List<AddFieldOperation.Builder> fields) {
var op = new AddTableOperation.Builder(table, fields);
operations.add(op);
return op;
}
public RenameTableOperation.Builder renameTable(String oldName, String newName) {
var op = new RenameTableOperation.Builder(oldName, newName);
operations.add(op);
return op;
}
public DropTableOperation.Builder dropTable(String table) {
var op = new DropTableOperation.Builder(table);
operations.add(op);
return op;
}
public AddFieldOperation.Builder addField(String table, String field) {
var op = new AddFieldOperation.Builder(table, field);
operations.add(op);
return op;
}
public RenameFieldOperation.Builder renameField(String table, String oldName, String newName) {
var op = new RenameFieldOperation.Builder(table, oldName, newName);
operations.add(op);
return op;
}
public UpdateFieldOperation.Builder updateField(String table, String field) {
var op = new UpdateFieldOperation.Builder(table, field);
operations.add(op);
return op;
}
public DropFieldOperation.Builder dropField(String table, String field) {
var op = new DropFieldOperation.Builder(table, field);
operations.add(op);
return op;
}
public AddPrimaryKeyOperation.Builder addPrimaryKey(String name, String table, List<String> fields) {
var op = new AddPrimaryKeyOperation.Builder(name, table, fields);
operations.add(op);
return op;
}
public AddForeignKeyOperation.Builder addForeignKey(String name, String table, List<String> fields, String referencedTable, List<String> referencedFields, ForeignKeyConstraint.Action onUpdate, ForeignKeyConstraint.Action onDelete) {
var op = new AddForeignKeyOperation.Builder(name, table, fields, referencedTable, referencedFields, onUpdate, onDelete);
operations.add(op);
return op;
}
public AddUniqueKeyOperation.Builder addUniqueKey(String name, String table, List<String> fields) {
var op = new AddUniqueKeyOperation.Builder(name, table, fields);
operations.add(op);
return op;
}
public AddKeyOperation.Builder addKey(String name, String table, List<String> fields) {
var op = new AddKeyOperation.Builder(name, table, fields);
operations.add(op);
return op;
}
public AddIndexOperation.Builder addIndex(String name, String table, List<String> fields) {
var op = new AddIndexOperation.Builder(name, table, fields);
operations.add(op);
return op;
}
public DropConstraintOperation.Builder dropConstraint(String name, String table) {
var op = new DropConstraintOperation.Builder(name, table);
operations.add(op);
return op;
}
}

View File

@@ -0,0 +1,195 @@
package jef.model.migration.creator;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.migration.Migration;
import jef.model.migration.MigrationBuilder;
import jef.model.migration.operation.AddFieldOperation;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddIndexOperation;
import jef.model.migration.operation.AddKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.AddUniqueKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropFieldOperation;
import jef.model.migration.operation.DropTableOperation;
import jef.model.migration.operation.MigrationOperation;
import jef.model.migration.operation.RenameFieldOperation;
import jef.model.migration.operation.RenameTableOperation;
import jef.model.migration.operation.UpdateFieldOperation;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@RequiredArgsConstructor
public class MigrationBuilderGenerator {
private final List<MigrationOperation> opsUp;
private final List<MigrationOperation> opsDown;
private final String name;
private final String packageName;
private final Set<Class<?>> imports = new HashSet<>();
@Getter
private String java = null;
public MigrationBuilderGenerator generate() {
if (java == null) {
java = generateMigrationBuilderJava();
}
return this;
}
private String generateMigrationBuilderJava() {
imports.add(Migration.class);
imports.add(MigrationBuilder.class);
//generate java for migration step and concat
var migrationUp = String.join("\n\n", opsUp.stream().map(this::getMigrationJava).toList());
var migrationDown = String.join("\n\n", opsDown.stream().map(this::getMigrationJava).toList());
//generate migration class file
var normalImports = imports.stream().filter(e -> !e.getName().startsWith("java")).sorted(Comparator.comparing(Class::getName, String.CASE_INSENSITIVE_ORDER)).toList();
var javaImports = imports.stream().filter(e -> e.getName().startsWith("java")).sorted(Comparator.comparing(Class::getName, String.CASE_INSENSITIVE_ORDER)).toList();
var java = "package " + packageName + ";\n"
+ "\n"
+ normalImports.stream().map(e -> "import " + e.getName().replace("$", ".") + ";").collect(Collectors.joining("\n")) + "\n\n"
+ javaImports.stream().map(e -> "import " + e.getName().replace("$", ".") + ";").collect(Collectors.joining("\n")) + "\n\n"
+ "public class " + name + " implements Migration {\n"
+ " public void up(MigrationBuilder mb) {\n"
+ " " + migrationUp.replace("\n", "\n ") + "\n"
+ " }\n"
+ "\n"
+ " public void down(MigrationBuilder mb) {\n"
+ " " + migrationDown.replace("\n", "\n ") + "\n"
+ " }\n"
+ "}\n";
return java;
}
//mapper stuff
private final Map<Class<? extends MigrationOperation>, Function<? extends MigrationOperation, String>> OP_TO_STRING_MAPPERS = initMapper();
private final Function<MigrationOperation, String> UNSUPPORTED_MIGRATION_OPERATION_FUNCTION = (MigrationOperation i) -> {
throw new RuntimeException(new UnsupportedOperationException("Unsupported migration operation: " + i.getClass().getSimpleName()));
};
private Map<Class<? extends MigrationOperation>, Function<? extends MigrationOperation, String>> initMapper() {
Map<Class<? extends MigrationOperation>, Function<? extends MigrationOperation, String>> map = new HashMap<>();
map.put(AddFieldOperation.class, (AddFieldOperation op) -> addFieldOp(op));
map.put(AddForeignKeyOperation.class, (AddForeignKeyOperation op) -> addForeignKeyOp(op));
map.put(AddIndexOperation.class, (AddIndexOperation op) -> addIndexOp(op));
map.put(AddKeyOperation.class, (AddKeyOperation op) -> addKeyOp(op));
map.put(AddPrimaryKeyOperation.class, (AddPrimaryKeyOperation op) -> addPrimaryKeyOp(op));
map.put(AddTableOperation.class, (AddTableOperation op) -> addTableOp(op));
map.put(AddUniqueKeyOperation.class, (AddUniqueKeyOperation op) -> addUniqueKeyOp(op));
map.put(DropConstraintOperation.class, (DropConstraintOperation op) -> dropConstraintOp(op));
map.put(DropFieldOperation.class, (DropFieldOperation op) -> dropFieldOp(op));
map.put(DropTableOperation.class, (DropTableOperation op) -> dropTableOp(op));
map.put(RenameFieldOperation.class, (RenameFieldOperation op) -> renameFieldOp(op));
map.put(RenameTableOperation.class, (RenameTableOperation op) -> renameTableOp(op));
map.put(UpdateFieldOperation.class, (UpdateFieldOperation op) -> updateField(op));
return map;
}
private String getMigrationJava(MigrationOperation migrationOperation) {
var mapper = ((Function<MigrationOperation, String>)
OP_TO_STRING_MAPPERS.getOrDefault(migrationOperation.getClass(), UNSUPPORTED_MIGRATION_OPERATION_FUNCTION));
return mapper.apply(migrationOperation);
}
private String addFieldOp(AddFieldOperation op) {
return "mb.addField(\"" + op.getTable() + "\", \"" + op.getField() + "\")" + addFieldOpOptional(op) + ";";
}
private String addFieldOpOptional(AddFieldOperation op) {
return "\n"
+ " .notNull(" + op.isNotNull() + ")\n"
+ " .sqlType(\"" + op.getSqlType() + "\")";
}
private String addForeignKeyOp(AddForeignKeyOperation op) {
imports.add(List.class);
imports.add(ForeignKeyConstraint.class);
imports.add(ForeignKeyConstraint.Action.class);
return "mb.addForeignKey(\"" + op.getName() + "\",\n"
+ " \"" + op.getTable() + "\",\n"
+ " List.of(" + op.getFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", ")) + "),\n"
+ " \"" + op.getReferencedTable() + "\",\n"
+ " List.of(" + op.getReferencedFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", ")) + "),\n"
+ " ForeignKeyConstraint.Action." + op.getOnUpdate().name() + ",\n"
+ " ForeignKeyConstraint.Action." + op.getOnDelete().name() + ");";
}
private String addIndexOp(AddIndexOperation op) {
imports.add(List.class);
return "mb.addIndex(\"" + op.getName() + "\",\n"
+ " \"" + op.getTable() + "\",\n"
+ " List.of(" + op.getFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", ")) + "));";
}
private String addKeyOp(AddKeyOperation op) {
imports.add(List.class);
return "mb.addKey(\"" + op.getName() + "\",\n"
+ " \"" + op.getTable() + "\",\n"
+ " List.of(" + op.getFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", ")) + "));";
}
private String addPrimaryKeyOp(AddPrimaryKeyOperation op) {
imports.add(List.class);
return "mb.addPrimaryKey(\"" + op.getName() + "\",\n"
+ " \"" + op.getTable() + "\",\n"
+ " List.of(" + op.getFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", ")) + "));";
}
private String addTableOp(AddTableOperation op) {
imports.add(List.class);
imports.add(AddFieldOperation.class);
imports.add(AddFieldOperation.Builder.class);
return "mb.addTable(\"" + op.getTable() + "\", List.of(\n"
+ op.getFields().stream()
/**/.map(f -> " new AddFieldOperation.Builder(\"" + op.getTable() + "\", \"" + f.build().getField() + "\")" + addFieldOpOptional(f.build()).replace("\n", "\n "))
/**/.collect(Collectors.joining(",\n")) + "\n"
+ "));";
}
private String addUniqueKeyOp(AddUniqueKeyOperation op) {
imports.add(List.class);
return "mb.addUniqueKey(\"" + op.getName() + "\",\n"
+ " \"" + op.getTable() + "\",\n"
+ " List.of(" + op.getFields().stream().map(e -> "\"" + e + "\"").collect(Collectors.joining(", ")) + "));";
}
private String dropConstraintOp(DropConstraintOperation op) {
return "mb.dropConstraint(\"" + op.getTable() + "\", \"" + op.getName() + "\");";
}
private String dropFieldOp(DropFieldOperation op) {
return "mb.dropField(\"" + op.getTable() + "\", \"" + op.getField() + "\");";
}
private String dropTableOp(DropTableOperation op) {
return "mb.dropTable(\"" + op.getTable() + "\");";
}
private String renameFieldOp(RenameFieldOperation op) {
return "mb.renameField(\"" + op.getTable() + "\", \"" + op.getOldName() + "\", \"" + op.getNewName() + "\");";
}
private String renameTableOp(RenameTableOperation op) {
return "mb.renameTable(\"" + op.getOldName() + "\", \"" + op.getNewName() + "\");";
}
private String updateField(UpdateFieldOperation op) {
return "mb.updateField(\"" + op.getTable() + "\", \"" + op.getField() + "\")\n"
+ (op.getNewName() != null ? " .newName(\"" + op.getNewName() + "\")\n" : "")
+ " .notNull(" + op.isNotNull() + ")\n"
+ " .sqlType(" + op.getSqlType() + ");";
}
}

View File

@@ -0,0 +1,487 @@
package jef.model.migration.creator;
import jef.model.DbField;
import jef.model.ModelBuilder;
import jef.model.ModelException;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.model.migration.operation.AddFieldOperation;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddIndexOperation;
import jef.model.migration.operation.AddKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.AddUniqueKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropFieldOperation;
import jef.model.migration.operation.DropTableOperation;
import jef.model.migration.operation.MigrationOperation;
import jef.model.migration.operation.RenameFieldOperation;
import jef.model.migration.operation.RenameTableOperation;
import jef.model.migration.operation.UpdateFieldOperation;
import jef.util.Check;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class MigrationCreator {
private static final String ILLEGAL_CHARACTERS = "\"'`,.";//TODO also validate this in modelbuilder/dbentity/dbfield
public Result createMigration(ModelBuilder from, ModelBuilder to, String name, String packageName, String currentSnapshotJava) {
var result = new Result();
//create pre-migration model snapshot class
result = generatePreMigrationSnapshot(name, packageName, currentSnapshotJava, result);
//create migration
result = generateMigration(from, to, name, packageName, result);
//create current model snapshot class
result = generatePostMigrationSnapshot(to, packageName, result);
return result;
}
private Result generatePreMigrationSnapshot(String name, String packageName, String currentSnapshotJava, Result result) {
String preMigrationSnapshot;
if (currentSnapshotJava == null || currentSnapshotJava.isBlank()) {
preMigrationSnapshot = generateModelBuilderJava(new ModelBuilder(), name, packageName);
} else {
preMigrationSnapshot = currentSnapshotJava.replace("CurrentSnapshot", name + "Snapshot");
}
result.setMigrationSnapshot(preMigrationSnapshot);
return result;
}
private Result generatePostMigrationSnapshot(ModelBuilder to, String packageName, Result result) {
result.setCurrentSnapshot(generateModelBuilderJava(to, "Current", packageName));
return result;
}
private String generateModelBuilderJava(ModelBuilder mb, String name, String packageName) {
return new ModelBuilderGenerator(mb, name, packageName).generate().getJava();
}
private Result generateMigration(ModelBuilder from, ModelBuilder to, String name, String packageName, Result result) {
//check for illegal characters
for (var entity : to.getEntities()) {
if (entity.getName().matches("[" + ILLEGAL_CHARACTERS + "]")) {
throw new ModelException("Entity " + entity.getType().getSimpleName() + " has illegal characters in its name: " + entity.getName());
}
for (var field : entity.getFields()) {
if (field.getName().matches("[" + ILLEGAL_CHARACTERS + "]")) {
throw new ModelException("Field " + entity.getType().getSimpleName() + "::" + field.getField().getName() + " has illegal characters in its name: " + entity.getName());
}
}
}
//reduce model builders to changes
var mcd = new ModelChangeDetector(from, to).detect();
var fromReduced = mcd.getFrom();
var toReduced = mcd.getTo();
//create migration steps
var migrationUpOps = new ArrayList<MigrationOperation.Builder>();
var migrationDownOps = new ArrayList<MigrationOperation.Builder>();
generateMigrationSteps(fromReduced, toReduced, from, to, migrationUpOps);
generateMigrationSteps(toReduced, fromReduced, to, from, migrationDownOps);
var builtUpOps = migrationUpOps.stream().map(e -> e.build()).toList();
var builtDownOps = migrationDownOps.stream().map(e -> e.build()).toList();
result.setMigration(new MigrationBuilderGenerator(builtUpOps, builtDownOps, name, packageName).generate().getJava());
result.setStepsUp(builtUpOps);
result.setStepsDown(builtDownOps);
return result;
}
private void generateMigrationSteps(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
//key drop
addForeignKeyDropGeneration(fromReduced, toReduced, from, to, steps);
addPrimaryKeyDropGeneration(fromReduced, toReduced, from, to, steps);
addUniqueKeyDropGeneration(fromReduced, toReduced, from, to, steps);
addKeyDropGeneration(fromReduced, toReduced, from, to, steps);
addIndexDropGeneration(fromReduced, toReduced, from, to, steps);
//table
addTableDropGeneration(fromReduced, toReduced, from, to, steps);
addTableRenameGeneration(fromReduced, toReduced, from, to, steps);
addTableAddGeneration(fromReduced, toReduced, from, to, steps);
//fields
addFieldAddRenameUpdateDropGeneration(fromReduced, toReduced, from, to, steps);
//key add
addPrimaryKeyAddGeneration(fromReduced, toReduced, from, to, steps);
addForeignKeyAddGeneration(fromReduced, toReduced, from, to, steps);
addUniqueKeyAddGeneration(fromReduced, toReduced, from, to, steps);
addKeyAddGeneration(fromReduced, toReduced, from, to, steps);
addIndexAddGeneration(fromReduced, toReduced, from, to, steps);
}
private void addTableAddGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var fromEntity = fromReduced.getEntity(toEntity.getTypeName());
//new entity
if (fromEntity == null) {
steps.add(new AddTableOperation.Builder(
toEntity.getName(),
toEntity.getFields().stream()
.filter(DbField::isDatabaseField)
.map(e -> new AddFieldOperation.Builder(toEntity.getName(), e.getName())
.notNull(e.isNotNull())
.sqlType("TODO"))
.toList()
));
}
}
}
private void addTableRenameGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var fromEntity = fromReduced.getEntity(toEntity.getTypeName());
// entity added
if (fromEntity == null) {
continue;
}
//entity rename
if (!fromEntity.getName().equals(toEntity.getName())) {
steps.add(new RenameTableOperation.Builder(fromEntity.getName(), toEntity.getName()));
}
}
}
private void addTableDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var fromEntity : fromReduced.getEntities()) {
var toEntity = toReduced.getEntity(fromEntity.getTypeName());
if (toEntity == null) {
steps.add(new DropTableOperation.Builder(fromEntity.getName()));
}
}
}
private void addFieldAddRenameUpdateDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var fromEntity = fromReduced.getEntities().stream().filter(e -> e.getName().equals(toEntity.getName())).findFirst().orElse(null);
//entity added -> nothing to do here
if (fromEntity == null) {
continue;
}
var remainingFromFields = new ArrayList<>(fromEntity.getFields());
var remainingToFields = new ArrayList<>(toEntity.getFields());
var handledFrom = new ArrayList<DbField<?>>();
var handledTo = new ArrayList<DbField<?>>();
//fields with same name but different type parameters
for (DbField<?> toField : toEntity.getFields()) {
var fromField = fromEntity.getFields().stream().filter(e -> e.getName().equals(toField.getName())).findFirst().orElse(null);
if (fromField != null) {
handledFrom.add(fromField);
handledTo.add(toField);
//this assumes the reduced ModelBuilders exclude exactly matching entities
steps.add(new UpdateFieldOperation.Builder(toField.getEntity().getName(), toField.getName())
.notNull(toField.isNotNull()));
}
}
//fields with different name but same type parameters
remainingFromFields.removeAll(handledFrom);
remainingToFields.removeAll(handledTo);
var map = new ArrayList<FieldCompare>();
for (DbField<?> toField : remainingToFields) {
for (DbField<?> FromField : remainingFromFields) {
map.add(new FieldCompare(FromField, toField));
}
}
map.removeIf(e -> e.getSimilarity() <= 0);
map.sort(Comparator.comparingInt(FieldCompare::getSimilarity).reversed());
for (FieldCompare compare : map) {
var toField = compare.getTo();
var fromField = compare.getFrom();
if (handledFrom.contains(fromField) || handledTo.contains(toField)) {
continue;
}
handledFrom.add(fromField);
handledTo.add(toField);
steps.add(new RenameFieldOperation.Builder(toField.getEntity().getName(), fromField.getName(), toField.getName()));
}
//drop
remainingFromFields.removeAll(handledFrom);
remainingToFields.removeAll(handledTo);
for (DbField<?> fromField : remainingFromFields) {
var toField = remainingToFields.stream().filter(e -> e.getName().equals(fromField.getName())).findFirst().orElse(null);
if (toField == null) {
handledFrom.add(fromField);
steps.add(new DropFieldOperation.Builder(fromField.getEntity().getName(), fromField.getName()));
}
}
//add
remainingFromFields.removeAll(handledFrom);
remainingToFields.removeAll(handledTo);
for (DbField<?> toField : remainingToFields) {
var fromField = remainingFromFields.stream().filter(e -> e.getName().equals(toField.getName())).findFirst().orElse(null);
if (fromField == null) {
handledTo.add(toField);
steps.add(new AddFieldOperation.Builder(toField.getEntity().getName(), toField.getName())
.notNull(toField.isNotNull()));
}
}
}
}
private void addPrimaryKeyAddGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var originalEntity = to.getEntities().stream().filter(e -> e.getName().equals(toEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = toEntity.getFields();
var involvedForeignKeys = originalEntity.getPrimaryKey() != null
&& originalEntity.getPrimaryKey().getFields().stream().anyMatch(involvedFields::contains);
if (involvedForeignKeys) {
steps.add(new AddPrimaryKeyOperation.Builder(
originalEntity.getPrimaryKey().getName(),
originalEntity.getPrimaryKey().getEntity().getName(),
originalEntity.getPrimaryKey().getFields().stream().map(DbField::getName).collect(Collectors.toList())));
}
}
}
private void addForeignKeyAddGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var originalEntity = to.getEntities().stream().filter(e -> e.getName().equals(toEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = toEntity.getFields();
var involvedForeignKeys = originalEntity.getForeignKeys().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || toEntity.getForeignKeys().contains(e))
.toList();
for (ForeignKeyConstraint foreignKey : involvedForeignKeys) {
steps.add(new AddForeignKeyOperation.Builder(
foreignKey.getName(),
foreignKey.getEntity().getName(),
foreignKey.getFields().stream().map(DbField::getName).collect(Collectors.toList()),
foreignKey.getReferencedEntity().getName(),
foreignKey.getReferencedFields().stream().map(DbField::getName).collect(Collectors.toList()),
foreignKey.getOnUpdate(), foreignKey.getOnDelete()));
}
}
}
private void addUniqueKeyAddGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var originalEntity = to.getEntities().stream().filter(e -> e.getName().equals(toEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = toEntity.getFields();
var involvedUniqueKeys = originalEntity.getUniqueKeys().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || toEntity.getUniqueKeys().contains(e))
.toList();
for (UniqueKeyConstraint uniqueKey : involvedUniqueKeys) {
steps.add(new AddUniqueKeyOperation.Builder(
uniqueKey.getName(),
uniqueKey.getEntity().getName(),
uniqueKey.getFields().stream().map(DbField::getName).collect(Collectors.toList())));
}
}
}
private void addKeyAddGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var originalEntity = to.getEntities().stream().filter(e -> e.getName().equals(toEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = toEntity.getFields();
var involvedKeys = originalEntity.getKeys().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || toEntity.getKeys().contains(e))
.toList();
for (KeyConstraint key : involvedKeys) {
steps.add(new AddKeyOperation.Builder(
key.getName(),
key.getEntity().getName(),
key.getFields().stream().map(DbField::getName).collect(Collectors.toList())));
}
}
}
private void addIndexAddGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var toEntity : toReduced.getEntities()) {
var originalEntity = to.getEntities().stream().filter(e -> e.getName().equals(toEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = toEntity.getFields();
var involvedIndexes = originalEntity.getIndexes().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || toEntity.getIndexes().contains(e))
.toList();
for (IndexConstraint index : involvedIndexes) {
steps.add(new AddIndexOperation.Builder(
index.getName(),
index.getEntity().getName(),
index.getFields().stream().map(DbField::getName).collect(Collectors.toList())));
}
}
}
private void addPrimaryKeyDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var fromEntity : fromReduced.getEntities()) {
var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = fromEntity.getFields();
if (originalEntity.getPrimaryKey() == null) {
continue;
}
var isPrimaryKeyInvolved = originalEntity.getPrimaryKey().getFields().stream().anyMatch(involvedFields::contains);
if (isPrimaryKeyInvolved) {
//drop all referencing foreign keys
from.getEntities().stream().flatMap(e -> e.getForeignKeys().stream())
.filter(foreignKey -> foreignKey.getReferencedEntity() == fromEntity
&& foreignKey.getReferencedFields().stream().anyMatch(involvedFields::contains))
.forEach(foreignKey -> steps.add(new DropConstraintOperation.Builder(foreignKey.getName(), fromEntity.getName())));
//drop primary
steps.add(new DropConstraintOperation.Builder(originalEntity.getPrimaryKey().getName(), fromEntity.getName()));
}
}
}
private void addForeignKeyDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var fromEntity : fromReduced.getEntities()) {
var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = fromEntity.getFields();
var involvedForeignKeys = originalEntity.getForeignKeys().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || fromEntity.getForeignKeys().contains(e))
.toList();
for (ForeignKeyConstraint foreignKey : involvedForeignKeys) {
steps.add(new DropConstraintOperation.Builder(foreignKey.getName(), fromEntity.getName()));
}
}
// for (var fromEntity : from.getEntities()) {
// var toEntity = to.getEntity(fromEntity.getType());
//
// //foreign keys
// for (var foreignKey : fromEntity.getForeignKeys()) {
// if (toEntity == null || toEntity.getForeignKeys().stream().noneMatch(fk -> compareForeignKey(foreignKey, fk))) {
// migrationUpOps.add(new DropConstraintOperation.Builder(foreignKey.getName(), fromEntity.getName()));
// }
// }
// }
}
private void addUniqueKeyDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var fromEntity : fromReduced.getEntities()) {
var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = fromEntity.getFields();
var involvedForeignKeys = originalEntity.getUniqueKeys().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || fromEntity.getUniqueKeys().contains(e))
.toList();
for (UniqueKeyConstraint uniqueKey : involvedForeignKeys) {
steps.add(new DropConstraintOperation.Builder(uniqueKey.getName(), fromEntity.getName()));
}
}
// for (var fromEntity : from.getEntities()) {
// var toEntity = to.getEntity(fromEntity.getType());
//
// //unique keys
// for (var uniqueKey : fromEntity.getUniqueKeys()) {
// if (toEntity == null || toEntity.getUniqueKeys().stream().noneMatch(uk -> compareConstraint(uniqueKey, uk))) {
// steps.add(new DropConstraintOperation.Builder(uniqueKey.getName(), fromEntity.getName()));
// }
// }
// }
}
private void addKeyDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var fromEntity : fromReduced.getEntities()) {
var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = fromEntity.getFields();
var involvedForeignKeys = originalEntity.getKeys().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || fromEntity.getKeys().contains(e))
.toList();
for (KeyConstraint key : involvedForeignKeys) {
steps.add(new DropConstraintOperation.Builder(key.getName(), fromEntity.getName()));
}
}
// for (var fromEntity : from.getEntities()) {
// var toEntity = to.getEntity(fromEntity.getType());
//
// //keys
// for (var key : fromEntity.getKeys()) {
// if (toEntity == null || toEntity.getKeys().stream().noneMatch(k -> compareConstraint(key, k))) {
// migrationUpOps.add(new DropConstraintOperation.Builder(key.getName(), fromEntity.getName()));
// }
// }
// }
}
private void addIndexDropGeneration(ModelBuilder fromReduced, ModelBuilder toReduced, ModelBuilder from, ModelBuilder to, List<MigrationOperation.Builder> steps) {
for (var fromEntity : fromReduced.getEntities()) {
var originalEntity = from.getEntities().stream().filter(e -> e.getName().equals(fromEntity.getName())).findFirst().orElse(null);
Check.notNull(originalEntity, "originalEntity"); //may never be null
var involvedFields = fromEntity.getFields();
var involvedForeignKeys = originalEntity.getIndexes().stream()
.filter(e -> e.getFields().stream().anyMatch(involvedFields::contains) || fromEntity.getIndexes().contains(e))
.toList();
for (IndexConstraint foreignKey : involvedForeignKeys) {
steps.add(new DropConstraintOperation.Builder(foreignKey.getName(), fromEntity.getName()));
}
}
// for (var fromEntity : from.getEntities()) {
// var toEntity = to.getEntity(fromEntity.getType());
//
// //indexes
// for (var index : fromEntity.getIndexes()) {
// if (toEntity == null || toEntity.getIndexes().stream().noneMatch(i -> compareConstraint(index, i))) {
// steps.add(new DropConstraintOperation.Builder(index.getName(), fromEntity.getName()));
// }
// }
// }
}
@Getter
@Setter(AccessLevel.PACKAGE)
@NoArgsConstructor(access = AccessLevel.PACKAGE)
public static class Result {
private List<MigrationOperation> stepsUp = new ArrayList<>();
private List<MigrationOperation> stepsDown = new ArrayList<>();
private String migration = "";
private String migrationSnapshot = "";
private String currentSnapshot = "";
}
@Getter
private class FieldCompare {
private final DbField<?> from;
private final DbField<?> to;
private final int similarity;
public FieldCompare(DbField<?> from, DbField<?> to) {
this.from = from;
this.to = to;
this.similarity = compute();
}
private int compute() {
return from.equalsExceptName(to) ? 1 : 0;
}
}
}

View File

@@ -0,0 +1,124 @@
package jef.model.migration.creator;
import jef.model.DbContext;
import jef.model.DbEntity;
import jef.model.DbField;
import jef.model.ModelBuilder;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.IndexConstraint;
import jef.model.constraints.KeyConstraint;
import jef.model.constraints.PrimaryKeyConstraint;
import jef.model.constraints.UniqueKeyConstraint;
import jef.serializable.SerializableObject;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@RequiredArgsConstructor
public class ModelBuilderGenerator {
private final ModelBuilder mb;
private final String name;
private final String packageName;
private final Set<Class<?>> imports = new HashSet<>();
@Getter
private String java = null;
public ModelBuilderGenerator generate() {
if (java == null) {
java = generateModelBuilderJava();
}
return this;
}
private String generateModelBuilderJava() {
var indent = " ";
imports.add(DbContext.class);
imports.add(ModelBuilder.class);
imports.add(DbEntity.class);
var java = ""
+ "public class " + name + "Snapshot extends DbContext {\n"
+ " @Override\n"
+ " public void onModelCreate(ModelBuilder mb) {\n"
+ indent + "DbEntity entity;\n"
+ indent + "DbEntity referencedEntity;\n";
for (DbEntity<? extends SerializableObject> entity : mb.getEntities()) {
java += indent + "entity = mb.getOrCreateEntity(\"" + entity.getTypeName() + "\");\n"
+ indent + "entity.setName(\"" + entity.getName() + "\");\n";
for (DbField<?> field : entity.getFields()) {
java += indent + "entity.getOrCreateField(\"" + field.getName() + "\", \"" + field.getTypeName() + "\")"
+ (field.isNotNull() ? "\n" + indent + " .setNotNull(true)" : "")
+ "\n" + indent + " .setDatabaseField(" + field.isDatabaseField() + ")"
+ "\n" + indent + " .setModelField(" + field.isModelField() + ")"
+ ";\n";
}
if (entity.getPrimaryKey() != null) {
imports.add(List.class);
imports.add(PrimaryKeyConstraint.class);
java += indent + "entity.setPrimaryKey(new PrimaryKeyConstraint(entity, List.of(\n"
+ indent + " " + entity.getPrimaryKey().getFields().stream().map(f -> "entity.getField(\"" + f.getName() + "\")").collect(Collectors.joining(",\n ")) + "\n"
+ indent + " )));\n";
}
java += "\n";
}
for (DbEntity<? extends SerializableObject> entity : mb.getEntities()) {
if (entity.getForeignKeys().isEmpty() && entity.getForeignKeys().isEmpty() && entity.getForeignKeys().isEmpty() && entity.getForeignKeys().isEmpty()) {
continue;
}
java += indent + "entity = mb.getOrCreateEntity(\"" + entity.getTypeName() + "\");\n";
for (ForeignKeyConstraint foreignKey : entity.getForeignKeys()) {
imports.add(List.class);
imports.add(ForeignKeyConstraint.class);
imports.add(ForeignKeyConstraint.Action.class);
java += indent + "referencedEntity = mb.getEntity(\"" + foreignKey.getReferencedEntity().getTypeName() + "\");\n"
+ indent + "entity.addForeignKey(new ForeignKeyConstraint(entity, List.of(\n"
+ indent + " " + foreignKey.getFields().stream().map(f -> "entity.getField(\"" + f.getName() + "\")").collect(Collectors.joining(",\n " + indent)) + "\n"
+ indent + " ),\n"
+ indent + " referencedEntity, List.of(\n"
+ indent + " " + foreignKey.getReferencedFields().stream().map(f -> "referencedEntity.getField(\"" + f.getName() + "\")").collect(Collectors.joining(",\n " + indent)) + "\n"
+ indent + " ),\n"
+ indent + " ForeignKeyConstraint.Action." + foreignKey.getOnUpdate().name() + ", ForeignKeyConstraint.Action." + foreignKey.getOnDelete().name() + "));\n";
}
for (UniqueKeyConstraint uniqueKey : entity.getUniqueKeys()) {
imports.add(List.class);
imports.add(UniqueKeyConstraint.class);
java += indent + "entity.addUniqueKey(new UniqueKeyConstraint(entity, List.of(\n"
+ indent + " " + uniqueKey.getFields().stream().map(f -> "entity.getField(\"" + f.getName() + "\")").collect(Collectors.joining(",\n " + indent)) + "\n"
+ indent + " )));\n";
}
for (KeyConstraint key : entity.getKeys()) {
imports.add(List.class);
imports.add(KeyConstraint.class);
java += indent + "entity.addKey(new KeyConstraint(entity, List.of(\n"
+ indent + " " + key.getFields().stream().map(f -> "entity.getField(\"" + f.getName() + "\")").collect(Collectors.joining(",\n " + indent)) + "\n"
+ indent + " )));\n";
}
for (IndexConstraint index : entity.getIndexes()) {
imports.add(List.class);
imports.add(IndexConstraint.class);
java += indent + "entity.addIndex(new IndexConstraint(entity, List.of(\n"
+ indent + " " + index.getFields().stream().map(f -> "entity.getField(\"" + f.getName() + "\")").collect(Collectors.joining(",\n " + indent)) + "\n"
+ indent + " )));\n";
}
}
java += " }\n"
+ "}\n";
//imports
var normalImports = imports.stream().filter(e -> !e.getName().startsWith("java")).sorted(Comparator.comparing(Class::getName, String.CASE_INSENSITIVE_ORDER)).toList();
var javaImports = imports.stream().filter(e -> e.getName().startsWith("java")).sorted(Comparator.comparing(Class::getName, String.CASE_INSENSITIVE_ORDER)).toList();
//finalize
java = "package " + packageName + ";\n\n"
+ normalImports.stream().map(e -> "import " + e.getName().replace("$", ".") + ";").collect(Collectors.joining("\n")) + "\n\n"
+ javaImports.stream().map(e -> "import " + e.getName().replace("$", ".") + ";").collect(Collectors.joining("\n")) + "\n\n"
+ java;
return java;
}
}

View File

@@ -0,0 +1,116 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import jef.util.Check;
import lombok.Getter;
import java.util.Objects;
@Getter
public class ModelChangeDetector {
private final ModelBuilder from;
private final ModelBuilder to;
public ModelChangeDetector(ModelBuilder from, ModelBuilder to) {
this.from = Check.notNull(from, "from").clone();
this.to = Check.notNull(to, "to").clone();
}
public ModelChangeDetector detect() {
extractChanges();
return this;
}
private void extractChanges() {
extractChangesFromTo(from, to);
extractChangesFromTo(to, from);
for (int i = 0; i < to.getEntities().size(); i++) {
var toEntity = to.getEntities().get(i);
var fromEntity = from.getEntities().stream().filter(e -> e.getTypeName().equals(toEntity.getTypeName())).findFirst().orElse(null);
if (fromEntity != null) {
//entity empty
if (toEntity.getName().equals(fromEntity.getName())
&& toEntity.getFields().isEmpty()
&& toEntity.getPrimaryKey() == null
&& toEntity.getForeignKeys().isEmpty()
&& toEntity.getUniqueKeys().isEmpty()
&& toEntity.getKeys().isEmpty()
&& toEntity.getIndexes().isEmpty()
&& fromEntity.getFields().isEmpty()
&& fromEntity.getPrimaryKey() == null
&& fromEntity.getForeignKeys().isEmpty()
&& fromEntity.getUniqueKeys().isEmpty()
&& fromEntity.getKeys().isEmpty()
&& fromEntity.getIndexes().isEmpty()) {
to.dropEntity(toEntity);
from.dropEntity(fromEntity);
i--;
}
}
}
}
private void extractChangesFromTo(ModelBuilder from, ModelBuilder to) {
for (int i = 0; i < to.getEntities().size(); i++) {
var toEntity = to.getEntities().get(i);
var fromEntity = from.getEntities().stream().filter(e -> e.getTypeName().equals(toEntity.getTypeName())).findFirst().orElse(null);
if (fromEntity != null) {
for (int j = 0; j < toEntity.getFields().size(); j++) {
var toField = toEntity.getFields().get(j);
var fromField = fromEntity.getFields().stream().filter(e -> e.equals(toField)).findFirst().orElse(null);
if (fromField != null) {
toEntity.dropField(toField);
fromEntity.dropField(fromField);
j--;
}
}
if (Objects.equals(fromEntity.getPrimaryKey(), toEntity.getPrimaryKey())) {
fromEntity.setPrimaryKey(null);
toEntity.setPrimaryKey(null);
}
for (int j = 0; j < toEntity.getForeignKeys().size(); j++) {
var toFk = toEntity.getForeignKeys().get(j);
var fromFk = fromEntity.getForeignKeys().stream().filter(e -> e.equals(toFk)).findFirst().orElse(null);
if (fromFk != null) {
toEntity.dropForeignKey(toFk);
fromEntity.dropForeignKey(fromFk);
j--;
}
}
for (int j = 0; j < toEntity.getUniqueKeys().size(); j++) {
var toUnique = toEntity.getUniqueKeys().get(j);
var fromUnique = fromEntity.getUniqueKeys().stream().filter(e -> e.equals(toUnique)).findFirst().orElse(null);
if (fromUnique != null) {
toEntity.dropUniqueKey(toUnique);
fromEntity.dropUniqueKey(fromUnique);
j--;
}
}
for (int j = 0; j < toEntity.getKeys().size(); j++) {
var toKey = toEntity.getKeys().get(j);
var fromKey = fromEntity.getKeys().stream().filter(e -> e.equals(toKey)).findFirst().orElse(null);
if (fromKey != null) {
toEntity.dropKey(toKey);
fromEntity.dropKey(fromKey);
j--;
}
}
for (int j = 0; j < toEntity.getIndexes().size(); j++) {
var toIx = toEntity.getIndexes().get(j);
var fromIx = fromEntity.getIndexes().stream().filter(e -> e.equals(toIx)).findFirst().orElse(null);
if (fromIx != null) {
toEntity.dropIndex(toIx);
fromEntity.dropIndex(fromIx);
j--;
}
}
}
}
}
}

View File

@@ -0,0 +1,41 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class AddFieldOperation implements MigrationOperation {
protected final String table;
protected final String field;
protected final String sqlType;
protected final boolean notNull;
public static class Builder implements MigrationOperation.Builder<AddFieldOperation> {
protected final String table;
protected final String field;
protected String sqlType;
protected boolean notNull;
public Builder(String table, String field) {
this.table = table;
this.field = field;
}
public Builder sqlType(String sqlType) {
this.sqlType = sqlType;
return this;
}
public Builder notNull(boolean notNull) {
this.notNull = notNull;
return this;
}
public AddFieldOperation build() {
return new AddFieldOperation(table, field, sqlType, notNull);
}
}
}

View File

@@ -0,0 +1,44 @@
package jef.model.migration.operation;
import jef.model.constraints.ForeignKeyConstraint;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
public class AddForeignKeyOperation implements MigrationOperation {
private final String name;
private final String table;
private final List<String> fields;
private final String referencedTable;
private final List<String> referencedFields;
private final ForeignKeyConstraint.Action onUpdate;
private final ForeignKeyConstraint.Action onDelete;
public static class Builder implements MigrationOperation.Builder<AddForeignKeyOperation> {
private final String name;
private final String table;
private final List<String> fields;
private final String referencedTable;
private final List<String> referencedFields;
private final ForeignKeyConstraint.Action onUpdate;
private final ForeignKeyConstraint.Action onDelete;
public Builder(String name, String table, List<String> fields, String referencedTable, List<String> referencedFields, ForeignKeyConstraint.Action onUpdate, ForeignKeyConstraint.Action onDelete) {
this.name = name;
this.table = table;
this.fields = fields;
this.referencedTable = referencedTable;
this.referencedFields = referencedFields;
this.onUpdate = onUpdate;
this.onDelete = onDelete;
}
public AddForeignKeyOperation build() {
return new AddForeignKeyOperation(name, table, fields, referencedTable, referencedFields, onUpdate, onDelete);
}
}
}

View File

@@ -0,0 +1,23 @@
package jef.model.migration.operation;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public class AddIndexOperation extends AddKeyOperationBase {
public AddIndexOperation(String name, String table, List<String> fields) {
super(name, table, fields);
}
public static class Builder extends AddKeyOperationBase.Builder<AddIndexOperation> {
public Builder(String name, String table, List<String> fields) {
super(name, table, fields);
}
public AddIndexOperation build() {
return new AddIndexOperation(name, table, fields);
}
}
}

View File

@@ -0,0 +1,23 @@
package jef.model.migration.operation;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public class AddKeyOperation extends AddKeyOperationBase {
public AddKeyOperation(String name, String table, List<String> fields) {
super(name, table, fields);
}
public static class Builder extends AddKeyOperationBase.Builder<AddKeyOperation> {
public Builder(String name, String table, List<String> fields) {
super(name, table, fields);
}
public AddKeyOperation build() {
return new AddKeyOperation(name, table, fields);
}
}
}

View File

@@ -0,0 +1,26 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public abstract class AddKeyOperationBase implements MigrationOperation {
protected final String name;
protected final String table;
protected final List<String> fields;
public abstract static class Builder<T extends AddKeyOperationBase> implements MigrationOperation.Builder<T> {
protected final String name;
protected final String table;
protected final List<String> fields;
public Builder(String name, String table, List<String> fields) {
this.name = name;
this.table = table;
this.fields = fields;
}
}
}

View File

@@ -0,0 +1,23 @@
package jef.model.migration.operation;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public class AddPrimaryKeyOperation extends AddKeyOperationBase {
public AddPrimaryKeyOperation(String name, String table, List<String> fields) {
super(name, table, fields);
}
public static class Builder extends AddKeyOperationBase.Builder<AddPrimaryKeyOperation> {
public Builder(String name, String table, List<String> fields) {
super(name, table, fields);
}
public AddPrimaryKeyOperation build() {
return new AddPrimaryKeyOperation(name, table, fields);
}
}
}

View File

@@ -0,0 +1,28 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
public class AddTableOperation implements MigrationOperation {
private final String table;
private final List<AddFieldOperation.Builder> fields;
public static class Builder implements MigrationOperation.Builder<AddTableOperation> {
private final String table;
private final List<AddFieldOperation.Builder> fields;
public Builder(String table, List<AddFieldOperation.Builder> fields) {
this.table = table;
this.fields = fields;
}
public AddTableOperation build() {
return new AddTableOperation(table, fields);
}
}
}

View File

@@ -0,0 +1,23 @@
package jef.model.migration.operation;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public class AddUniqueKeyOperation extends AddKeyOperationBase {
public AddUniqueKeyOperation(String name, String table, List<String> fields) {
super(name, table, fields);
}
public static class Builder extends AddKeyOperationBase.Builder<AddUniqueKeyOperation> {
public Builder(String name, String table, List<String> fields) {
super(name, table, fields);
}
public AddUniqueKeyOperation build() {
return new AddUniqueKeyOperation(name, table, fields);
}
}
}

View File

@@ -0,0 +1,27 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class DropConstraintOperation implements MigrationOperation {
private final String name;
private final String table;
public static class Builder implements MigrationOperation.Builder<DropConstraintOperation> {
private final String name;
private final String table;
public Builder(String name, String table) {
this.name = name;
this.table = table;
}
public DropConstraintOperation build() {
return new DropConstraintOperation(name, table);
}
}
}

View File

@@ -0,0 +1,27 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class DropFieldOperation implements MigrationOperation {
private final String table;
private final String field;
public static class Builder implements MigrationOperation.Builder<DropFieldOperation> {
private final String table;
private final String field;
public Builder(String table, String field) {
this.table = table;
this.field = field;
}
public DropFieldOperation build() {
return new DropFieldOperation(table, field);
}
}
}

View File

@@ -0,0 +1,24 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class DropTableOperation implements MigrationOperation {
private final String table;
public static class Builder implements MigrationOperation.Builder<DropTableOperation> {
private final String table;
public Builder(String table) {
this.table = table;
}
public DropTableOperation build() {
return new DropTableOperation(table);
}
}
}

View File

@@ -0,0 +1,7 @@
package jef.model.migration.operation;
public interface MigrationOperation {
public interface Builder<T extends MigrationOperation> {
T build();
}
}

View File

@@ -0,0 +1,30 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class RenameFieldOperation implements MigrationOperation {
private final String table;
private final String oldName;
private final String newName;
public static class Builder implements MigrationOperation.Builder<RenameFieldOperation> {
private final String table;
private final String oldName;
private final String newName;
public Builder(String table, String oldName, String newName) {
this.table = table;
this.oldName = oldName;
this.newName = newName;
}
public RenameFieldOperation build() {
return new RenameFieldOperation(table, oldName, newName);
}
}
}

View File

@@ -0,0 +1,27 @@
package jef.model.migration.operation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class RenameTableOperation implements MigrationOperation {
private final String oldName;
private final String newName;
public static class Builder implements MigrationOperation.Builder<RenameTableOperation> {
private final String oldName;
private final String newName;
public Builder(String oldName, String newName) {
this.oldName = oldName;
this.newName = newName;
}
public RenameTableOperation build() {
return new RenameTableOperation(oldName, newName);
}
}
}

View File

@@ -0,0 +1,32 @@
package jef.model.migration.operation;
import lombok.Getter;
import java.util.List;
@Getter
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);
this.newName = newName;
}
public static class Builder extends AddFieldOperation.Builder {
private String newName;
public Builder(String table, String field) {
super(table, field);
}
public Builder newName(String newName) {
this.newName = newName;
return this;
}
public UpdateFieldOperation build() {
return new UpdateFieldOperation(table, field, newName, sqlType, notNull);
}
}
}

View File

@@ -0,0 +1,10 @@
package jef.util;
public abstract class Check {
public static <T> T notNull(T t, String name) {
if (t == null) {
throw new IllegalArgumentException(name + " must be not null");
}
return t;
}
}

View File

@@ -0,0 +1,18 @@
package jef.util;
import java.util.Optional;
public abstract class Util {
public static <T> Optional<T> tryGet(ThrowableSupplier<T> s) {
try {
return Optional.ofNullable(s.get());
} catch (Throwable t) {
return Optional.empty();
}
}
@FunctionalInterface
public interface ThrowableSupplier<T> {
T get() throws Throwable;
}
}

View File

@@ -16,7 +16,7 @@ class DbContextSimpleTest {
public void test() {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertEquals(5, mb.getEntity(TestClass.class).getFields().size());
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).count());

View File

@@ -16,8 +16,8 @@ class EntityInitializerMultiple1To1RelationTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(2, mb.getEntities().size());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass2.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));

View File

@@ -18,9 +18,9 @@ class ForeignKeyInitializerNToNRelationTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(3, mb.getEntities().size());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals(Mapping.class.getSimpleName(), mb.getEntity(Mapping.class).getName());
assertEquals("objects2", mb.getEntity(TestClass2.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertEquals("rels", mb.getEntity(Mapping.class).getName());
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(Mapping.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(Mapping.class)));

View File

@@ -18,9 +18,9 @@ class ForeignKeyInitializerNestedList2LayerTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(3, mb.getEntities().size());
assertEquals(TestClass3.class.getSimpleName(), mb.getEntity(TestClass3.class).getName());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass3.class).getName());
assertEquals("nested2", mb.getEntity(TestClass2.class).getName());
assertEquals("nested", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass3.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass3.class)));
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -18,8 +18,8 @@ class ForeignKeyInitializerNestedListSimpleTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(2, mb.getEntities().size());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass2.class).getName());
assertEquals("nested", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));

View File

@@ -16,9 +16,9 @@ class ForeignKeyInitializerNestedObject2LayerTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(3, mb.getEntities().size());
assertEquals(TestClass3.class.getSimpleName(), mb.getEntity(TestClass3.class).getName());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass3.class).getName());
assertEquals("nested2", mb.getEntity(TestClass2.class).getName());
assertEquals("nested", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass3.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass3.class)));
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
@@ -57,14 +57,14 @@ class ForeignKeyInitializerNestedObject2LayerTest {
assertEquals(mb.getEntity(TestClass3.class), mb.getEntity(TestClass3.class).getForeignKeys().get(0).getEntity());
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass3.class).getForeignKeys().get(0).getReferencedEntity());
assertEquals(mb.getEntity(TestClass3.class).getFields().stream().filter(e -> e.getName().equals("nested2I")).toList(),
mb.getEntity(TestClass3.class).getForeignKeys().get(0).getFields());
mb.getEntity(TestClass3.class).getForeignKeys().get(0).getFields());
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass3.class).getForeignKeys().get(0).getReferencedFields());
//refs TestClass2, TestClass
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass2.class).getForeignKeys().get(0).getEntity());
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass2.class).getForeignKeys().get(0).getReferencedEntity());
assertEquals(mb.getEntity(TestClass2.class).getFields().stream().filter(e -> e.getName().equals("nestedI")).toList(),
mb.getEntity(TestClass2.class).getForeignKeys().get(0).getFields());
mb.getEntity(TestClass2.class).getForeignKeys().get(0).getFields());
assertEquals(mb.getEntity(TestClass.class).getPrimaryKey().getFields(), mb.getEntity(TestClass2.class).getForeignKeys().get(0).getReferencedFields());
// /keys ------------------------
}

View File

@@ -16,8 +16,8 @@ class ForeignKeyInitializerNestedObjectSimpleTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(2, mb.getEntities().size());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass2.class).getName());
assertEquals("nested", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));

View File

@@ -16,7 +16,7 @@ class ForeignKeyInitializerRecursive1To1RelationTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -18,7 +18,7 @@ class ForeignKeyInitializerRecursive1ToNRelationTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -18,7 +18,7 @@ class ForeignKeyInitializerRecursiveWithParent1ToNRelationTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -0,0 +1,258 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.annotations.Index;
import jef.model.annotations.Key;
import jef.model.annotations.Unique;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ModelBuilderCloneTest {
@Test
public void testClone() {
var mb = ModelBuilder.from(Ctx.class);
var copy = mb.clone();
debugHelper(mb, copy);
//true check
assertEquals(mb, copy);
//proper clone reference checks
for (int i = 0; i < mb.getEntities().size(); i++) {
assertTrue(mb.getEntities().get(i) != copy.getEntities().get(i));
for (int j = 0; j < mb.getEntities().get(0).getFields().size(); j++) {
assertTrue(mb.getEntities().get(i).getFields().get(j) != copy.getEntities().get(i).getFields().get(j));
assertTrue(mb.getEntities().get(i).getFields().get(j).getEntity() != copy.getEntities().get(i).getFields().get(j).getEntity());
}
assertTrue(mb.getEntities().get(i).getPrimaryKey() != copy.getEntities().get(i).getPrimaryKey());
assertTrue(mb.getEntities().get(i).getPrimaryKey().getEntity() != copy.getEntities().get(i).getPrimaryKey().getEntity());
for (int j = 0; j < mb.getEntities().get(i).getPrimaryKey().getFields().size(); j++) {
assertTrue(mb.getEntities().get(i).getPrimaryKey().getFields().get(j) != copy.getEntities().get(i).getPrimaryKey().getFields().get(j));
assertTrue(mb.getEntities().get(i).getPrimaryKey().getFields().get(j).getEntity() != copy.getEntities().get(i).getPrimaryKey().getFields().get(j).getEntity());
}
for (int j = 0; j < mb.getEntities().get(i).getForeignKeys().size(); j++) {
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j) != copy.getEntities().get(i).getForeignKeys().get(j));
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j).getEntity() != copy.getEntities().get(i).getForeignKeys().get(j).getEntity());
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j).getReferencedEntity() != copy.getEntities().get(i).getForeignKeys().get(j).getReferencedEntity());
for (int k = 0; k < mb.getEntities().get(i).getForeignKeys().get(j).getFields().size(); k++) {
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j).getFields().get(k) != copy.getEntities().get(i).getForeignKeys().get(j).getFields().get(k));
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j).getFields().get(k).getEntity() != copy.getEntities().get(i).getForeignKeys().get(j).getFields().get(k).getEntity());
}
for (int k = 0; k < mb.getEntities().get(i).getForeignKeys().get(j).getReferencedFields().size(); k++) {
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j).getReferencedFields().get(k) != copy.getEntities().get(i).getForeignKeys().get(j).getReferencedFields().get(k));
assertTrue(mb.getEntities().get(i).getForeignKeys().get(j).getReferencedFields().get(k).getEntity() != copy.getEntities().get(i).getForeignKeys().get(j).getReferencedFields().get(k).getEntity());
}
}
for (int j = 0; j < mb.getEntities().get(i).getUniqueKeys().size(); j++) {
assertTrue(mb.getEntities().get(i).getUniqueKeys().get(j) != copy.getEntities().get(i).getUniqueKeys().get(j));
assertTrue(mb.getEntities().get(i).getUniqueKeys().get(j).getEntity() != copy.getEntities().get(i).getUniqueKeys().get(j).getEntity());
for (int k = 0; k < mb.getEntities().get(i).getUniqueKeys().get(j).getFields().size(); k++) {
assertTrue(mb.getEntities().get(i).getUniqueKeys().get(j).getFields().get(k) != copy.getEntities().get(i).getUniqueKeys().get(j).getFields().get(k));
assertTrue(mb.getEntities().get(i).getUniqueKeys().get(j).getFields().get(k).getEntity() != copy.getEntities().get(i).getUniqueKeys().get(j).getFields().get(k).getEntity());
}
}
for (int j = 0; j < mb.getEntities().get(i).getKeys().size(); j++) {
assertTrue(mb.getEntities().get(i).getKeys().get(j) != copy.getEntities().get(i).getKeys().get(j));
assertTrue(mb.getEntities().get(i).getKeys().get(j).getEntity() != copy.getEntities().get(i).getKeys().get(j).getEntity());
for (int k = 0; k < mb.getEntities().get(i).getKeys().get(j).getFields().size(); k++) {
assertTrue(mb.getEntities().get(i).getKeys().get(j).getFields().get(k) != copy.getEntities().get(i).getKeys().get(j).getFields().get(k));
assertTrue(mb.getEntities().get(i).getKeys().get(j).getFields().get(k).getEntity() != copy.getEntities().get(i).getKeys().get(j).getFields().get(k).getEntity());
}
}
for (int j = 0; j < mb.getEntities().get(i).getIndexes().size(); j++) {
assertTrue(mb.getEntities().get(i).getIndexes().get(j) != copy.getEntities().get(i).getIndexes().get(j));
assertTrue(mb.getEntities().get(i).getIndexes().get(j).getEntity() != copy.getEntities().get(i).getIndexes().get(j).getEntity());
for (int k = 0; k < mb.getEntities().get(i).getIndexes().get(j).getFields().size(); k++) {
assertTrue(mb.getEntities().get(i).getIndexes().get(j).getFields().get(k) != copy.getEntities().get(i).getIndexes().get(j).getFields().get(k));
assertTrue(mb.getEntities().get(i).getIndexes().get(j).getFields().get(k).getEntity() != copy.getEntities().get(i).getIndexes().get(j).getFields().get(k).getEntity());
}
}
}
}
public static void debugHelper(ModelBuilder expected, ModelBuilder actual) {
//debug helpers
assertEquals(expected.getEntities().size(), actual.getEntities().size());
for (int i = 0; i < expected.getEntities().size(); i++) {
var o = expected.getEntities().get(i);
var c = actual.getEntities().get(i);
if (!o.getTypeName().equals(c.getTypeName())) {
System.out.println("typenames differ: entity " + o.getName());
System.out.println("ex: " + o.getTypeName());
System.out.println("ac: " + c.getTypeName());
}
if (!Objects.equals(o.getType(), c.getType())) {
System.out.println("types differ: entity " + o.getName());
System.out.println("ex: " + o.getType());
System.out.println("ac: " + c.getType());
}
if (!o.getName().equals(c.getName())) {
System.out.println("names differ: entity " + o.getName());
System.out.println("ex: " + o.getName());
System.out.println("ac: " + c.getName());
}
if (!Objects.equals(o.getPrimaryKey(), c.getPrimaryKey())) {
System.out.println("primary keys differ: entity " + o.getName());
System.out.println("ex: " + o.getPrimaryKey());
System.out.println("ac: " + c.getPrimaryKey());
}
assertEquals(o.getForeignKeys().size(), c.getForeignKeys().size());
for (int j = 0; j < o.getForeignKeys().size(); j++) {
var ok = o.getForeignKeys().get(j);
var ck = c.getForeignKeys().get(j);
if (!Objects.equals(ok, ck)) {
System.out.println("foreign keys differ: entity " + o.getName());
System.out.println("ex: " + ok);
System.out.println("ac: " + ck);
}
}
assertEquals(o.getUniqueKeys().size(), c.getUniqueKeys().size());
for (int j = 0; j < o.getUniqueKeys().size(); j++) {
var ok = o.getUniqueKeys().get(j);
var ck = c.getUniqueKeys().get(j);
if (!Objects.equals(ok, ck)) {
System.out.println("unique keys differ: entity " + o.getName());
System.out.println("ex: " + ok);
System.out.println("ac: " + ck);
}
}
assertEquals(o.getKeys().size(), c.getKeys().size());
for (int j = 0; j < o.getKeys().size(); j++) {
var ok = o.getKeys().get(j);
var ck = c.getKeys().get(j);
if (!Objects.equals(ok, ck)) {
System.out.println("keys differ: entity " + o.getName());
System.out.println("ex: " + ok);
System.out.println("ac: " + ck);
}
}
assertEquals(o.getIndexes().size(), c.getIndexes().size());
for (int j = 0; j < o.getIndexes().size(); j++) {
var ok = o.getIndexes().get(j);
var ck = c.getIndexes().get(j);
if (!Objects.equals(ok, ck)) {
System.out.println("indexes differ: entity " + o.getName());
System.out.println("ex: " + ok);
System.out.println("ac: " + ck);
}
}
assertEquals(o.getFields().size(), c.getFields().size());
for (int j = 0; j < o.getIndexes().size(); j++) {
var of = o.getFields().get(j);
var cf = c.getFields().get(j);
if (of.isModelField() != cf.isModelField()) {
System.out.println("is model fields differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.isModelField());
System.out.println("ac: " + cf.isModelField());
}
if (of.isDatabaseField() != cf.isDatabaseField()) {
System.out.println("is database fields differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.isDatabaseField());
System.out.println("ac: " + cf.isDatabaseField());
}
if (of.isNotNull() != cf.isNotNull()) {
System.out.println("is notnull differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.isNotNull());
System.out.println("ac: " + cf.isNotNull());
}
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());
System.out.println("ac: " + cf.getEntity().getName());
}
if (!of.getTypeName().equals(cf.getTypeName())) {
System.out.println("typenames differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.getTypeName());
System.out.println("ac: " + cf.getTypeName());
}
if (!Objects.equals(of.getType(), cf.getType())) {
System.out.println("types differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.getType());
System.out.println("ac: " + cf.getType());
}
if (!Objects.equals(of.getField() == null ? null : of.getField().getName(), cf.getField() == null ? null : cf.getField().getName())) {
System.out.println("fields differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.getField());
System.out.println("ac: " + cf.getField());
}
if (!Objects.equals(of.getForeignKeyModelLink() == null ? null : of.getForeignKeyModelLink().getName(),
cf.getForeignKeyModelLink() == null ? null : cf.getForeignKeyModelLink().getName())) {
System.out.println("fk model links differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + (of.getForeignKeyModelLink() == null ? null : of.getForeignKeyModelLink().getName()));
System.out.println("ac: " + (cf.getForeignKeyModelLink() == null ? null : cf.getForeignKeyModelLink().getName()));
}
if (!of.getName().equals(cf.getName())) {
System.out.println("names differ: entity " + o.getName() + ", field " + of.getName());
System.out.println("ex: " + of.getName());
System.out.println("ac: " + cf.getName());
}
// if (!Objects.equals(of, cf)) {
// System.out.println("fields differ: entity " + o.getName() + ", field " + of.getName());
// System.out.println("ex: " + of);
// System.out.println("ac: " + cf);
// }
}
}
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects2;
@Clazz(clazz = TestClass.class)
private DbSet<TestClass> objects1;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
@Key
public double d;
@Index
public float f;
@Unique
public long l;
private TestClass previous;
private TestClass next;
@Clazz(clazz = TestClass.class)
private List<TestClass> children;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public TestClass o;
public TestClass o2;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model;
import jef.DbSet;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.annotations.Index;
import jef.model.annotations.Key;
import jef.model.annotations.Unique;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ModelBuilderSimpleTest {
@Test
public void testClone() {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass.class)
private DbSet<TestClass> objects1;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -19,8 +19,8 @@ class ForeignKeyProcessorExposeTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(2, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass2.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -19,7 +19,7 @@ class ForeignKeyProcessorSelfReferenceTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -19,8 +19,8 @@ class ForeignKeyProcessorSimpleTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(2, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals(TestClass2.class.getSimpleName(), mb.getEntity(TestClass2.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertEquals("objects2", mb.getEntity(TestClass2.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));
assertTrue(mb.getEntity(TestClass2.class).getFields().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
assertTrue(mb.getEntity(TestClass.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass.class)));

View File

@@ -19,7 +19,7 @@ class IndexProcessorClassTest {
var mb = ModelBuilder.from(CtxIndexOnClass.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isIndex());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isIndex());

View File

@@ -19,7 +19,7 @@ class IndexProcessorFieldTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isIndex());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isIndex());

View File

@@ -19,7 +19,7 @@ class KeyProcessorClassTest {
var mb = ModelBuilder.from(CtxKeyOnClass.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isKey());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isKey());

View File

@@ -19,7 +19,7 @@ class KeyProcessorFieldTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isKey());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isKey());

View File

@@ -19,7 +19,7 @@ class NotNullProcessorTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isNotNull());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isNotNull());

View File

@@ -19,7 +19,7 @@ class UniqueProcessorClassTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isUnique());
assertTrue(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isUnique());

View File

@@ -19,7 +19,7 @@ class UniqueProcessorFieldTest {
var mb = ModelBuilder.from(Ctx.class);
assertEquals(1, mb.getEntities().size());
assertEquals(TestClass.class.getSimpleName(), mb.getEntity(TestClass.class).getName());
assertEquals("objects1", mb.getEntity(TestClass.class).getName());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i")).findFirst().get().isUnique());
assertFalse(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d")).findFirst().get().isUnique());

View File

@@ -0,0 +1,111 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.constraints.PrimaryKeyConstraint;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropTableOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorAddEntityTest extends MigrationCreatorTestBase {
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getOrCreateEntity("AddedEntity");
ent.getOrCreateField("id", int.class.getName());
ent.getOrCreateField("addedField", int.class.getName());
ent.setPrimaryKey(new PrimaryKeyConstraint(ent, List.of(ent.getField("id"))));
ent.addForeignKey(new ForeignKeyConstraint(ent, List.of(ent.getField("addedField")), ent, List.of(ent.getField("id")), ForeignKeyConstraint.Action.CASCADE, ForeignKeyConstraint.Action.CASCADE));
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(3, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddTableOperation o
&& o.getTable().equals("AddedEntity")
&& 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("addedField")).count() == 1)
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("AddedEntity")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("id"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("AddedEntity")
&& o.getReferencedTable().equals("AddedEntity")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("addedField")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("id"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(3, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("AddedEntity")
&& o.getName().equals("FK_AddedEntity_AddedEntity_addedField"))
.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()
.filter(e -> e instanceof DropTableOperation o
&& o.getTable().equals("AddedEntity"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,95 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.migration.operation.AddFieldOperation;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropFieldOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorAddFieldTest extends MigrationCreatorTestBase{
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass2.class);
ent.getOrCreateField("addedField", int.class.getName());
ent.addForeignKey(new ForeignKeyConstraint(ent, List.of(ent.getField("addedField")), ent, List.of(ent.getField("i")), ForeignKeyConstraint.Action.CASCADE, ForeignKeyConstraint.Action.CASCADE));
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(2, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddFieldOperation o
&& o.getTable().equals("objects1")
&& o.getField().equals("addedField"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("objects1")
&& o.getReferencedTable().equals("objects1")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("addedField")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(2, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("objects1")
&& o.getName().equals("FK_objects1_objects1_addedField"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropFieldOperation o
&& o.getTable().equals("objects1")
&& o.getField().equals("addedField"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,83 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.constraints.ForeignKeyConstraint;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorAddForeignKeyTest extends MigrationCreatorTestBase{
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass.class);
ent.addForeignKey(new ForeignKeyConstraint(ent, List.of(ent.getField("i2")), ent, List.of(ent.getField("i")), ForeignKeyConstraint.Action.CASCADE, ForeignKeyConstraint.Action.CASCADE));
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(1, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("nested")
&& o.getReferencedTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i2")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(1, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("FK_nested_nested_i2"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public int i2 = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,78 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.migration.operation.AddIndexOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorAddIndexTest extends MigrationCreatorTestBase{
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass.class);
ent.getField("d").setIndex(true);
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(1, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddIndexOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("d"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(1, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("I_nested_d"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,78 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.migration.operation.AddKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorAddKeyTest extends MigrationCreatorTestBase{
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass.class);
ent.getField("d").setKey(true);
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(1, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("d"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(1, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("K_nested_d"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,78 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.migration.operation.AddUniqueKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorAddUniqueTest extends MigrationCreatorTestBase{
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass.class);
ent.getField("d").setUnique(true);
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(1, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddUniqueKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("d"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(1, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("U_nested_d"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,61 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.annotations.Index;
import jef.model.annotations.Key;
import jef.model.annotations.Unique;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorEmptyTest extends MigrationCreatorTestBase{
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "EmptyMigration", "test",new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
assertEquals(0, res.getStepsUp().size());
assertEquals(0, res.getStepsDown().size());
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
@Index
public double d;
@Unique
public float f;
@Key
public long l;
}
}

View File

@@ -0,0 +1,130 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddTableOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.DropTableOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorInitialMigrationTest extends MigrationCreatorTestBase {
@Test
public void test() {
var from = new ModelBuilder(List.of());
var to = ModelBuilder.from(Ctx.class);
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "InitialMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(5, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddTableOperation o
&& o.getTable().equals("objects1")
&& o.getFields().size() == 1
&& o.getFields().get(0).build().getField().equals("i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddTableOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 6
&& o.getFields().stream().filter(f -> f.build().getField().equals("d")).count() == 1
&& o.getFields().stream().filter(f -> f.build().getField().equals("f")).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("o")).count() == 1
&& o.getFields().stream().filter(f -> f.build().getField().equals("nestedI")).count() == 1)
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("objects1")
&& 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("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("nested")
&& o.getReferencedTable().equals("objects1")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("nestedI")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(5, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("FK_nested_objects1_nestedI"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& 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()
.filter(e -> e instanceof DropTableOperation o
&& o.getTable().equals("nested"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropTableOperation o
&& o.getTable().equals("objects1"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,129 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.RenameTableOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorRenameEntityTest extends MigrationCreatorTestBase {
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass.class);
ent.setName("d2");
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(5, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("FK_nested_objects1_nestedI"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof RenameTableOperation o
&& o.getOldName().equals("nested")
&& o.getNewName().equals("d2"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("d2")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("d2")
&& o.getReferencedTable().equals("objects1")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("nestedI")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(5, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("d2")
&& o.getName().equals("FK_d2_objects1_nestedI"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("d2")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof RenameTableOperation o
&& o.getOldName().equals("d2")
&& o.getNewName().equals("nested"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("nested")
&& o.getReferencedTable().equals("objects1")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("nestedI")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,210 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.ForeignKey;
import jef.model.annotations.Id;
import jef.model.annotations.Index;
import jef.model.annotations.Key;
import jef.model.annotations.Unique;
import jef.model.migration.operation.AddForeignKeyOperation;
import jef.model.migration.operation.AddIndexOperation;
import jef.model.migration.operation.AddKeyOperation;
import jef.model.migration.operation.AddPrimaryKeyOperation;
import jef.model.migration.operation.AddUniqueKeyOperation;
import jef.model.migration.operation.DropConstraintOperation;
import jef.model.migration.operation.RenameFieldOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorRenameFieldConstraintsTest extends MigrationCreatorTestBase {
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
to.getEntity(TestClass.class).getField("i").setName("i2");
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(11, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("FK_nested_nested_iFk"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("U_nested_i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("K_nested_i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("I_nested_i"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof RenameFieldOperation o
&& o.getTable().equals("nested")
&& o.getOldName().equals("i")
&& o.getNewName().equals("i2"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i2"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("nested")
&& o.getReferencedTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("iFk")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i2"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddUniqueKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i2"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i2"))
.count());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof AddIndexOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i2"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(11, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("FK_nested_nested_iFk"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("PRIMARY"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("U_nested_i2"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("K_nested_i2"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof DropConstraintOperation o
&& o.getTable().equals("nested")
&& o.getName().equals("I_nested_i2"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof RenameFieldOperation o
&& o.getTable().equals("nested")
&& o.getOldName().equals("i2")
&& o.getNewName().equals("i"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddPrimaryKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddForeignKeyOperation o
&& o.getTable().equals("nested")
&& o.getReferencedTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("iFk")
&& o.getReferencedFields().size() == 1
&& o.getReferencedFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddUniqueKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddKeyOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof AddIndexOperation o
&& o.getTable().equals("nested")
&& o.getFields().size() == 1
&& o.getFields().get(0).equals("i"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
@Index
@Unique
@Key
public int i = 1;
@ForeignKey(entity = TestClass.class, getterOrField = "i")
public int iFk = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,78 @@
package jef.model.migration.creator;
import jef.DbSet;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.annotations.Clazz;
import jef.model.annotations.Id;
import jef.model.migration.operation.RenameFieldOperation;
import jef.serializable.SerializableObject;
import lombok.Getter;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorRenameFieldTest extends MigrationCreatorTestBase {
@Test
public void test() {
var from = ModelBuilder.from(Ctx.class);
var to = ModelBuilder.from(Ctx.class);
var ent = to.getEntity(TestClass.class);
ent.getField("d").setName("d2");
var mc = new MigrationCreator();
var res = mc.createMigration(from, to, "SomeMigration", "test", new ModelBuilderGenerator(from, "Current", "test").generate().getJava());
try {
validateUp(res);
validateDown(res);
} catch (Throwable t) {
System.out.println(res.getMigration());
throw t;
}
validateMigration(res, from, to);
}
private void validateUp(MigrationCreator.Result res) {
assertEquals(1, res.getStepsUp().size());
assertEquals(1, res.getStepsUp().stream()
.filter(e -> e instanceof RenameFieldOperation o
&& o.getTable().equals("nested")
&& o.getOldName().equals("d")
&& o.getNewName().equals("d2"))
.count());
}
private void validateDown(MigrationCreator.Result res) {
assertEquals(1, res.getStepsDown().size());
assertEquals(1, res.getStepsDown().stream()
.filter(e -> e instanceof RenameFieldOperation o
&& o.getTable().equals("nested")
&& o.getOldName().equals("d2")
&& o.getNewName().equals("d"))
.count());
}
public static class Ctx extends DbContext {
@Clazz(clazz = TestClass2.class)
private DbSet<TestClass2> objects1;
}
@Getter
public static class TestClass2 extends SerializableObject {
@Id
public int i = 1;
@Clazz(clazz = TestClass.class)
public List<TestClass> nested;
}
@Getter
public static class TestClass extends SerializableObject {
@Id
public int i = 1;
public Object o = new Object();
public double d;
public float f;
public long l;
}
}

View File

@@ -0,0 +1,142 @@
package jef.model.migration.creator;
import jef.model.DbContext;
import jef.model.ModelBuilder;
import jef.model.ModelBuilderCloneTest;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MigrationCreatorTestBase {
private static final File JAVA_FILES_DIR = new File("target/jef-tests/sources");
private static final File CLASS_FILES_DIR = new File("target/jef-tests/classes");
public void validateMigration(MigrationCreator.Result result, ModelBuilder from, ModelBuilder to) {
validateMigrationJava(result.getMigration());
validatePreMigrationSnapshotJava(result.getMigrationSnapshot(), from);
validatePostMigrationSnapshotJava(result.getCurrentSnapshot(), to);
}
public void validateMigrationJava(String migrationJava) {
try {
var packageName = findPackageName(migrationJava);
var className = findClassName(migrationJava);
compile(packageName, className, migrationJava);
var clazz = loadClass(packageName, className);
//for migration just test if it's compiling
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Failed to load generated class", t);
}
}
public void validatePreMigrationSnapshotJava(String snapshotJava, ModelBuilder from) {
try {
var packageName = findPackageName(snapshotJava);
var className = findClassName(snapshotJava);
compile(packageName, className, snapshotJava);
var clazz = (Class<? extends DbContext>) loadClass(packageName, className);
var mb = ModelBuilder.from(clazz);
ModelBuilderCloneTest.debugHelper(from, mb);
assertEquals(from, mb);
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Failed to load generated class", t);
}
}
public void validatePostMigrationSnapshotJava(String snapshotJava, ModelBuilder to) {
try {
var packageName = findPackageName(snapshotJava);
var className = findClassName(snapshotJava);
compile(packageName, className, snapshotJava);
var clazz = (Class<? extends DbContext>) loadClass(packageName, className);
var mb = ModelBuilder.from(clazz);
assertEquals(to, mb);
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Failed to load generated class", t);
}
}
private File compile(String packageName, String className, String java) {
try {//src
File srcdir = new File(getSourcesFilesDir(), packageName.replace(".", File.separator));
srcdir.mkdirs();
File srcf = new File(srcdir, className + ".java");
Files.writeString(srcf.toPath(), java, StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
//dest
File destdir = getClassFilesDir();
var process = new ProcessBuilder()
.command("javac", //TODO use javac for current jdk
"-cp", "target/classes" + File.pathSeparator + "target/test-classes",
"-encoding", "UTF8",
"-g", //debug symbols
"-d", destdir.getPath(), //target
srcf.getPath()
)
.inheritIO()
.start();
process.waitFor(10, TimeUnit.SECONDS);
var exitCode = process.exitValue();
assertEquals(0, exitCode, "Compilation failed");
return new File(new File(destdir, packageName.replace(".", File.separator)), srcf.getName().replace(".java", ".class"));
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Error while compiling generated class", t);
}
}
private Class<?> loadClass(String packageName, String className) {
try {
var cl = new URLClassLoader(new URL[]{getClassFilesDir().toURL()});
return cl.loadClass(packageName + "." + className);
} catch (AssertionError | RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new RuntimeException("Failed to load generated class", t);
}
}
private String findPackageName(String java) {
Pattern p = Pattern.compile("package ([^;]+);");
Matcher m = p.matcher(java);
m.find();
return m.group(1);
}
private String findClassName(String java) {
Pattern p = Pattern.compile("public class ([^ ]+)");
Matcher m = p.matcher(java);
m.find();
return m.group(1);
}
private File getSourcesFilesDir() {
File srcdir = new File(JAVA_FILES_DIR, getClass().getSimpleName());
srcdir.mkdirs();
return srcdir;
}
private File getClassFilesDir() {
File destdir = new File(CLASS_FILES_DIR, getClass().getSimpleName());
destdir.mkdirs();
return destdir;
}
}

View File

@@ -0,0 +1,45 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorAddEntityTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(0, cd.getFrom().getEntities().size());
assertEquals(2, cd.getTo().getEntities().size());
assertEquals(1, cd.getTo().getEntities().get(0).getFields().size());
assertEquals(1, cd.getTo().getEntities().get(1).getFields().size());
assertEquals("i2", cd.getTo().getEntities().get(0).getFields().get(0).getName());
assertEquals("i3", cd.getTo().getEntities().get(1).getFields().get(0).getName());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass2");
mb.getOrCreateEntity("TestClass2").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass3");
mb.getOrCreateEntity("TestClass3").getOrCreateField("i3", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorAddFieldTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(0, cd.getFrom().getEntities().get(0).getFields().size());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(2, cd.getTo().getEntities().get(0).getFields().size());
assertEquals("i2", cd.getTo().getEntities().get(0).getFields().get(0).getName());
assertEquals("i3", cd.getTo().getEntities().get(0).getFields().get(1).getName());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i3", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,48 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import jef.model.constraints.ForeignKeyConstraint;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorAddForeignKeyTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(0, cd.getFrom().getEntities().get(0).getForeignKeys().size());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(1, cd.getTo().getEntities().get(0).getForeignKeys().size());
assertEquals("FK_TestClass_TestClass_i2", cd.getTo().getEntities().get(0).getForeignKeys().get(0).getName());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
var ent = mb.getOrCreateEntity("TestClass");
ent.getOrCreateField("i", int.class.getName())
.setNotNull(true);
ent.getOrCreateField("i2", int.class.getName())
.setNotNull(true);
ent.addForeignKey(new ForeignKeyConstraint(ent, List.of(ent.getField("i2")),
ent, List.of(ent.getField("i")),
ForeignKeyConstraint.Action.CASCADE, ForeignKeyConstraint.Action.CASCADE));
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorAddIndexTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(0, cd.getFrom().getEntities().get(0).getIndexes().size());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(1, cd.getTo().getEntities().get(0).getIndexes().size());
assertEquals("I_TestClass_i", cd.getTo().getEntities().get(0).getIndexes().get(0).getName());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName()).setIndex(true);
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorAddKeyTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(0, cd.getFrom().getEntities().get(0).getKeys().size());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(1, cd.getTo().getEntities().get(0).getKeys().size());
assertEquals("K_TestClass_i", cd.getTo().getEntities().get(0).getKeys().get(0).getName());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName()).setKey(true);
return mb;
}
}

View File

@@ -0,0 +1,48 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import jef.model.constraints.PrimaryKeyConstraint;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
class ModelChangeDetectorAddPrimaryKeyTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertNull(cd.getFrom().getEntities().get(0).getPrimaryKey());
assertEquals(1, cd.getTo().getEntities().size());
assertNotNull(cd.getTo().getEntities().get(0).getPrimaryKey());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").setPrimaryKey(new PrimaryKeyConstraint(mb.getOrCreateEntity("TestClass"),
List.of(mb.getOrCreateEntity("TestClass").getField("i"))));
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorAddUniqueTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(0, cd.getFrom().getEntities().get(0).getUniqueKeys().size());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(1, cd.getTo().getEntities().get(0).getUniqueKeys().size());
assertEquals("U_TestClass_i", cd.getTo().getEntities().get(0).getUniqueKeys().get(0).getName());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName()).setUnique(true);
return mb;
}
}

View File

@@ -0,0 +1,45 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorDropEntityTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(2, cd.getFrom().getEntities().size());
assertEquals(1, cd.getFrom().getEntities().get(0).getFields().size());
assertEquals(1, cd.getFrom().getEntities().get(1).getFields().size());
assertEquals("i2", cd.getFrom().getEntities().get(0).getFields().get(0).getName());
assertEquals("i3", cd.getFrom().getEntities().get(1).getFields().get(0).getName());
assertEquals(0, cd.getTo().getEntities().size());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass2");
mb.getOrCreateEntity("TestClass2").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass3");
mb.getOrCreateEntity("TestClass3").getOrCreateField("i3", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorDropFieldTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(2, cd.getFrom().getEntities().get(0).getFields().size());
assertEquals("i2", cd.getFrom().getEntities().get(0).getFields().get(0).getName());
assertEquals("i3", cd.getFrom().getEntities().get(0).getFields().get(1).getName());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(0, cd.getTo().getEntities().get(0).getFields().size());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i3", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,48 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import jef.model.constraints.ForeignKeyConstraint;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorDropForeignKeyTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(1, cd.getFrom().getEntities().get(0).getForeignKeys().size());
assertEquals("FK_TestClass_TestClass_i2", cd.getFrom().getEntities().get(0).getForeignKeys().get(0).getName());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(0, cd.getTo().getEntities().get(0).getForeignKeys().size());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
var ent = mb.getOrCreateEntity("TestClass");
ent.getOrCreateField("i", int.class.getName())
.setNotNull(true);
ent.getOrCreateField("i2", int.class.getName())
.setNotNull(true);
ent.addForeignKey(new ForeignKeyConstraint(ent, List.of(ent.getField("i2")),
ent, List.of(ent.getField("i")),
ForeignKeyConstraint.Action.CASCADE, ForeignKeyConstraint.Action.CASCADE));
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorDropIndexTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(1, cd.getFrom().getEntities().get(0).getIndexes().size());
assertEquals("I_TestClass_i", cd.getFrom().getEntities().get(0).getIndexes().get(0).getName());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(0, cd.getTo().getEntities().get(0).getIndexes().size());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName()).setIndex(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorDropKeyTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(1, cd.getFrom().getEntities().get(0).getKeys().size());
assertEquals("K_TestClass_i", cd.getFrom().getEntities().get(0).getKeys().get(0).getName());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(0, cd.getTo().getEntities().get(0).getKeys().size());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName()).setKey(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,48 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import jef.model.constraints.PrimaryKeyConstraint;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
class ModelChangeDetectorDropPrimaryKeyTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertNotNull(cd.getFrom().getEntities().get(0).getPrimaryKey());
assertEquals(1, cd.getTo().getEntities().size());
assertNull(cd.getTo().getEntities().get(0).getPrimaryKey());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").setPrimaryKey(new PrimaryKeyConstraint(mb.getOrCreateEntity("TestClass"),
List.of(mb.getOrCreateEntity("TestClass").getField("i"))));
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,43 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ModelChangeDetectorDropUniqueTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertEquals(1, cd.getFrom().getEntities().size());
assertEquals(1, cd.getFrom().getEntities().get(0).getUniqueKeys().size());
assertEquals("U_TestClass_i", cd.getFrom().getEntities().get(0).getUniqueKeys().get(0).getName());
assertEquals(1, cd.getTo().getEntities().size());
assertEquals(0, cd.getTo().getEntities().get(0).getUniqueKeys().size());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName()).setUnique(true);
return mb;
}
private ModelBuilder createTo() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
}

View File

@@ -0,0 +1,34 @@
package jef.model.migration.creator;
import jef.model.ModelBuilder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ModelChangeDetectorNoChangeTest {
@Test
public void test() {
var from = createFrom();
var to = createTo();
var cd = new ModelChangeDetector(from, to).detect();
assertTrue(cd.getFrom().getEntities().isEmpty());
assertTrue(cd.getTo().getEntities().isEmpty());
}
private ModelBuilder createFrom() {
var mb = new ModelBuilder();
mb.getOrCreateEntity("TestClass");
mb.getOrCreateEntity("TestClass").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass2");
mb.getOrCreateEntity("TestClass2").getOrCreateField("i", int.class.getName())
.setNotNull(true);
mb.getOrCreateEntity("TestClass2").getOrCreateField("i2", int.class.getName())
.setNotNull(true);
return mb;
}
private ModelBuilder createTo() {
return createFrom();
}
}