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:
David Montag
2011-03-21 16:56:05 -07:00
parent d0ff6c8be2
commit 4fc038eab2
8 changed files with 60 additions and 18 deletions

View File

@@ -52,5 +52,5 @@ public @interface RelatedToVia {
/**
* @return target relationship entity class
*/
Class<? extends RelationshipBacked> elementClass() default RelationshipBacked.class;
Class<? extends RelationshipBacked> elementClass();
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();
}
}