fix issues with foreign key exposure and add some tests for it
This commit is contained in:
@@ -5,10 +5,7 @@ import jef.model.annotations.ForeignKey;
|
|||||||
import jef.serializable.SerializableObject;
|
import jef.serializable.SerializableObject;
|
||||||
import jef.util.Util;
|
import jef.util.Util;
|
||||||
|
|
||||||
import java.util.Locale;
|
class ForeignKeyExposeInitializer {
|
||||||
|
|
||||||
class ForeignKeyExposeInitializer {//TODO testcases for this class
|
|
||||||
|
|
||||||
static void initForeignKeyExposures(ModelBuilder mb) {
|
static void initForeignKeyExposures(ModelBuilder mb) {
|
||||||
var entities = mb.entities();
|
var entities = mb.entities();
|
||||||
for (int i = 0; i < entities.size(); i++) {
|
for (int i = 0; i < entities.size(); i++) {
|
||||||
@@ -29,43 +26,29 @@ class ForeignKeyExposeInitializer {//TODO testcases for this class
|
|||||||
}
|
}
|
||||||
//match with name in annotation
|
//match with name in annotation
|
||||||
var anno = e.getField().getField().getAnnotation(ForeignKey.class);
|
var anno = e.getField().getField().getAnnotation(ForeignKey.class);
|
||||||
if (anno != null) {
|
if (anno == null) {
|
||||||
if (anno.entity() != SerializableObject.class) { // not a foreign key exposure
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
if (anno.entity() != SerializableObject.class) { // not a foreign key exposure, ignore
|
||||||
var fieldname = anno.getterOrField(); //TODO test
|
return false;
|
||||||
|
}
|
||||||
|
var fieldname = anno.getterOrField();
|
||||||
|
|
||||||
//if getter, extract getter field
|
//if getter, extract getter field
|
||||||
var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField())); //TODO test
|
var method = Util.tryGet(() -> entityBuilder.type().orElseThrow().getMethod(anno.getterOrField()));
|
||||||
if (method.isPresent()) {
|
if (method.isPresent()) {
|
||||||
var res = new OptimizedAsmParser(method.get()).parse();
|
var res = new OptimizedAsmParser(method.get()).parse();
|
||||||
fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName();
|
fieldname = res.getAccessedFields().stream().findFirst().orElseThrow().getName();
|
||||||
}
|
|
||||||
return f.getField().getName().equals(fieldname);
|
|
||||||
}
|
}
|
||||||
//only allow fields with same prefix
|
if (!SerializableObject.class.isAssignableFrom(entityBuilder.getEntity().getField(fieldname).getType())) {
|
||||||
if (!e.getField().getName().startsWith(f.getField().getName())) {
|
throw new ModelException("The getter/field in " + entityBuilder.className() + "::" + f.getField().getField().getName() + " is not a " + SerializableObject.class.getSimpleName() + " (via @ForeignKey in TestClass::testFk)");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
//match id field which ends in Id or Fk
|
return f.getField().getName().equals(fieldname);
|
||||||
if (e.getField().getName().equals(f.getField().getName() + "Id") //TODO test
|
})
|
||||||
|| e.getField().getName().equals(f.getField().getName() + "ID") //TODO test
|
.toList();
|
||||||
|| e.getField().getName().equals(f.getField().getName() + "Fk") //TODO test
|
|
||||||
|| e.getField().getName().equals(f.getField().getName() + "FK")) { //TODO test
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//match id field which ends in the name of the primary key field of the referenced entity
|
|
||||||
var other = mb.getEntity(f.getField().getTypeName()); //TODO test
|
|
||||||
if (other == null || other.getPrimaryKey() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//assumption: initializing from models only has primary keys with 1 field (no composite keys)
|
|
||||||
var x = other.getPrimaryKey().getFields().get(0).getName(); //TODO test
|
|
||||||
x = x.substring(0, 1).toUpperCase(Locale.ROOT) + x.substring(1);
|
|
||||||
return e.getField().getName().equals(f.getField().getName() + x);
|
|
||||||
}).toList();
|
|
||||||
idFields.forEach(e -> {
|
idFields.forEach(e -> {
|
||||||
e.getField().setExposingForeignKeyOf(f.getField());
|
e.getField().setExposingForeignKeyOf(f.getField());
|
||||||
|
e.getField().setDatabaseField(idFields.stream().findFirst().orElseThrow() == e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,12 +147,20 @@ class ForeignKeyInitializer {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
//find objectId in other entity (1 to 1 relation)
|
//find objectId in other entity (1 to 1 relation)
|
||||||
|
//search for exposing foreign key field first
|
||||||
|
var entityIdField = entity.getFields().stream()
|
||||||
|
.filter(oef -> oef.getExposingForeignKeyOf() != null && oef.getExposingForeignKeyOf().getField().equals(f))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
//search for field with name object field name + primary field name
|
||||||
var idFieldName = f.getName()
|
var idFieldName = f.getName()
|
||||||
+ otherPrimaryField.getName().substring(0, 1).toUpperCase(Locale.ROOT)
|
+ otherPrimaryField.getName().substring(0, 1).toUpperCase(Locale.ROOT)
|
||||||
+ otherPrimaryField.getName().substring(1);
|
+ otherPrimaryField.getName().substring(1);
|
||||||
var entityIdField = entity.getFields().stream().filter(oef -> oef.getName().equals(idFieldName)).findFirst();
|
if (!entityIdField.isPresent()) {
|
||||||
|
entityIdField = entity.getFields().stream().filter(oef -> oef.getName().equals(idFieldName)).findFirst();
|
||||||
|
}
|
||||||
if (entityIdField.isPresent()) {
|
if (entityIdField.isPresent()) {
|
||||||
return new FieldSearchResult(entityIdField.get(), entityIdField.get().getField().getAnnotationsByType(ForeignKey.class).length == 0);
|
return new FieldSearchResult(entityIdField.get(), true);//entityIdField.get().getField().getAnnotationsByType(ForeignKey.class).length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var field = new DbField<>(entity, otherPrimaryField.getType(), null, idFieldName);
|
var field = new DbField<>(entity, otherPrimaryField.getType(), null, idFieldName);
|
||||||
|
|||||||
@@ -28,15 +28,10 @@ class ForeignKeyProcessorExposeTest {
|
|||||||
|
|
||||||
//fields ------------------------
|
//fields ------------------------
|
||||||
//TestClass
|
//TestClass
|
||||||
assertEquals(8, mb.getEntity(TestClass.class).getFields().size());
|
assertEquals(3, mb.getEntity(TestClass.class).getFields().size());
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("d") && e.isModelField() && e.isDatabaseField()).count());
|
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("f") && e.isModelField() && e.isDatabaseField()).count());
|
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("l") && e.isModelField() && e.isDatabaseField()).count());
|
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("o") && e.isModelField() && e.isDatabaseField()).count());
|
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("test") && e.isModelField() && !e.isDatabaseField()).count());
|
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("test") && e.isModelField() && !e.isDatabaseField()).count());
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testFk") && e.isModelField() && e.isDatabaseField()).count());
|
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField") && e.isModelField() && e.isDatabaseField()).count());
|
||||||
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testI") && !e.isModelField() && e.isDatabaseField()).count());
|
|
||||||
|
|
||||||
//TestClass2
|
//TestClass2
|
||||||
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
|
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
|
||||||
@@ -47,10 +42,13 @@ class ForeignKeyProcessorExposeTest {
|
|||||||
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
|
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
|
||||||
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity());
|
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity());
|
||||||
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedEntity());
|
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedEntity());
|
||||||
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testI")).toList(),
|
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField")).toList(),
|
||||||
mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
|
mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
|
||||||
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
|
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
|
||||||
// /keys ------------------------
|
// /keys ------------------------
|
||||||
|
|
||||||
|
//exposing fields
|
||||||
|
assertEquals(mb.getEntity(TestClass.class).getField("test"), mb.getEntity(TestClass.class).getField("testField").getExposingForeignKeyOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Ctx extends DbContext {
|
public static class Ctx extends DbContext {
|
||||||
@@ -63,13 +61,9 @@ class ForeignKeyProcessorExposeTest {
|
|||||||
@Getter
|
@Getter
|
||||||
public static class TestClass extends SerializableObject {
|
public static class TestClass extends SerializableObject {
|
||||||
public int i = 1;
|
public int i = 1;
|
||||||
public Object o = new Object();
|
|
||||||
public double d;
|
|
||||||
public float f;
|
|
||||||
public long l;
|
|
||||||
private TestClass2 test;
|
private TestClass2 test;
|
||||||
@ForeignKey(getterOrField = "test")
|
@ForeignKey(getterOrField = "test")
|
||||||
public int testFk;
|
public int testField;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package jef.model.annotations.processors;
|
||||||
|
|
||||||
|
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.serializable.SerializableObject;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class ForeignKeyProcessorExposeViaMethodTest {
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
var mb = ModelBuilder.from(Ctx.class);
|
||||||
|
|
||||||
|
assertEquals(2, mb.getEntities().size());
|
||||||
|
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)));
|
||||||
|
assertTrue(mb.getEntity(TestClass2.class).getForeignKeys().stream().allMatch(e -> e.getEntity() == mb.getEntity(TestClass2.class)));
|
||||||
|
|
||||||
|
//fields ------------------------
|
||||||
|
//TestClass
|
||||||
|
assertEquals(3, mb.getEntity(TestClass.class).getFields().size());
|
||||||
|
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
||||||
|
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("test") && e.isModelField() && !e.isDatabaseField()).count());
|
||||||
|
assertEquals(1, mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField") && e.isModelField() && e.isDatabaseField()).count());
|
||||||
|
|
||||||
|
//TestClass2
|
||||||
|
assertEquals(1, mb.getEntity(TestClass2.class).getFields().size());
|
||||||
|
assertEquals(1, mb.getEntity(TestClass2.class).getFields().stream().filter(e -> e.getName().equals("i") && e.isModelField() && e.isDatabaseField()).count());
|
||||||
|
// /fields ------------------------
|
||||||
|
|
||||||
|
//keys ------------------------
|
||||||
|
assertEquals(1, mb.getEntity(TestClass.class).getForeignKeys().size());
|
||||||
|
assertEquals(mb.getEntity(TestClass.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getEntity());
|
||||||
|
assertEquals(mb.getEntity(TestClass2.class), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedEntity());
|
||||||
|
assertEquals(mb.getEntity(TestClass.class).getFields().stream().filter(e -> e.getName().equals("testField")).toList(),
|
||||||
|
mb.getEntity(TestClass.class).getForeignKeys().get(0).getFields());
|
||||||
|
assertEquals(mb.getEntity(TestClass2.class).getPrimaryKey().getFields(), mb.getEntity(TestClass.class).getForeignKeys().get(0).getReferencedFields());
|
||||||
|
// /keys ------------------------
|
||||||
|
|
||||||
|
//exposing fields
|
||||||
|
assertEquals(mb.getEntity(TestClass.class).getField("test"), mb.getEntity(TestClass.class).getField("testField").getExposingForeignKeyOf());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Ctx extends DbContext {
|
||||||
|
@Clazz(TestClass.class)
|
||||||
|
private DbSet<TestClass> objects1;
|
||||||
|
@Clazz(TestClass2.class)
|
||||||
|
private DbSet<TestClass2> objects2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class TestClass extends SerializableObject {
|
||||||
|
public int i = 1;
|
||||||
|
private TestClass2 test;
|
||||||
|
@ForeignKey(getterOrField = "getTest")
|
||||||
|
public int testField;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class TestClass2 extends SerializableObject {
|
||||||
|
@Id
|
||||||
|
public int i = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package jef.model.annotations.processors;
|
||||||
|
|
||||||
|
import jef.DbSet;
|
||||||
|
import jef.model.DbContext;
|
||||||
|
import jef.model.ModelBuilder;
|
||||||
|
import jef.model.ModelException;
|
||||||
|
import jef.model.annotations.Clazz;
|
||||||
|
import jef.model.annotations.ForeignKey;
|
||||||
|
import jef.model.annotations.Id;
|
||||||
|
import jef.serializable.SerializableObject;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class ForeignKeyProcessorInvalidFieldTypeTest {
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
assertEquals("The getter/field in TestClass::test is not a SerializableObject (via @ForeignKey in TestClass::testFk)",
|
||||||
|
assertThrowsExactly(ModelException.class, () -> ModelBuilder.from(Ctx.class)).getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Ctx extends DbContext {
|
||||||
|
@Clazz(TestClass.class)
|
||||||
|
private DbSet<TestClass> objects1;
|
||||||
|
@Clazz(TestClass2.class)
|
||||||
|
private DbSet<TestClass2> objects2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class TestClass extends SerializableObject {
|
||||||
|
public int i = 1;
|
||||||
|
public Object o = new Object();
|
||||||
|
public double d;
|
||||||
|
public float f;
|
||||||
|
public long l;
|
||||||
|
private TestClass2 test;
|
||||||
|
@ForeignKey(getterOrField = "l")
|
||||||
|
public int testFk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public static class TestClass2 extends SerializableObject {
|
||||||
|
@Id
|
||||||
|
public int i = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
class ForeignKeyProcessorInvalidFieldOrGetterTest {
|
class ForeignKeyProcessorNonExistingFieldOrGetterTest {
|
||||||
@Test
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
assertEquals("Cannot find getter/field TestClass::x (via @ForeignKey in TestClass::ref)",
|
assertEquals("Cannot find getter/field TestClass::x (via @ForeignKey in TestClass::ref)",
|
||||||
Reference in New Issue
Block a user