diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java index 318bc997d..1d73667f8 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java @@ -52,5 +52,5 @@ public @interface RelatedToVia { /** * @return target relationship entity class */ - Class elementClass() default RelationshipBacked.class; + Class elementClass(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java index ce2615b71..2c9e19766 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java @@ -43,8 +43,8 @@ public abstract class DefaultEntityState, 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 diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java index e2996d827..bc48e4bae 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java @@ -119,11 +119,11 @@ public abstract class DelegatingFieldAccessorFactory implements FieldAccessor - private final Map, FieldAccessorFactoryProviders> acessorFactoryProviderCache = new HashMap, FieldAccessorFactoryProviders>(); + private final Map, FieldAccessorFactoryProviders> accessorFactoryProviderCache = new HashMap, FieldAccessorFactoryProviders>(); public FieldAccessorFactoryProviders accessorFactoriesFor(final Class type) { synchronized (this) { - final FieldAccessorFactoryProviders fieldAccessorFactoryProviders = acessorFactoryProviderCache.get(type); + final FieldAccessorFactoryProviders fieldAccessorFactoryProviders = accessorFactoryProviderCache.get(type); if (fieldAccessorFactoryProviders != null) return fieldAccessorFactoryProviders; final FieldAccessorFactoryProviders newFieldAccessorFactories = new FieldAccessorFactoryProviders(type); ReflectionUtils.doWithFields(type, new ReflectionUtils.FieldCallback() { @@ -133,7 +133,7 @@ public abstract class DelegatingFieldAccessorFactory implements FieldAccessor newFieldAccessorFactories.add(field, factory, listenerFactories); } }); - acessorFactoryProviderCache.put(type, newFieldAccessorFactories); + accessorFactoryProviderCache.put(type, newFieldAccessorFactories); return newFieldAccessorFactories; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java index f3303f71c..6955d2f39 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java @@ -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; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java index 1f6d6c50c..96552a528 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java @@ -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 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) { diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidOneToNEntity.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidOneToNEntity.java new file mode 100644 index 000000000..5585d7181 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidOneToNEntity.java @@ -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 others; +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidReadOnlyOneToNEntity.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidReadOnlyOneToNEntity.java new file mode 100644 index 000000000..e1b2993dc --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidReadOnlyOneToNEntity.java @@ -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 others; +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java index 6bad30933..917a732cc 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java @@ -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(); + } }