Throw exception with descriptive message when trying to instantiate an entity class with a misconfigured @RelatedTo annotation missing its elementClass attribute for a 1:N relationship.
This commit is contained in:
@@ -52,5 +52,5 @@ public @interface RelatedToVia {
|
||||
/**
|
||||
* @return target relationship entity class
|
||||
*/
|
||||
Class<? extends RelationshipBacked> elementClass() default RelationshipBacked.class;
|
||||
Class<? extends RelationshipBacked> elementClass();
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ public abstract class DefaultEntityState<ENTITY extends GraphBacked<STATE>, STAT
|
||||
this.entity = entity;
|
||||
this.type = type;
|
||||
fieldAccessorFactoryProviders = delegatingFieldAccessorFactory.accessorFactoriesFor(type);
|
||||
this.fieldAccessors= fieldAccessorFactoryProviders.getFieldAccessors();
|
||||
this.fieldAccessorListeners= fieldAccessorFactoryProviders.getFieldAccessListeners();
|
||||
this.fieldAccessors = fieldAccessorFactoryProviders.getFieldAccessors();
|
||||
this.fieldAccessorListeners = fieldAccessorFactoryProviders.getFieldAccessListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -119,11 +119,11 @@ public abstract class DelegatingFieldAccessorFactory<T> implements FieldAccessor
|
||||
|
||||
|
||||
|
||||
private final Map<Class<?>, FieldAccessorFactoryProviders> acessorFactoryProviderCache = new HashMap<Class<?>, FieldAccessorFactoryProviders>();
|
||||
private final Map<Class<?>, FieldAccessorFactoryProviders> accessorFactoryProviderCache = new HashMap<Class<?>, FieldAccessorFactoryProviders>();
|
||||
|
||||
public <T> FieldAccessorFactoryProviders<T> accessorFactoriesFor(final Class<T> type) {
|
||||
synchronized (this) {
|
||||
final FieldAccessorFactoryProviders<T> fieldAccessorFactoryProviders = acessorFactoryProviderCache.get(type);
|
||||
final FieldAccessorFactoryProviders<T> fieldAccessorFactoryProviders = accessorFactoryProviderCache.get(type);
|
||||
if (fieldAccessorFactoryProviders != null) return fieldAccessorFactoryProviders;
|
||||
final FieldAccessorFactoryProviders<T> newFieldAccessorFactories = new FieldAccessorFactoryProviders<T>(type);
|
||||
ReflectionUtils.doWithFields(type, new ReflectionUtils.FieldCallback() {
|
||||
@@ -133,7 +133,7 @@ public abstract class DelegatingFieldAccessorFactory<T> implements FieldAccessor
|
||||
newFieldAccessorFactories.add(field, factory, listenerFactories);
|
||||
}
|
||||
});
|
||||
acessorFactoryProviderCache.put(type, newFieldAccessorFactories);
|
||||
accessorFactoryProviderCache.put(type, newFieldAccessorFactories);
|
||||
return newFieldAccessorFactories;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.graph.neo4j.fieldaccess;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
|
||||
@@ -59,7 +60,7 @@ abstract class NodeRelationshipFieldAccessorFactory implements FieldAccessorFact
|
||||
}
|
||||
|
||||
protected DynamicRelationshipType typeFrom(Field field, RelatedTo relAnnotation) {
|
||||
return "".equals(relAnnotation.type()) ? typeFrom(field) : typeFrom(relAnnotation);
|
||||
return "".equals(relAnnotation.type()) ? typeFrom(field) : typeFrom(relAnnotation);
|
||||
}
|
||||
|
||||
protected RelatedTo getRelationshipAnnotation(Field field) {
|
||||
@@ -68,6 +69,11 @@ abstract class NodeRelationshipFieldAccessorFactory implements FieldAccessorFact
|
||||
|
||||
protected boolean hasValidRelationshipAnnotation(Field field) {
|
||||
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
|
||||
return relAnnotation != null && !relAnnotation.elementClass().equals(NodeBacked.class);
|
||||
if (relAnnotation == null) return false;
|
||||
boolean hasElementClass = !relAnnotation.elementClass().equals(NodeBacked.class);
|
||||
if (!hasElementClass) throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Missing mandatory attribute @RelatedTo.elementClass for one-to-N relationship field %s in class: %s",
|
||||
field.getName(), field.getDeclaringClass().getName()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,15 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
|
||||
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), graphDatabaseContext);
|
||||
}
|
||||
|
||||
private boolean hasValidRelationshipAnnotation(final Field f) {
|
||||
final RelatedToVia relEntityAnnotation = getRelationshipAnnotation(f);
|
||||
return relEntityAnnotation != null && !RelationshipBacked.class.equals(relEntityAnnotation.elementClass());
|
||||
private boolean hasValidRelationshipAnnotation(final Field field) {
|
||||
final RelatedToVia relEntityAnnotation = getRelationshipAnnotation(field);
|
||||
if (relEntityAnnotation == null) return false;
|
||||
Class<? extends RelationshipBacked> elementClass = relEntityAnnotation.elementClass();
|
||||
boolean hasElementClass = elementClass != null && !RelationshipBacked.class.equals(elementClass);
|
||||
if (!hasElementClass) throw new InvalidDataAccessApiUsageException(String.format(
|
||||
"Missing mandatory attribute @RelatedTo.elementClass for one-to-N relationship field %s in class: %s",
|
||||
field.getName(), field.getDeclaringClass().getName()));
|
||||
return hasElementClass;
|
||||
}
|
||||
|
||||
private RelatedToVia getRelationshipAnnotation(final Field field) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.springframework.data.graph.neo4j;
|
||||
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@NodeEntity
|
||||
public class InvalidOneToNEntity {
|
||||
@RelatedTo
|
||||
private Collection<InvalidOneToNEntity> others;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.springframework.data.graph.neo4j;
|
||||
|
||||
import org.springframework.data.graph.annotation.NodeEntity;
|
||||
import org.springframework.data.graph.annotation.RelatedTo;
|
||||
|
||||
@NodeEntity
|
||||
public class InvalidReadOnlyOneToNEntity {
|
||||
@RelatedTo
|
||||
private Iterable<InvalidReadOnlyOneToNEntity> others;
|
||||
}
|
||||
@@ -2,19 +2,14 @@ package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.graph.neo4j.Friendship;
|
||||
import org.springframework.data.graph.neo4j.Person;
|
||||
import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
import org.springframework.data.graph.neo4j.Personality;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.graph.neo4j.*;
|
||||
import org.springframework.data.graph.neo4j.finder.FinderFactory;
|
||||
import org.springframework.data.graph.neo4j.support.node.Neo4jHelper;
|
||||
|
||||
import org.springframework.test.annotation.Rollback;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -24,6 +19,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.data.graph.neo4j.Person.persistedPerson;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"})
|
||||
@@ -131,4 +127,16 @@ public class PropertyTest {
|
||||
Friendship f = p.knows(p2);
|
||||
assertEquals("Wrong ID.", (Long)f.getPersistentState().getId(), f.getRelationshipId());
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Transactional
|
||||
public void testFailFastOnMisconfiguredOneToNProperty() {
|
||||
new InvalidOneToNEntity();
|
||||
}
|
||||
|
||||
@Test(expected = InvalidDataAccessApiUsageException.class)
|
||||
@Transactional
|
||||
public void testFailFastOnMisconfiguredReadOnlyOneToNProperty() {
|
||||
new InvalidReadOnlyOneToNEntity();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user