GH-2788 - Fix implementation class detection for Node annotated interfaces.
Closes #2788
This commit is contained in:
@@ -289,7 +289,8 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
}
|
||||
|
||||
// determine super class to create the node hierarchy
|
||||
Class<? super T> superclass = typeInformation.getType().getSuperclass();
|
||||
Class<T> type = typeInformation.getType();
|
||||
Class<? super T> superclass = type.getSuperclass();
|
||||
|
||||
if (isValidParentNode(superclass)) {
|
||||
synchronized (this) {
|
||||
@@ -303,6 +304,17 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
}
|
||||
}
|
||||
|
||||
for (Class<?> typeInterface : type.getInterfaces()) {
|
||||
if (isValidEntityInterface(typeInterface)) {
|
||||
super.setStrict(false);
|
||||
Neo4jPersistentEntity<?> parentNodeDescription = getPersistentEntity(typeInterface);
|
||||
if (parentNodeDescription != null) {
|
||||
parentNodeDescription.addChildNodeDescription(newEntity);
|
||||
}
|
||||
this.setStrict(strict);
|
||||
}
|
||||
}
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
@@ -316,6 +328,10 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
|
||||
parentClass.isAnnotationPresent(Node.class);
|
||||
}
|
||||
|
||||
private static boolean isValidEntityInterface(Class<?> typeInterface) {
|
||||
return typeInterface.isAnnotationPresent(Node.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(org.springframework.data.mapping.model.Property, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
|
||||
|
||||
@@ -333,6 +333,44 @@ public class InheritanceMappingIT {
|
||||
}
|
||||
}
|
||||
|
||||
@Test // GH-2788
|
||||
void detectPropertiesAndRelationshipsOfImplementingEntities(@Autowired Neo4jTemplate template) {
|
||||
String id;
|
||||
try (Session session = driver.session(bookmarkCapture.createSessionConfig()); Transaction transaction = session.beginTransaction()) {
|
||||
id = transaction.run("" +
|
||||
"CREATE (e:`GH-2788-Entity`) " +
|
||||
"CREATE (e)-[:RELATED_TO]-> (a:`GH-2788-Interface`:`GH-2788-A` {name:'A'}) " +
|
||||
"CREATE (e)-[:RELATED_TO]-> (b:`GH-2788-Interface`:`GH-2788-B` {name:'B'}) " +
|
||||
"CREATE (a)-[:RELATED_TO]-> (:`Gh2788ArelatedEntity`) " +
|
||||
"CREATE (b)-[:RELATED_TO]-> (:`Gh2788BrelatedEntity`) " +
|
||||
"RETURN elementId(e)")
|
||||
.single().get(0).asString();
|
||||
transaction.commit();
|
||||
bookmarkCapture.seedWith(session.lastBookmarks());
|
||||
}
|
||||
|
||||
Optional<Inheritance.Gh2788Entity> gh2788Entity = transactionTemplate.execute(tx ->
|
||||
template.findById(id, Inheritance.Gh2788Entity.class));
|
||||
|
||||
assertThat(gh2788Entity).hasValueSatisfying(v -> {
|
||||
List<Inheritance.Gh2788Interface> relatedTo = v.relatedTo;
|
||||
assertThat(relatedTo).allSatisfy(relatedElement -> {
|
||||
if (relatedElement instanceof Inheritance.Gh2788A relatedAelement) {
|
||||
assertThat(relatedAelement.name).isEqualTo("A");
|
||||
assertThat(relatedAelement.relatedTo)
|
||||
.hasSize(1)
|
||||
.hasOnlyElementsOfType(Inheritance.Gh2788ArelatedEntity.class);
|
||||
} else if (relatedElement instanceof Inheritance.Gh2788B relatedBelement) {
|
||||
assertThat(relatedBelement.name).isEqualTo("B");
|
||||
assertThat(relatedBelement.relatedTo)
|
||||
.hasSize(1)
|
||||
.hasOnlyElementsOfType(Inheritance.Gh2788BrelatedEntity.class);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-2262
|
||||
void shouldMatchPolymorphicClassesWhenFetchedById(@Autowired DivisionRepository repository) {
|
||||
|
||||
|
||||
@@ -957,5 +957,84 @@ public class Inheritance {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity that has an interface-based relationship.
|
||||
* For testing that the properties and relationships of the implementing classes will also get fetched.
|
||||
*/
|
||||
@Node("GH-2788-Entity")
|
||||
public static class Gh2788Entity {
|
||||
@Id @GeneratedValue public String id;
|
||||
public List<Gh2788Interface> relatedTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for relationship
|
||||
*/
|
||||
@Node("GH-2788-Interface")
|
||||
public interface Gh2788Interface {
|
||||
String getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* First implementation
|
||||
*/
|
||||
@Node("GH-2788-A")
|
||||
public static class Gh2788A implements Gh2788Interface {
|
||||
@Id @GeneratedValue String id;
|
||||
|
||||
public final String name;
|
||||
public final String aValue;
|
||||
public final List<Gh2788ArelatedEntity> relatedTo;
|
||||
|
||||
public Gh2788A(String name, String aValue, List<Gh2788ArelatedEntity> relatedTo) {
|
||||
this.name = name;
|
||||
this.aValue = aValue;
|
||||
this.relatedTo = relatedTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Related entity for first implementation
|
||||
*/
|
||||
@Node
|
||||
public static class Gh2788ArelatedEntity {
|
||||
@Id @GeneratedValue String id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Second implementation
|
||||
*/
|
||||
@Node("GH-2788-B")
|
||||
public static class Gh2788B implements Gh2788Interface {
|
||||
@Id @GeneratedValue String id;
|
||||
|
||||
public final String name;
|
||||
public final String bValue;
|
||||
public final List<Gh2788BrelatedEntity> relatedTo;
|
||||
|
||||
public Gh2788B(String name, String bValue, List<Gh2788BrelatedEntity> relatedTo) {
|
||||
this.name = name;
|
||||
this.bValue = bValue;
|
||||
this.relatedTo = relatedTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Related entity for second implementation
|
||||
*/
|
||||
@Node
|
||||
public static class Gh2788BrelatedEntity {
|
||||
@Id @GeneratedValue String id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user