converted usage of java.lang.reflect.Field to Neo4jPersistentProperty

This commit is contained in:
Michael Hunger
2011-09-27 01:01:09 +02:00
parent 2b31a6f3f4
commit 7d5d85a4d0
41 changed files with 524 additions and 418 deletions

View File

@@ -19,6 +19,8 @@ package org.springframework.data.neo4j.core;
import java.lang.reflect.Field;
import org.springframework.data.neo4j.fieldaccess.FieldAccessor;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
/**
* Interface for classes encapsulating and delegating read and write field access of an GraphBacked entity to a number of field accessors.
@@ -50,11 +52,13 @@ public interface EntityState<ENTITY extends GraphBacked<STATE>,STATE> {
boolean isWritable(Field field);
/**
*
* @param field
* @param newVal
* @return sets the value in the entity and/or the state
*/
Object setValue(Field field, Object newVal);
Object setValue(Neo4JPersistentProperty property, Object newVal);
/**
* callback for creating and initializing an initial state
@@ -67,4 +71,6 @@ public interface EntityState<ENTITY extends GraphBacked<STATE>,STATE> {
STATE getPersistentState();
ENTITY persist();
Neo4JPersistentEntity<ENTITY> getPersistentEntity();
}

View File

@@ -17,8 +17,7 @@
package org.springframework.data.neo4j.core;
import org.neo4j.graphdb.traversal.TraversalDescription;
import java.lang.reflect.Field;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
/**
* Interface for classes that build traversal descriptions. Those classes can be referred to by
@@ -31,9 +30,10 @@ import java.lang.reflect.Field;
public interface FieldTraversalDescriptionBuilder {
/**
* Builder method for traversal description.
*
* @param start the Entity that contains the field with the dynamic traversal. Used for the parametrization of the traversal description.
* @param field the concrete field that will provide the traversal. Used for the parametrization of the traversal description.
* @param property
* @return the TraversalDescription to apply on fieldaccess, the start node is the current entity node
*/
TraversalDescription build(NodeBacked start, Field field, String...params);
TraversalDescription build(NodeBacked start, Neo4JPersistentProperty property, String...params);
}

View File

@@ -19,9 +19,9 @@ package org.springframework.data.neo4j.fieldaccess;
import org.neo4j.graphdb.*;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
@@ -31,17 +31,17 @@ import java.util.Set;
*/
public abstract class AbstractNodeRelationshipFieldAccessor<ENTITY extends GraphBacked,STATE extends PropertyContainer,TARGET extends GraphBacked,TSTATE extends PropertyContainer> implements FieldAccessor<ENTITY> {
protected final RelationshipType type;
protected final Field field;
protected final Neo4JPersistentProperty property;
protected final Direction direction;
protected final Class<? extends TARGET> relatedType;
protected final GraphDatabaseContext graphDatabaseContext;
public AbstractNodeRelationshipFieldAccessor(Class<? extends TARGET> clazz, GraphDatabaseContext graphDatabaseContext, Direction direction, RelationshipType type, Field field) {
public AbstractNodeRelationshipFieldAccessor(Class<? extends TARGET> clazz, GraphDatabaseContext graphDatabaseContext, Direction direction, RelationshipType type, Neo4JPersistentProperty property) {
this.relatedType = clazz;
this.graphDatabaseContext = graphDatabaseContext;
this.direction = direction;
this.type = type;
this.field = field;
this.property = property;
}
@Override
@@ -84,7 +84,7 @@ public abstract class AbstractNodeRelationshipFieldAccessor<ENTITY extends Graph
}
protected ManagedFieldAccessorSet<ENTITY,TARGET> createManagedSet(ENTITY entity, Set<TARGET> result) {
return new ManagedFieldAccessorSet<ENTITY,TARGET>(entity, result, field);
return new ManagedFieldAccessorSet<ENTITY,TARGET>(entity, result, property);
}
protected Set<TARGET> createEntitySetFromRelationshipEndNodes(ENTITY entity) {

View File

@@ -20,10 +20,8 @@ import org.neo4j.graphdb.PropertyContainer;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import java.lang.reflect.Field;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import scala.annotation.target.field;
/**
* @author Michael Hunger
@@ -40,35 +38,20 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
@Override
public boolean accept(final Field field) {
return isSerializableField(field) && isDeserializableField(field);
public boolean accept(final Neo4JPersistentProperty field) {
return field.isSerializableField(conversionService) && field.isDeserializableField(conversionService);
}
@Override
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Field field) {
return new ConvertingNodePropertyFieldAccessor(conversionService, DelegatingFieldAccessorFactory.getNeo4jPropertyName(field),field.getType());
}
private boolean isSerializableField(final Field field) {
return isSimpleValueField(field) && conversionService.canConvert(field.getType(), String.class);
}
private boolean isDeserializableField(final Field field) {
return isSimpleValueField(field) && conversionService.canConvert(String.class, field.getType());
}
private boolean isSimpleValueField(final Field field) {
final Class<?> type = field.getType();
if (Iterable.class.isAssignableFrom(type) || NodeBacked.class.isAssignableFrom(type) || RelationshipBacked.class.isAssignableFrom(type))
return false;
return true;
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Neo4JPersistentProperty property) {
return new ConvertingNodePropertyFieldAccessor(conversionService,property);
}
public static class ConvertingNodePropertyFieldAccessor extends PropertyFieldAccessorFactory.PropertyFieldAccessor {
private final ConversionService conversionService;
public ConvertingNodePropertyFieldAccessor(ConversionService conversionService, String propertyName, Class fieldType) {
super(conversionService,propertyName,fieldType);
public ConvertingNodePropertyFieldAccessor(ConversionService conversionService, Neo4JPersistentProperty property) {
super(conversionService, property);
this.conversionService = conversionService;
}

View File

@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import java.lang.reflect.Field;
import java.util.HashMap;
@@ -34,20 +35,20 @@ import java.util.Map;
public abstract class DefaultEntityState<ENTITY extends GraphBacked<STATE>, STATE> implements EntityState<ENTITY,STATE> {
protected final ENTITY entity;
protected final Class<? extends ENTITY> type;
private final Map<Field, FieldAccessor<ENTITY>> fieldAccessors = new HashMap<Field, FieldAccessor<ENTITY>>();
private final Map<Field,List<FieldAccessListener<ENTITY,?>>> fieldAccessorListeners = new HashMap<Field, List<FieldAccessListener<ENTITY, ?>>>();
private final Map<Neo4JPersistentProperty, FieldAccessor<ENTITY>> fieldAccessors = new HashMap<Neo4JPersistentProperty, FieldAccessor<ENTITY>>();
private final Map<Neo4JPersistentProperty,List<FieldAccessListener<ENTITY,?>>> fieldAccessorListeners = new HashMap<Neo4JPersistentProperty, List<FieldAccessListener<ENTITY, ?>>>();
private STATE state;
protected final static Log log= LogFactory.getLog(DefaultEntityState.class);
private final FieldAccessorFactoryProviders<ENTITY> fieldAccessorFactoryProviders;
private final Neo4JPersistentEntity<?> persistentEntity;
private final Neo4JPersistentEntity<ENTITY> persistentEntity;
public DefaultEntityState(final STATE underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4JPersistentEntity<?> persistentEntity) {
public DefaultEntityState(final STATE underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4JPersistentEntity<ENTITY> persistentEntity) {
this.state = underlyingState;
this.entity = entity;
this.type = type;
this.persistentEntity = persistentEntity;
if (delegatingFieldAccessorFactory!=null) {
fieldAccessorFactoryProviders = delegatingFieldAccessorFactory.accessorFactoriesFor(type);
fieldAccessorFactoryProviders = delegatingFieldAccessorFactory.accessorFactoriesFor(persistentEntity);
this.fieldAccessors.putAll(fieldAccessorFactoryProviders.getFieldAccessors());
this.fieldAccessorListeners.putAll(fieldAccessorFactoryProviders.getFieldAccessListeners());
} else {
@@ -78,38 +79,53 @@ public abstract class DefaultEntityState<ENTITY extends GraphBacked<STATE>, STAT
return state;
}
public Neo4JPersistentEntity<ENTITY> getPersistentEntity() {
return persistentEntity;
}
@Override
public boolean isWritable(Field field) {
final FieldAccessor<ENTITY> accessor = accessorFor(field);
final FieldAccessor<ENTITY> accessor = accessorFor(property(field));
if (accessor == null) return true;
return accessor.isWriteable(entity);
}
@Override
public Object getValue(final Field field) {
final FieldAccessor<ENTITY> accessor = accessorFor(field);
final FieldAccessor<ENTITY> accessor = accessorFor(property(field));
if (accessor == null) return null;
else return accessor.getValue(entity);
}
@Override
public Object setValue(final Field field, final Object newVal) {
final FieldAccessor<ENTITY> accessor = accessorFor(field);
return setValue(property(field),newVal);
}
@Override
public Object setValue(final Neo4JPersistentProperty property, final Object newVal) {
final FieldAccessor<ENTITY> accessor = accessorFor(property);
final Object result=accessor!=null ? accessor.setValue(entity, newVal) : newVal;
notifyListeners(field, result);
notifyListeners(property, result);
return result;
}
@Override
public Object getDefaultImplementation(Field field) {
final FieldAccessor<ENTITY> accessor = accessorFor(field);
final FieldAccessor<ENTITY> accessor = accessorFor(property(field));
if (accessor == null) return null;
else return accessor.getDefaultImplementation();
}
protected FieldAccessor<ENTITY> accessorFor(final Field field) {
return fieldAccessors.get(field);
protected Neo4JPersistentProperty property(Field field) {
return persistentEntity.getPersistentProperty(field.getName());
}
private void notifyListeners(final Field field, final Object result) {
protected FieldAccessor<ENTITY> accessorFor(final Neo4JPersistentProperty property) {
return fieldAccessors.get(property);
}
private void notifyListeners(final Neo4JPersistentProperty field, final Object result) {
if (!fieldAccessorListeners.containsKey(field) || fieldAccessorListeners.get(field) == null) return;
for (final FieldAccessListener<ENTITY, ?> listener : fieldAccessorListeners.get(field)) {
listener.valueChanged(entity, null, result); // todo oldValue
@@ -117,13 +133,12 @@ public abstract class DefaultEntityState<ENTITY extends GraphBacked<STATE>, STAT
}
protected Object getIdFromEntity() {
final Field idField = fieldAccessorFactoryProviders.getIdField();
if (idField==null) return null;
final Neo4JPersistentProperty idProperty = fieldAccessorFactoryProviders.getIdProperty();
if (idProperty==null) return null;
try {
idField.setAccessible(true);
return idField.get(entity);
return idProperty.getValue(entity);
} catch (IllegalAccessException e) {
log.warn("Error accessing id field "+idField);
log.warn("Error accessing id field "+idProperty);
return null;
}
}

View File

@@ -18,10 +18,13 @@ package org.springframework.data.neo4j.fieldaccess;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.data.util.TypeInformation;
import java.lang.reflect.Field;
import java.util.*;
@@ -49,62 +52,44 @@ public abstract class DelegatingFieldAccessorFactory<T> implements FieldAccessor
}
@Override
public boolean accept(final Field f) {
public boolean accept(final Neo4JPersistentProperty f) {
return true;
}
final Collection<FieldAccessorFactory<?>> fieldAccessorFactories = new ArrayList<FieldAccessorFactory<?>>();
final Collection<FieldAccessorListenerFactory<?>> fieldAccessorListenerFactories = new ArrayList<FieldAccessorListenerFactory<?>>();
public FieldAccessor forField(final Field field) {
final FieldAccessorFactory<?> factory = factoryForField(field);
return factory != null ? factory.forField(field) : null;
public FieldAccessor forField(final Neo4JPersistentProperty property) {
final FieldAccessorFactory<?> factory = factoryForField(property);
return factory != null ? factory.forField(property) : null;
}
private <E> FieldAccessorFactory<E> factoryForField(final Field field) {
if (isSyntheticField(field)) return null;
private <E> FieldAccessorFactory<E> factoryForField(final Neo4JPersistentProperty property) {
if (property.isSyntheticField()) return null;
for (final FieldAccessorFactory<?> fieldAccessorFactory : fieldAccessorFactories) {
if (fieldAccessorFactory.accept(field)) {
if (log.isInfoEnabled()) log.info("Factory " + fieldAccessorFactory + " used for field: " + field);
if (fieldAccessorFactory.accept(property)) {
if (log.isInfoEnabled()) log.info("Factory " + fieldAccessorFactory + " used for field: " + property);
return (FieldAccessorFactory<E>) fieldAccessorFactory;
}
}
if (log.isWarnEnabled()) log.warn("No FieldAccessor configured for field: " + field);
if (log.isWarnEnabled()) log.warn("No FieldAccessor configured for field: " + property);
return null;
}
private boolean isSyntheticField(final Field field) {
return field.getName().contains("$");
}
public static String getNeo4jPropertyName(final Field field) {
final Class<?> entityClass = field.getDeclaringClass();
if (useShortNames(entityClass)) return field.getName();
return String.format("%s.%s", entityClass.getSimpleName(), field.getName());
}
private static boolean useShortNames(final Class<?> entityClass) {
final NodeEntity graphEntity = entityClass.getAnnotation(NodeEntity.class);
if (graphEntity != null) return graphEntity.useShortNames();
final RelationshipEntity graphRelationship = entityClass.getAnnotation(RelationshipEntity.class);
if (graphRelationship != null) return graphRelationship.useShortNames();
return false;
}
public List<FieldAccessListener<T, ?>> listenersFor(final Field field) {
public List<FieldAccessListener<T, ?>> listenersFor(final Neo4JPersistentProperty property) {
final List<FieldAccessListener<T, ?>> result = new ArrayList<FieldAccessListener<T, ?>>();
final List<FieldAccessorListenerFactory<T>> fieldAccessListenerFactories = getFieldAccessListenerFactories(field);
final List<FieldAccessorListenerFactory<T>> fieldAccessListenerFactories = getFieldAccessListenerFactories(property);
for (final FieldAccessorListenerFactory<T> fieldAccessorListenerFactory : fieldAccessListenerFactories) {
final FieldAccessListener<T, ?> listener = fieldAccessorListenerFactory.forField(field);
final FieldAccessListener<T, ?> listener = fieldAccessorListenerFactory.forField(property);
result.add(listener);
}
return result;
}
private <E> List<FieldAccessorListenerFactory<E>> getFieldAccessListenerFactories(final Field field) {
private <E> List<FieldAccessorListenerFactory<E>> getFieldAccessListenerFactories(final Neo4JPersistentProperty property) {
final List<FieldAccessorListenerFactory<E>> result = new ArrayList<FieldAccessorListenerFactory<E>>();
for (final FieldAccessorListenerFactory<?> fieldAccessorListenerFactory : fieldAccessorListenerFactories) {
if (fieldAccessorListenerFactory.accept(field)) {
if (fieldAccessorListenerFactory.accept(property)) {
result.add((FieldAccessorListenerFactory<E>) fieldAccessorListenerFactory);
}
}
@@ -114,21 +99,32 @@ public abstract class DelegatingFieldAccessorFactory<T> implements FieldAccessor
private final Map<Class<?>, FieldAccessorFactoryProviders> accessorFactoryProviderCache = new HashMap<Class<?>, FieldAccessorFactoryProviders>();
private final Map<TypeInformation<?>, FieldAccessorFactoryProviders> accessorFactoryProviderCache = new HashMap<TypeInformation<?>, FieldAccessorFactoryProviders>();
public <T> FieldAccessorFactoryProviders<T> accessorFactoriesFor(final Class<T> type) {
public <T> FieldAccessorFactoryProviders<T> accessorFactoriesFor(final Neo4JPersistentEntity<?> type) {
synchronized (this) {
final FieldAccessorFactoryProviders<T> fieldAccessorFactoryProviders = accessorFactoryProviderCache.get(type);
final TypeInformation<?> typeInformation = type.getTypeInformation();
final FieldAccessorFactoryProviders<T> fieldAccessorFactoryProviders = accessorFactoryProviderCache.get(typeInformation);
if (fieldAccessorFactoryProviders != null) return fieldAccessorFactoryProviders;
final FieldAccessorFactoryProviders<T> newFieldAccessorFactories = new FieldAccessorFactoryProviders<T>(type);
ReflectionUtils.doWithFields(type, new ReflectionUtils.FieldCallback() {
public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
final FieldAccessorFactory<?> factory = factoryForField(field);
final List<FieldAccessorListenerFactory> listenerFactories = (List<FieldAccessorListenerFactory>) getFieldAccessListenerFactories(field);
newFieldAccessorFactories.add(field, factory, listenerFactories);
final FieldAccessorFactoryProviders<T> newFieldAccessorFactories = new FieldAccessorFactoryProviders<T>(typeInformation);
type.doWithProperties(new PropertyHandler<Neo4JPersistentProperty>() {
@Override
public void doWithPersistentProperty(Neo4JPersistentProperty property) {
final FieldAccessorFactory<?> factory = factoryForField(property);
final List<FieldAccessorListenerFactory> listenerFactories = (List<FieldAccessorListenerFactory>) getFieldAccessListenerFactories(property);
newFieldAccessorFactories.add(property, factory, listenerFactories);
}
});
accessorFactoryProviderCache.put(type, newFieldAccessorFactories);
type.doWithAssociations(new AssociationHandler<Neo4JPersistentProperty>() {
@Override
public void doWithAssociation(Association<Neo4JPersistentProperty> association) {
final Neo4JPersistentProperty property = association.getInverse();
final FieldAccessorFactory<?> factory = factoryForField(property);
final List<FieldAccessorListenerFactory> listenerFactories = (List<FieldAccessorListenerFactory>) getFieldAccessListenerFactories(property);
newFieldAccessorFactories.add(property, factory, listenerFactories);
}
});
accessorFactoryProviderCache.put(typeInformation, newFieldAccessorFactories);
return newFieldAccessorFactories;
}
}

View File

@@ -22,6 +22,8 @@ import org.neo4j.graphdb.Transaction;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import org.springframework.util.ObjectUtils;
@@ -42,8 +44,11 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, STATE> imple
protected final EntityState<ENTITY,STATE> delegate;
private final static Log log = LogFactory.getLog(DetachedEntityState.class);
private GraphDatabaseContext graphDatabaseContext;
private Neo4JPersistentEntity<ENTITY> persistentEntity;
public DetachedEntityState(final EntityState<ENTITY, STATE> delegate, GraphDatabaseContext graphDatabaseContext) {
this.delegate = delegate;
this.persistentEntity = delegate.getPersistentEntity();
this.graphDatabaseContext = graphDatabaseContext;
}
@@ -67,6 +72,11 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, STATE> imple
return delegate.getPersistentState();
}
@Override
public Neo4JPersistentEntity<ENTITY> getPersistentEntity() {
return persistentEntity;
}
@Override
public Object getValue(final Field field) {
if (isDetached()) {
@@ -124,7 +134,17 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, STATE> imple
}
@Override
public Object setValue(final Field field, final Object newVal) {
return setValue(property(field),newVal);
}
private Neo4JPersistentProperty property(Field field) {
return persistentEntity.getPersistentProperty(field.getName());
}
@Override
public Object setValue(final Neo4JPersistentProperty property, final Object newVal) {
if (isDetached()) {
final Field field = property.getField();
if (!isDirty(field) && isWritable(field)) {
Object existingValue;
if (hasPersistentState()) {
@@ -139,7 +159,7 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, STATE> imple
return newVal;
}
// flushDirty();
return delegate.setValue(field, newVal);
return delegate.setValue(property, newVal);
}
@Override
public Object getDefaultImplementation(Field field) {

View File

@@ -16,17 +16,14 @@
package org.springframework.data.neo4j.fieldaccess;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.DoReturn;
/**
@@ -42,22 +39,22 @@ public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFacto
}
@Override
public boolean accept(Field f) {
public boolean accept(Neo4JPersistentProperty f) {
return DynamicProperties.class.isAssignableFrom(f.getType());
}
@Override
public FieldAccessor<GraphBacked<PropertyContainer>> forField(Field field) {
public FieldAccessor<GraphBacked<PropertyContainer>> forField(Neo4JPersistentProperty field) {
return new DynamicPropertiesFieldAccessor(conversionService,
DelegatingFieldAccessorFactory.getNeo4jPropertyName(field), field);
field.getNeo4jPropertyName(), field);
}
public static class DynamicPropertiesFieldAccessor implements FieldAccessor<GraphBacked<PropertyContainer>> {
private final ConversionService conversionService;
private final String propertyNamePrefix;
private final Field field;
private final Neo4JPersistentProperty field;
public DynamicPropertiesFieldAccessor(ConversionService conversionService, String propertyName, Field field) {
public DynamicPropertiesFieldAccessor(ConversionService conversionService, String propertyName, Neo4JPersistentProperty field) {
this.conversionService = conversionService;
this.propertyNamePrefix = propertyName;
this.field = field;

View File

@@ -16,6 +16,8 @@
package org.springframework.data.neo4j.fieldaccess;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import java.lang.reflect.Field;
/**
@@ -27,14 +29,16 @@ import java.lang.reflect.Field;
*/
public interface FieldAccessorFactory<E> {
/**
*
* @param f field to check
* @return true if this factory is responsible for creating a accessor for this field
*/
boolean accept(Field f);
boolean accept(Neo4JPersistentProperty f);
/**
*
* @param f the field to create an accessor for
* @return a field accessor for the field or null if none can be created
*/
FieldAccessor<E> forField(Field f);
FieldAccessor<E> forField(Neo4JPersistentProperty f);
}

View File

@@ -16,6 +16,9 @@
package org.springframework.data.neo4j.fieldaccess;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.util.TypeInformation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
@@ -29,69 +32,69 @@ import java.util.Map;
public class FieldAccessorFactoryProviders<T> {
static class FieldAccessorFactoryProvider<E> {
private final Field field;
private final Neo4JPersistentProperty property;
private final FieldAccessorFactory<E> fieldAccessorFactory;
private final List<FieldAccessorListenerFactory<E>> fieldAccessorListenerFactories;
FieldAccessorFactoryProvider(final Field field, final FieldAccessorFactory<E> fieldAccessorFactory, final List<FieldAccessorListenerFactory<E>> fieldAccessorListenerFactories) {
this.field = field;
FieldAccessorFactoryProvider(final Neo4JPersistentProperty property, final FieldAccessorFactory fieldAccessorFactory, final List fieldAccessorListenerFactories) {
this.property = property;
this.fieldAccessorFactory = fieldAccessorFactory;
this.fieldAccessorListenerFactories = fieldAccessorListenerFactories;
}
public FieldAccessor<E> accessor() {
if (fieldAccessorFactory == null) return null;
return fieldAccessorFactory.forField(field);
return fieldAccessorFactory.forField(property);
}
public List<FieldAccessListener<E, ?>> listeners() {
if (fieldAccessorListenerFactories == null) return null;
final List<FieldAccessListener<E, ?>> listeners = new ArrayList<FieldAccessListener<E, ?>>(fieldAccessorListenerFactories.size());
for (final FieldAccessorListenerFactory<E> fieldAccessorListenerFactory : fieldAccessorListenerFactories) {
listeners.add(fieldAccessorListenerFactory.forField(field));
listeners.add(fieldAccessorListenerFactory.forField(property));
}
return listeners;
}
public Field getField() {
return field;
public Neo4JPersistentProperty getProperty() {
return property;
}
}
private final Class<T> type;
private final TypeInformation<?> type;
private final List<FieldAccessorFactoryProvider<T>> fieldAccessorFactoryProviders = new ArrayList<FieldAccessorFactoryProvider<T>>();
private final IdFieldAccessorFactory idFieldAccessorFactory;
private Field idField;
private Neo4JPersistentProperty idProperty;
FieldAccessorFactoryProviders(Class<T> type) {
FieldAccessorFactoryProviders(TypeInformation<?> type) {
this.type = type;
idFieldAccessorFactory = new IdFieldAccessorFactory();
}
public Map<Field, FieldAccessor<T>> getFieldAccessors() {
final Map<Field, FieldAccessor<T>> result = new HashMap<Field, FieldAccessor<T>>(fieldAccessorFactoryProviders.size(),1);
public Map<Neo4JPersistentProperty, FieldAccessor<T>> getFieldAccessors() {
final Map<Neo4JPersistentProperty, FieldAccessor<T>> result = new HashMap<Neo4JPersistentProperty, FieldAccessor<T>>(fieldAccessorFactoryProviders.size(),1);
for (final FieldAccessorFactoryProvider<T> fieldAccessorFactoryProvider : fieldAccessorFactoryProviders) {
final FieldAccessor<T> accessor = fieldAccessorFactoryProvider.accessor();
result.put(fieldAccessorFactoryProvider.getField(), accessor);
result.put(fieldAccessorFactoryProvider.getProperty(), accessor);
}
return result;
}
public Map<Field, List<FieldAccessListener<T,?>>> getFieldAccessListeners() {
final Map<Field, List<FieldAccessListener<T,?>>> result = new HashMap<Field, List<FieldAccessListener<T,?>>>(fieldAccessorFactoryProviders.size(),1);
public Map<Neo4JPersistentProperty, List<FieldAccessListener<T,?>>> getFieldAccessListeners() {
final Map<Neo4JPersistentProperty, List<FieldAccessListener<T,?>>> result = new HashMap<Neo4JPersistentProperty, List<FieldAccessListener<T,?>>>(fieldAccessorFactoryProviders.size(),1);
for (final FieldAccessorFactoryProvider<T> fieldAccessorFactoryProvider : fieldAccessorFactoryProviders) {
final List<FieldAccessListener<T,?>> listeners = (List<FieldAccessListener<T,?>>) fieldAccessorFactoryProvider.listeners();
result.put(fieldAccessorFactoryProvider.getField(), listeners);
result.put(fieldAccessorFactoryProvider.getProperty(), listeners);
}
return result;
}
public void add(Field field, FieldAccessorFactory<?> fieldAccessorFactory, List<FieldAccessorListenerFactory> listenerFactories) {
fieldAccessorFactoryProviders.add(new FieldAccessorFactoryProvider(field, fieldAccessorFactory, listenerFactories));
if (idFieldAccessorFactory.accept(field)) this.idField = field;
public void add(Neo4JPersistentProperty property, FieldAccessorFactory<?> fieldAccessorFactory, List<FieldAccessorListenerFactory> listenerFactories) {
fieldAccessorFactoryProviders.add(new FieldAccessorFactoryProvider(property, fieldAccessorFactory, listenerFactories));
if (property.isIdProperty()) this.idProperty = property;
}
public Field getIdField() {
return idField;
public Neo4JPersistentProperty getIdProperty() {
return idProperty;
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.data.neo4j.fieldaccess;
import java.lang.reflect.Field;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
/**
* factory interface for field accessor listeners. Provides means to check if a field is eligible for this factory
@@ -27,14 +27,14 @@ import java.lang.reflect.Field;
*/
public interface FieldAccessorListenerFactory<E> {
/**
* @param f field to check
* @return true if this factory is able to create a listener for the field
*
* @param property@return true if this factory is able to create a listener for the field
*/
boolean accept(Field f);
boolean accept(Neo4JPersistentProperty property);
/**
* @param f field to create a listener for
* @return newly created field listener
*
* @param property@return newly created field listener
*/
FieldAccessListener<E, ?> forField(Field f);
FieldAccessListener<E, ?> forField(Neo4JPersistentProperty property);
}

View File

@@ -16,11 +16,8 @@
package org.springframework.data.neo4j.fieldaccess;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.core.NodeBacked;
import javax.persistence.Id;
import java.lang.reflect.Field;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import static org.springframework.data.neo4j.support.DoReturn.doReturn;
@@ -30,25 +27,20 @@ import static org.springframework.data.neo4j.support.DoReturn.doReturn;
*/
public class IdFieldAccessorFactory implements FieldAccessorFactory<NodeBacked> {
@Override
public boolean accept(final Field f) {
return isIdField(f);
}
private boolean isIdField(Field field) {
final Class<?> type = field.getType();
return (type.equals(Long.class) || type.equals(long.class)) && (field.isAnnotationPresent(GraphId.class) || field.isAnnotationPresent(Id.class));
public boolean accept(final Neo4JPersistentProperty property) {
return property.isIdProperty();
}
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
return new IdFieldAccessor(field);
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty property) {
return new IdFieldAccessor(property);
}
public static class IdFieldAccessor implements FieldAccessor<NodeBacked> {
protected final Field field;
protected final Neo4JPersistentProperty property;
public IdFieldAccessor(final Field field) {
this.field = field;
public IdFieldAccessor(final Neo4JPersistentProperty property) {
this.property = property;
}
@Override

View File

@@ -23,6 +23,7 @@ import org.neo4j.graphdb.index.Index;
import org.neo4j.index.lucene.ValueContext;
import org.springframework.data.neo4j.annotation.Indexed;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.AnnotatedElement;
@@ -42,18 +43,18 @@ public class IndexingPropertyFieldAccessorListenerFactory<S extends PropertyCont
}
@Override
public boolean accept(final Field f) {
return isPropertyField(f) && indexProvider.isIndexed(f);
public boolean accept(final Neo4JPersistentProperty property) {
return isPropertyField(property) && property.isIndexed();
}
private boolean isPropertyField(final Field f) {
return propertyFieldAccessorFactory.accept(f) || convertingNodePropertyFieldAccessorFactory.accept(f);
private boolean isPropertyField(final Neo4JPersistentProperty property) {
return propertyFieldAccessorFactory.accept(property) || convertingNodePropertyFieldAccessorFactory.accept(property);
}
@Override
public FieldAccessListener<T, ?> forField(Field field) {
return (FieldAccessListener<T, ?>) new IndexingPropertyFieldAccessorListener(field, indexProvider);
public FieldAccessListener<T, ?> forField(Neo4JPersistentProperty property) {
return (FieldAccessListener<T, ?>) new IndexingPropertyFieldAccessorListener(property, indexProvider);
}
@@ -64,36 +65,27 @@ public class IndexingPropertyFieldAccessorListenerFactory<S extends PropertyCont
this.graphDatabaseContext = graphDatabaseContext;
}
private boolean isIndexed(final Field f) {
final Indexed indexedAnnotation = getIndexedAnnotation(f);
return indexedAnnotation != null;
}
private String getIndexKey(Field field) {
Indexed indexed = getIndexedAnnotation(field);
if (indexed==null || indexed.fieldName().isEmpty()) return DelegatingFieldAccessorFactory.getNeo4jPropertyName(field);
private String getIndexKey(Neo4JPersistentProperty property) {
Indexed indexed = property.getAnnotation(Indexed.class);
if (indexed==null || indexed.fieldName().isEmpty()) return property.getNeo4jPropertyName();
return indexed.fieldName();
}
private boolean isFulltextIndex(Field field) {
Indexed indexed = getIndexedAnnotation(field);
return indexed!=null && indexed.fulltext();
}
private Indexed getIndexedAnnotation(AnnotatedElement element) {
return element.getAnnotation(Indexed.class);
}
private Index<S> getIndex(Field field, T instance) {
final Indexed indexedAnnotation = getIndexedAnnotation(field);
final Class<T> type = (Class<T>) field.getDeclaringClass();
private Index<S> getIndex(Neo4JPersistentProperty property, GraphBacked instance) {
final Indexed indexedAnnotation = property.getAnnotation(Indexed.class);
final Class<T> type = (Class<T>) property.getOwner().getType();
final String providedIndexName = indexedAnnotation.indexName().isEmpty() ? null : indexedAnnotation.indexName();
String indexName = Indexed.Name.get(indexedAnnotation.level(), type, providedIndexName, instance.getClass());
if (!isFulltextIndex(field)) {
if (!property.getIndexInfo().isFulltext()) {
return graphDatabaseContext.getIndex(type, indexName, false);
}
if (providedIndexName == null) throw new IllegalStateException("@Indexed(fullext=true) on "+field+" requires an providedIndexName too ");
if (providedIndexName == null) throw new IllegalStateException("@Indexed(fullext=true) on "+property+" requires an providedIndexName too ");
String defaultIndexName = Indexed.Name.get(indexedAnnotation.level(), type, null, instance.getClass());
if (providedIndexName.equals(defaultIndexName)) throw new IllegalStateException("Full-index name for "+field+" must differ from the default name: "+defaultIndexName);
if (providedIndexName.equals(defaultIndexName)) throw new IllegalStateException("Full-index name for "+property+" must differ from the default name: "+defaultIndexName);
return graphDatabaseContext.getIndex(type, indexName, true);
}
}
@@ -107,18 +99,18 @@ public class IndexingPropertyFieldAccessorListenerFactory<S extends PropertyCont
private final static Log log = LogFactory.getLog( IndexingPropertyFieldAccessorListener.class );
protected final String indexKey;
private final Field field;
private final Neo4JPersistentProperty property;
private final IndexProvider indexProvider;
public IndexingPropertyFieldAccessorListener(final Field field, IndexProvider indexProvider) {
this.field = field;
public IndexingPropertyFieldAccessorListener(final Neo4JPersistentProperty property, IndexProvider indexProvider) {
this.property = property;
this.indexProvider = indexProvider;
indexKey = indexProvider.getIndexKey(field);
indexKey = indexProvider.getIndexKey(property);
}
@Override
public void valueChanged(GraphBacked<T> graphBacked, Object oldVal, Object newVal) {
Index<T> index = indexProvider.getIndex(field, graphBacked);
Index<T> index = indexProvider.getIndex(property, graphBacked);
if (newVal instanceof Number) newVal = ValueContext.numeric((Number) newVal);
final T state = graphBacked.getPersistentState();

View File

@@ -18,9 +18,9 @@ package org.springframework.data.neo4j.fieldaccess;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import javax.persistence.Id;
import java.lang.reflect.Field;
/**
* @author Michael Hunger
@@ -28,20 +28,20 @@ import java.lang.reflect.Field;
*/
public class JpaIdFieldAccessListenerFactory implements FieldAccessorListenerFactory<NodeBacked> {
@Override
public boolean accept(final Field f) {
return f.isAnnotationPresent(Id.class);
public boolean accept(final Neo4JPersistentProperty property) {
return property.isAnnotationPresent(Id.class);
}
@Override
public FieldAccessListener<NodeBacked, ?> forField(final Field field) {
return new JpaIdFieldListener(field);
public FieldAccessListener<NodeBacked, ?> forField(final Neo4JPersistentProperty property) {
return new JpaIdFieldListener(property);
}
public static class JpaIdFieldListener implements FieldAccessListener<NodeBacked, Object> {
protected final Field field;
protected final Neo4JPersistentProperty property;
public JpaIdFieldListener(final Field field) {
this.field = field;
public JpaIdFieldListener(final Neo4JPersistentProperty property) {
this.property = property;
}
@Override

View File

@@ -20,9 +20,9 @@ import org.neo4j.graphdb.Node;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.DoReturn;
import java.lang.reflect.Field;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
@@ -34,11 +34,11 @@ import java.util.Set;
public class ManagedFieldAccessorSet<ENTITY,T> extends AbstractSet<T> {
private final ENTITY entity;
final Set<T> delegate;
private final Field field;
private final Neo4JPersistentProperty property;
public ManagedFieldAccessorSet(final ENTITY entity, final Object newVal, final Field field) {
public ManagedFieldAccessorSet(final ENTITY entity, final Object newVal, final Neo4JPersistentProperty property) {
this.entity = entity;
this.field = field;
this.property = property;
delegate = (Set<T>) newVal;
}
@@ -78,13 +78,12 @@ public class ManagedFieldAccessorSet<ENTITY,T> extends AbstractSet<T> {
private Object updateValue(EntityState entityState) {
try {
final Object newValue = entityState.setValue(field, delegate);
final Object newValue = entityState.setValue(property, delegate);
if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue);
field.setAccessible(true);
field.set(entity,newValue);
property.setValue(entity, newValue);
return newValue;
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not update field "+field+" to new value of type "+delegate.getClass());
throw new RuntimeException("Could not update field "+ property +" to new value of type "+delegate.getClass());
}
}

View File

@@ -19,9 +19,12 @@ import java.lang.reflect.Field;
import java.util.Map;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.DoReturn;
/**
@@ -33,23 +36,23 @@ import org.springframework.data.neo4j.support.DoReturn;
*/
public class ManagedPrefixedDynamicProperties<ENTITY> extends PrefixedDynamicProperties {
private final ENTITY entity;
private final Field field;
private final Neo4JPersistentProperty property;
public ManagedPrefixedDynamicProperties(String prefix, final Field field, final ENTITY entity) {
public ManagedPrefixedDynamicProperties(String prefix, final Neo4JPersistentProperty property, final ENTITY entity) {
super(prefix);
this.field = field;
this.property = property;
this.entity = entity;
}
public ManagedPrefixedDynamicProperties(String prefix, int initialCapacity, final Field field, final ENTITY entity) {
public ManagedPrefixedDynamicProperties(String prefix, int initialCapacity, final Neo4JPersistentProperty property, final ENTITY entity) {
super(prefix, initialCapacity);
this.field = field;
this.property = property;
this.entity = entity;
}
public static <E> ManagedPrefixedDynamicProperties<E> create(String prefix, final Field field,
public static <E> ManagedPrefixedDynamicProperties<E> create(String prefix, final Neo4JPersistentProperty property,
final E entity) {
return new ManagedPrefixedDynamicProperties<E>(prefix, field, entity);
return new ManagedPrefixedDynamicProperties<E>(prefix, property, entity);
}
@Override
@@ -73,7 +76,7 @@ public class ManagedPrefixedDynamicProperties<ENTITY> extends PrefixedDynamicPro
@Override
public DynamicProperties createFrom(Map<String, Object> map) {
DynamicProperties d = new ManagedPrefixedDynamicProperties<ENTITY>(prefix, map.size(), field, entity);
DynamicProperties d = new ManagedPrefixedDynamicProperties<ENTITY>(prefix, map.size(), property, entity);
d.setPropertiesFrom(map);
return d;
}
@@ -92,14 +95,15 @@ public class ManagedPrefixedDynamicProperties<ENTITY> extends PrefixedDynamicPro
private Object updateValue(EntityState entityState) {
try {
final Object newValue = entityState.setValue(field, this);
final Object newValue = entityState.setValue(property, this);
if (newValue instanceof DoReturn)
return DoReturn.unwrap(newValue);
final Field field = this.property.getField();
field.setAccessible(true);
field.set(entity, newValue);
return newValue;
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not update field " + field + " to new value of type "
throw new RuntimeException("Could not update field " + property + " to new value of type "
+ this.getClass());
}
}

View File

@@ -20,6 +20,7 @@ import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GenericTypeExtractor;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
@@ -33,40 +34,8 @@ public abstract class NodeRelationshipFieldAccessorFactory implements FieldAcces
protected GraphDatabaseContext graphDatabaseContext;
public NodeRelationshipFieldAccessorFactory(
GraphDatabaseContext graphDatabaseContext) {
super();
public NodeRelationshipFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) {
this.graphDatabaseContext = graphDatabaseContext;
}
@SuppressWarnings({"unchecked"})
protected Class<? extends NodeBacked> targetFrom(Field field, RelatedTo relatedTo) {
if (relatedTo!=null && relatedTo.elementClass()!=NodeBacked.class) return relatedTo.elementClass();
return (Class<? extends NodeBacked>) GenericTypeExtractor.resolveFieldType(field);
}
protected Direction dirFrom(RelatedTo relAnnotation) {
return relAnnotation.direction().toNeo4jDir();
}
protected DynamicRelationshipType typeFrom(Field field) {
return DynamicRelationshipType.withName(DelegatingFieldAccessorFactory.getNeo4jPropertyName(field));
}
protected DynamicRelationshipType typeFrom(RelatedTo relAnnotation) {
return DynamicRelationshipType.withName(relAnnotation.type());
}
protected DynamicRelationshipType typeFrom(Field field, RelatedTo relAnnotation) {
return "".equals(relAnnotation.type()) ? typeFrom(field) : typeFrom(relAnnotation);
}
protected RelatedTo getRelationshipAnnotation(Field field) {
return field.getAnnotation(RelatedTo.class);
}
protected boolean hasValidRelationshipAnnotation(Field field) {
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
return (relAnnotation != null);
}
}

View File

@@ -22,6 +22,7 @@ import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
@@ -33,8 +34,8 @@ import java.util.Set;
* @since 12.09.2010
*/
public abstract class NodeToNodesRelationshipFieldAccessor<TARGET extends GraphBacked> extends AbstractNodeRelationshipFieldAccessor<NodeBacked, Node, TARGET, Node> {
public NodeToNodesRelationshipFieldAccessor(final Class<? extends TARGET> clazz, final GraphDatabaseContext graphDatabaseContext, final Direction direction, final RelationshipType type, Field field) {
super(clazz, graphDatabaseContext, direction, type,field);
public NodeToNodesRelationshipFieldAccessor(final Class<? extends TARGET> clazz, final GraphDatabaseContext graphDatabaseContext, final Direction direction, final RelationshipType type, Neo4JPersistentProperty property) {
super(clazz, graphDatabaseContext, direction, type,property);
}
@Override

View File

@@ -21,6 +21,8 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.neo4j.annotation.RelatedToVia;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.mapping.RelationshipInfo;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
@@ -40,47 +42,19 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
}
@Override
public boolean accept(final Field f) {
return Iterable.class.isAssignableFrom(f.getType()) && hasValidRelationshipAnnotation(f);
public boolean accept(final Neo4JPersistentProperty property) {
return property.isRelationship() && !property.getRelationshipInfo().targetsNodes() && property.getRelationshipInfo().isMultiple();
}
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
final RelatedToVia relEntityAnnotation = getRelationshipAnnotation(field);
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), graphDatabaseContext,field);
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty property) {
final RelationshipInfo relationshipInfo = property.getRelationshipInfo();
return new OneToNRelationshipEntityFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class<? extends RelationshipBacked>) relationshipInfo.getTargetType().getType(), graphDatabaseContext,property);
}
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) {
return field.getAnnotation(RelatedToVia.class);
}
private Class<? extends RelationshipBacked> targetFrom(final RelatedToVia relEntityAnnotation) {
return relEntityAnnotation.elementClass();
}
private Direction dirFrom(final RelatedToVia relEntityAnnotation) {
return relEntityAnnotation.direction().toNeo4jDir();
}
private DynamicRelationshipType typeFrom(final RelatedToVia relEntityAnnotation) {
return DynamicRelationshipType.withName(relEntityAnnotation.type());
}
public static class OneToNRelationshipEntityFieldAccessor extends AbstractNodeRelationshipFieldAccessor<NodeBacked, Node, RelationshipBacked, Relationship> {
public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends RelationshipBacked> elementClass, final GraphDatabaseContext graphDatabaseContext, Field field) {
super(elementClass, graphDatabaseContext, direction, type, field);
public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends RelationshipBacked> elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty property) {
super(elementClass, graphDatabaseContext, direction, type, property);
}
@Override
@@ -97,7 +71,7 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
public Object getValue(final NodeBacked entity) {
checkUnderlyingNode(entity);
final Set<RelationshipBacked> result = createEntitySetFromRelationships(entity);
return doReturn(new ManagedFieldAccessorSet<NodeBacked, RelationshipBacked>(entity, result, field));
return doReturn(new ManagedFieldAccessorSet<NodeBacked, RelationshipBacked>(entity, result, property));
}
private Set<RelationshipBacked> createEntitySetFromRelationships(final NodeBacked entity) {

View File

@@ -19,12 +19,11 @@ package org.springframework.data.neo4j.fieldaccess;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.mapping.RelationshipInfo;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
@@ -36,21 +35,24 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel
super(graphDatabaseContext);
}
@Override
public boolean accept(final Field f) {
return Collection.class.isAssignableFrom(f.getType()) && hasValidRelationshipAnnotation(f);
}
@Override
public boolean accept(final Neo4JPersistentProperty property) {
if (!property.isRelationship()) return false;
final RelationshipInfo info = property.getRelationshipInfo();
return info.isMultiple() && info.targetsNodes() && !info.isReadonly();
}
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
return new OneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field, relAnnotation), graphDatabaseContext,field);
@Override
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty property) {
final RelationshipInfo relationshipInfo = property.getRelationshipInfo();
final Class<? extends NodeBacked> targetType = (Class<? extends NodeBacked>) relationshipInfo.getTargetType().getType();
return new OneToNRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), targetType, graphDatabaseContext,property);
}
public static class OneToNRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor<NodeBacked> {
public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final GraphDatabaseContext graphDatabaseContext, Field field) {
super(elementClass, graphDatabaseContext, direction, type,field);
public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty property) {
super(elementClass, graphDatabaseContext, direction, type,property);
}
public Object setValue(final NodeBacked entity, final Object newVal) {

View File

@@ -19,8 +19,7 @@ package org.springframework.data.neo4j.fieldaccess;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.neo4j.core.GraphBacked;
import java.lang.reflect.Field;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import static org.springframework.data.neo4j.support.DoReturn.doReturn;
@@ -37,34 +36,26 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory<GraphB
}
@Override
public boolean accept(final Field f) {
return isNeo4jPropertyType(f.getType());
public boolean accept(final Neo4JPersistentProperty f) {
return f.isNeo4jPropertyType();
}
@Override
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Field field) {
return new PropertyFieldAccessor(conversionService,DelegatingFieldAccessorFactory.getNeo4jPropertyName(field),field.getType());
}
private boolean isNeo4jPropertyType(final Class<?> fieldType) {
// todo: add array support
return fieldType.isPrimitive()
|| fieldType.equals(String.class)
|| fieldType.equals(Character.class)
|| fieldType.equals(Boolean.class)
|| (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType))
|| (fieldType.isArray() && !fieldType.getComponentType().isArray() && isNeo4jPropertyType(fieldType.getComponentType()));
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Neo4JPersistentProperty field) {
return new PropertyFieldAccessor(conversionService, field);
}
public static class PropertyFieldAccessor implements FieldAccessor<GraphBacked<PropertyContainer>> {
private final ConversionService conversionService;
private final Neo4JPersistentProperty property;
protected final String propertyName;
protected final Class<?> fieldType;
public PropertyFieldAccessor(ConversionService conversionService, String propertyName, Class fieldType) {
public PropertyFieldAccessor(ConversionService conversionService, Neo4JPersistentProperty property) {
this.conversionService = conversionService;
this.propertyName = propertyName;
this.fieldType = fieldType;
this.property = property;
this.propertyName = property.getNeo4jPropertyName();
this.fieldType = property.getType() ;
}
@Override

View File

@@ -19,7 +19,9 @@ package org.springframework.data.neo4j.fieldaccess;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GenericTypeExtractor;
import org.springframework.data.util.TypeInformation;
import java.lang.reflect.Field;
import java.util.HashMap;
@@ -29,7 +31,7 @@ import static org.springframework.data.neo4j.support.DoReturn.doReturn;
public class QueryFieldAccessorFactory implements FieldAccessorFactory<NodeBacked> {
@Override
public boolean accept(final Field f) {
public boolean accept(final Neo4JPersistentProperty f) {
final Query query = f.getAnnotation(Query.class);
return query != null
&& !query.value().isEmpty();
@@ -37,7 +39,7 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory<NodeBacke
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty field) {
return new QueryFieldAccessor(field);
}
@@ -46,27 +48,27 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory<NodeBacke
* @since 12.09.2010
*/
public static class QueryFieldAccessor implements FieldAccessor<NodeBacked> {
protected final Field field;
protected final Neo4JPersistentProperty property;
private final String query;
private Class<?> target;
protected String[] annotationParams;
private boolean iterableResult;
public QueryFieldAccessor(final Field field) {
this.field = field;
final Query query = field.getAnnotation(Query.class);
public QueryFieldAccessor(final Neo4JPersistentProperty property) {
this.property = property;
final Query query = property.getAnnotation(Query.class);
this.annotationParams = query.params();
if ((this.annotationParams.length % 2) != 0) {
throw new IllegalArgumentException("Number of parameters has to be even to construct a parameter map");
}
this.query = query.value();
this.iterableResult = Iterable.class.isAssignableFrom(field.getType());
this.target = resolveTarget(query,field);
this.iterableResult = Iterable.class.isAssignableFrom(property.getType());
this.target = resolveTarget(query,property);
}
private Class<?> resolveTarget(Query query, Field field) {
private Class<?> resolveTarget(Query query, Neo4JPersistentProperty property) {
if (!query.elementClass().equals(Object.class)) return query.elementClass();
return GenericTypeExtractor.resolveFieldType(field);
return property.getTypeInformation().getActualType().getType();
}
@Override
@@ -76,7 +78,7 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory<NodeBacke
@Override
public Object setValue(final NodeBacked nodeBacked, final Object newVal) {
throw new InvalidDataAccessApiUsageException("Cannot set readonly query field " + field);
throw new InvalidDataAccessApiUsageException("Cannot set readonly query field " + property);
}
@Override

View File

@@ -19,8 +19,9 @@ package org.springframework.data.neo4j.fieldaccess;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.mapping.RelationshipInfo;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
@@ -32,19 +33,21 @@ public class ReadOnlyOneToNRelationshipFieldAccessorFactory extends NodeRelation
}
@Override
public boolean accept(final Field f) {
return Iterable.class.equals(f.getType()) && hasValidRelationshipAnnotation(f);
public boolean accept(final Neo4JPersistentProperty f) {
if (!f.isRelationship()) return false;
final RelationshipInfo info = f.getRelationshipInfo();
return info.isMultiple() && info.targetsNodes() && info.isReadonly();
}
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field, relAnnotation), graphDatabaseContext,field);
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty property) {
final RelationshipInfo relationshipInfo = property.getRelationshipInfo();
return new ReadOnlyOneToNRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class<? extends NodeBacked>) property.getRelationshipInfo().getTargetType().getType(), graphDatabaseContext,property);
}
public static class ReadOnlyOneToNRelationshipFieldAccessor extends OneToNRelationshipFieldAccessorFactory.OneToNRelationshipFieldAccessor {
public ReadOnlyOneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final GraphDatabaseContext graphDatabaseContext, Field field) {
public ReadOnlyOneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty field) {
super(type,direction,elementClass, graphDatabaseContext, field);
}

View File

@@ -23,10 +23,9 @@ import org.springframework.data.neo4j.annotation.EndNode;
import org.springframework.data.neo4j.annotation.StartNode;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
import static org.springframework.data.neo4j.support.DoReturn.doReturn;
/**
@@ -43,22 +42,22 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor
}
@Override
public boolean accept(final Field f) {
public boolean accept(final Neo4JPersistentProperty f) {
return isStartNodeField(f) || isEndNodeField(f);
}
private boolean isEndNodeField(final Field f) {
private boolean isEndNodeField(final Neo4JPersistentProperty f) {
return f.isAnnotationPresent(EndNode.class);
}
private boolean isStartNodeField(final Field f) {
private boolean isStartNodeField(final Neo4JPersistentProperty f) {
return f.isAnnotationPresent(StartNode.class);
}
@Override
public FieldAccessor<RelationshipBacked> forField(final Field f) {
if (isStartNodeField(f)) {
return new RelationshipNodeFieldAccessor(f, graphDatabaseContext) {
public FieldAccessor<RelationshipBacked> forField(final Neo4JPersistentProperty property) {
if (isStartNodeField(property)) {
return new RelationshipNodeFieldAccessor(property, graphDatabaseContext) {
@Override
protected Node getNode(final Relationship relationship) {
return relationship.getStartNode();
@@ -66,8 +65,8 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor
};
}
if (isEndNodeField(f)) {
return new RelationshipNodeFieldAccessor(f, graphDatabaseContext) {
if (isEndNodeField(property)) {
return new RelationshipNodeFieldAccessor(property, graphDatabaseContext) {
@Override
protected Node getNode(final Relationship relationship) {
return relationship.getEndNode();
@@ -79,11 +78,11 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor
public static abstract class RelationshipNodeFieldAccessor implements FieldAccessor<RelationshipBacked> {
private final Field field;
private final Neo4JPersistentProperty property;
private final GraphDatabaseContext graphDatabaseContext;
public RelationshipNodeFieldAccessor(final Field field, final GraphDatabaseContext graphDatabaseContext) {
this.field = field;
public RelationshipNodeFieldAccessor(final Neo4JPersistentProperty property, final GraphDatabaseContext graphDatabaseContext) {
this.property = property;
this.graphDatabaseContext = graphDatabaseContext;
}
@@ -99,7 +98,7 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor
if (node == null) {
return null;
}
final NodeBacked result = graphDatabaseContext.createEntityFromState(node, (Class<? extends NodeBacked>) field.getType());
final NodeBacked result = graphDatabaseContext.createEntityFromState(node, (Class<? extends NodeBacked>) property.getType());
return doReturn(result);
}

View File

@@ -21,6 +21,8 @@ import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.mapping.RelationshipInfo;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import java.lang.reflect.Field;
@@ -36,21 +38,19 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel
}
@Override
public boolean accept(final Field f) {
return NodeBacked.class.isAssignableFrom(f.getType());
public boolean accept(final Neo4JPersistentProperty property) {
return property.isRelationship() && property.getRelationshipInfo().targetsNodes() && !property.getRelationshipInfo().isMultiple();
}
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
final RelatedTo relAnnotation = getRelationshipAnnotation(field);
if (relAnnotation == null)
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field, relAnnotation), graphDatabaseContext, field);
return new SingleRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field, relAnnotation), graphDatabaseContext,field);
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty property) {
final RelationshipInfo relationshipInfo = property.getRelationshipInfo();
return new SingleRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class<? extends NodeBacked>) relationshipInfo.getTargetType().getType(), graphDatabaseContext,property);
}
public static class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor<NodeBacked> {
public SingleRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> clazz, final GraphDatabaseContext graphDatabaseContext, Field field) {
super(clazz, graphDatabaseContext, direction, type, field);
public SingleRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> clazz, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty property) {
super(clazz, graphDatabaseContext, direction, type, property);
}
@Override

View File

@@ -18,19 +18,17 @@ package org.springframework.data.neo4j.fieldaccess;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.data.neo4j.core.GraphBacked;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
public class TransientFieldAccessorFactory implements FieldAccessorFactory<GraphBacked<PropertyContainer>> {
@Override
public boolean accept(final Field f) {
return Modifier.isTransient(f.getModifiers());
public boolean accept(final Neo4JPersistentProperty property) {
return property.isTransient();
}
@Override
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Field field) {
return new TransientFieldAccessor(field);
public FieldAccessor<GraphBacked<PropertyContainer>> forField(final Neo4JPersistentProperty property) {
return new TransientFieldAccessor(property);
}
/**
@@ -38,10 +36,10 @@ public class TransientFieldAccessorFactory implements FieldAccessorFactory<Graph
* @since 12.09.2010
*/
public static class TransientFieldAccessor implements FieldAccessor<GraphBacked<PropertyContainer>> {
protected final Field field;
protected final Neo4JPersistentProperty property;
public TransientFieldAccessor(final Field field) {
this.field = field;
public TransientFieldAccessor(final Neo4JPersistentProperty property) {
this.property = property;
}
@Override

View File

@@ -25,6 +25,7 @@ import org.springframework.data.neo4j.annotation.GraphTraversal;
import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GenericTypeExtractor;
import java.lang.reflect.Constructor;
@@ -34,7 +35,7 @@ import static org.springframework.data.neo4j.support.DoReturn.doReturn;
public class TraversalFieldAccessorFactory implements FieldAccessorFactory<NodeBacked> {
@Override
public boolean accept(final Field f) {
public boolean accept(final Neo4JPersistentProperty f) {
final GraphTraversal graphEntityTraversal = f.getAnnotation(GraphTraversal.class);
return graphEntityTraversal != null
&& graphEntityTraversal.traversalBuilder() != FieldTraversalDescriptionBuilder.class
@@ -43,8 +44,8 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory<NodeB
@Override
public FieldAccessor<NodeBacked> forField(final Field field) {
return new TraversalFieldAccessor(field);
public FieldAccessor<NodeBacked> forField(final Neo4JPersistentProperty property) {
return new TraversalFieldAccessor(property);
}
/**
@@ -52,24 +53,24 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory<NodeB
* @since 12.09.2010
*/
public static class TraversalFieldAccessor implements FieldAccessor<NodeBacked> {
protected final Field field;
protected final Neo4JPersistentProperty property;
private final FieldTraversalDescriptionBuilder fieldTraversalDescriptionBuilder;
private Class<?> target;
protected String[] params;
public TraversalFieldAccessor(final Field field) {
this.field = field;
final GraphTraversal graphEntityTraversal = field.getAnnotation(GraphTraversal.class);
this.target = resolveTarget(graphEntityTraversal,field);
public TraversalFieldAccessor(final Neo4JPersistentProperty property) {
this.property = property;
final GraphTraversal graphEntityTraversal = property.getAnnotation(GraphTraversal.class);
this.target = resolveTarget(graphEntityTraversal,property);
this.params = graphEntityTraversal.params();
this.fieldTraversalDescriptionBuilder = createTraversalDescription(graphEntityTraversal);
}
private Class<?> resolveTarget(GraphTraversal graphTraversal, Field field) {
private Class<?> resolveTarget(GraphTraversal graphTraversal, Neo4JPersistentProperty property) {
if (!graphTraversal.elementClass().equals(NodeBacked.class)) return graphTraversal.elementClass();
final Class<?> result = GenericTypeExtractor.resolveFieldType(field);
final Class<?> result = property.getTypeInformation().getActualType().getType();
Class<?>[] allowedTypes={NodeBacked.class,RelationshipBacked.class,Node.class,Relationship.class, Path.class};
if (!checkTypes(result,allowedTypes)) throw new IllegalArgumentException("The target result type "+result+" of the traversal is no subclass of the allowed types: "+field+" "+allowedTypes);
if (!checkTypes(result,allowedTypes)) throw new IllegalArgumentException("The target result type "+result+" of the traversal is no subclass of the allowed types: "+property+" "+allowedTypes);
return result;
}
@@ -88,12 +89,12 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory<NodeB
@Override
public Object setValue(final NodeBacked nodeBacked, final Object newVal) {
throw new InvalidDataAccessApiUsageException("Cannot set readonly traversal description field " + field);
throw new InvalidDataAccessApiUsageException("Cannot set readonly traversal description field " + property);
}
@Override
public Object getValue(final NodeBacked nodeBacked) {
final TraversalDescription traversalDescription = fieldTraversalDescriptionBuilder.build(nodeBacked,field,params);
final TraversalDescription traversalDescription = fieldTraversalDescriptionBuilder.build(nodeBacked, property,params);
return doReturn(nodeBacked.findAllByTraversal(target, traversalDescription));
}
@@ -105,7 +106,7 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory<NodeB
constructor.setAccessible(true);
return constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException("Error creating TraversalDescription from " + field,e);
throw new RuntimeException("Error creating TraversalDescription from " + property,e);
}
}

View File

@@ -19,7 +19,10 @@ package org.springframework.data.neo4j.fieldaccess;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.neo4j.core.GraphBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import javax.validation.Constraint;
@@ -40,20 +43,20 @@ class ValidatingNodePropertyFieldAccessorListenerFactory<T extends GraphBacked<?
}
@Override
public boolean accept(final Field f) {
return hasValidationAnnotation(f);
public boolean accept(final Neo4JPersistentProperty property) {
return hasValidationAnnotation(property);
}
private boolean hasValidationAnnotation(final Field f) {
for (Annotation annotation : f.getAnnotations()) {
private boolean hasValidationAnnotation(final Neo4JPersistentProperty property) {
for (Annotation annotation : property.getAnnotations()) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) return true;
}
return false;
}
@Override
public FieldAccessListener<T, ?> forField(Field field) {
return new ValidatingNodePropertyFieldAccessorListener(field,graphDatabaseContext.getValidator());
public FieldAccessListener<T, ?> forField(Neo4JPersistentProperty property) {
return new ValidatingNodePropertyFieldAccessorListener(property,graphDatabaseContext.getValidator());
}
@@ -66,18 +69,18 @@ class ValidatingNodePropertyFieldAccessorListenerFactory<T extends GraphBacked<?
private final static Log log = LogFactory.getLog( ValidatingNodePropertyFieldAccessorListener.class );
private String propertyName;
private Validator validator;
private Class<?> entityType;
private Neo4JPersistentEntity<?> entityType;
public ValidatingNodePropertyFieldAccessorListener(final Field field, Validator validator) {
public ValidatingNodePropertyFieldAccessorListener(final Neo4JPersistentProperty field, Validator validator) {
this.propertyName = field.getName();
this.entityType = field.getDeclaringClass();
this.entityType = (Neo4JPersistentEntity<?>) field.getOwner();
this.validator = validator;
}
@Override
public void valueChanged(GraphBacked<T> graphBacked, Object oldVal, Object newVal) {
if (validator==null) return;
Set<ConstraintViolation<T>> constraintViolations = validator.validateValue((Class<T>)entityType, propertyName, newVal);
Set<ConstraintViolation<T>> constraintViolations = validator.validateValue((Class<T>)entityType.getType(), propertyName, newVal);
if (!constraintViolations.isEmpty()) throw new ValidationException("Error validating field "+propertyName+ " of "+entityType+": "+constraintViolations);
}
}

View File

@@ -25,4 +25,5 @@ import org.springframework.data.mapping.PersistentEntity;
*/
public interface Neo4JPersistentEntity<T> extends PersistentEntity<T, Neo4JPersistentProperty> {
boolean useShortNames();
}

View File

@@ -17,8 +17,14 @@
package org.springframework.data.neo4j.mapping;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.util.TypeInformation;
import java.lang.annotation.Annotation;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* Implementation of {@link Neo4JPersistentEntity}.
*
@@ -26,6 +32,8 @@ import org.springframework.data.util.TypeInformation;
*/
class Neo4JPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4JPersistentProperty> implements Neo4JPersistentEntity<T> {
private Map<Class<? extends Annotation>,Annotation> annotations=new IdentityHashMap<Class<? extends Annotation>,Annotation>();
/**
* Creates a new {@link Neo4JPersistentEntityImpl} instance.
*
@@ -33,5 +41,20 @@ class Neo4JPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4JPersist
*/
public Neo4JPersistentEntityImpl(TypeInformation<T> information) {
super(information);
for (Annotation annotation : information.getType().getAnnotations()) {
annotations.put(annotation.annotationType(),annotation);
}
}
public boolean useShortNames() {
final NodeEntity graphEntity = getAnnotation(NodeEntity.class);
if (graphEntity != null) return graphEntity.useShortNames();
final RelationshipEntity graphRelationship = getAnnotation(RelationshipEntity.class);
if (graphRelationship != null) return graphRelationship.useShortNames();
return false;
}
private <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return (T) annotations.get(annotationType);
}
}

View File

@@ -16,7 +16,13 @@
package org.springframework.data.neo4j.mapping;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.neo4j.annotation.Indexed;
import javax.persistence.Id;
import java.lang.annotation.Annotation;
import java.util.Collection;
/**
* Interface for Neo4J specific {@link PersistentProperty}s. Declares additional metadata to lookup relationship
@@ -45,4 +51,26 @@ public interface Neo4JPersistentProperty extends PersistentProperty<Neo4JPersist
boolean isIndexed();
Neo4JPersistentPropertyImpl.IndexInfo getIndexInfo();
String getNeo4jPropertyName();
boolean isSimpleValueField();
boolean isSerializableField(final ConversionService conversionService);
boolean isDeserializableField(final ConversionService conversionService);
boolean isNeo4jPropertyType();
boolean isSyntheticField();
Collection<? extends Annotation> getAnnotations();
<T extends Annotation> T getAnnotation(Class<T> annotationType);
<T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType);
void setValue(Object entity, Object newValue) throws IllegalAccessException;
Object getValue(final Object entity) throws IllegalAccessException;
}

View File

@@ -16,17 +16,20 @@
package org.springframework.data.neo4j.mapping;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AbstractPersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.core.Direction;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.util.TypeInformation;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -65,7 +68,7 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty<Neo4JPersis
return annotation!=null ? new IndexInfo(annotation) : null;
}
private <T extends Annotation> T getAnnotation(Class<T> annotationType) {
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
return (T) annotations.get(annotationType);
}
@@ -83,12 +86,18 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty<Neo4JPersis
return null;
}
private <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType) {
public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType) {
return annotations.containsKey(annotationType);
}
@Override
public void setValue(Object entity, Object newValue) throws IllegalAccessException {
field.setAccessible(true);
field.set(entity, newValue);
}
private static boolean hasAnnotation(TypeInformation<?> typeInformation, final Class<NodeEntity> annotationClass) {
return typeInformation.getActualType().getClass().isAnnotationPresent(annotationClass);
return typeInformation.getActualType().getType().isAnnotationPresent(annotationClass);
}
@Override
@@ -121,6 +130,58 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty<Neo4JPersis
return indexInfo;
}
public String getNeo4jPropertyName() {
final Neo4JPersistentEntity entityClass = (Neo4JPersistentEntity) getOwner();
if (entityClass.useShortNames()) return getName();
return String.format("%s.%s", entityClass.getType().getSimpleName(), getName());
}
public boolean isSimpleValueField() {
final Class<?> type = getType();
if (Iterable.class.isAssignableFrom(type) || NodeBacked.class.isAssignableFrom(type) || RelationshipBacked.class.isAssignableFrom(type))
return false;
return true;
}
public boolean isSerializableField(final ConversionService conversionService) {
return isSimpleValueField() && conversionService.canConvert(getType(), String.class);
}
public boolean isDeserializableField(final ConversionService conversionService) {
return isSimpleValueField() && conversionService.canConvert(String.class, getType());
}
@Override
public boolean isNeo4jPropertyType() {
return isNeo4jPropertyType(getType());
}
private static boolean isNeo4jPropertyType(final Class<?> fieldType) {
// todo: add array support
return fieldType.isPrimitive()
|| fieldType.equals(String.class)
|| fieldType.equals(Character.class)
|| fieldType.equals(Boolean.class)
|| (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType))
|| (fieldType.isArray() && !fieldType.getComponentType().isArray() && isNeo4jPropertyType(fieldType.getComponentType()));
}
public boolean isSyntheticField() {
return getName().contains("$");
}
@Override
public Collection<? extends Annotation> getAnnotations() {
return annotations.values();
}
public Object getValue(final Object entity) throws IllegalAccessException {
final Field field = getField();
field.setAccessible(true);
return field.get(entity);
}
public static class IndexInfo {
private String indexName;
private boolean fulltext;
@@ -143,4 +204,8 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty<Neo4JPersis
}
}
@Override
public String toString() {
return getType() +" "+ getName() + " rel: "+isRelationship()+ " idx: "+isIndexed();
}
}

View File

@@ -16,14 +16,18 @@
package org.springframework.data.neo4j.mapping;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.annotation.RelatedToVia;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.core.Direction;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import scala.annotation.target.field;
import java.lang.reflect.Field;
@@ -33,7 +37,8 @@ public class RelationshipInfo {
private final Direction direction;
private final String type;
private final TypeInformation<?> targetType;
private final boolean isNodeRelationship;
private final boolean targetsNodes;
private boolean readonly;
public Direction getDirection() {
return direction;
@@ -42,38 +47,61 @@ public class RelationshipInfo {
public String getType() {
return type;
}
public RelationshipType getRelationshipType() {
return DynamicRelationshipType.withName(type);
}
public boolean isMultiple() {
return isMultiple;
}
public RelationshipInfo(String type, Direction direction, TypeInformation<?> typeInformation) {
public RelationshipInfo(String type, Direction direction, TypeInformation<?> typeInformation, TypeInformation<?> concreteActualType, boolean targetsNode) {
this.type = type;
this.direction = direction;
isMultiple = typeInformation.isCollectionLike();
targetType = typeInformation.getActualType();
isNodeRelationship = isNodeEntity(targetType);
targetType = concreteActualType!=null ? concreteActualType : typeInformation.getActualType();
targetsNodes = isNodeEntity(targetType);
this.readonly = isMultiple() && typeInformation.getType().equals(Iterable.class);
}
private boolean isNodeEntity(TypeInformation<?> targetType) {
final Class<?> type = targetType.getType();
if (type.isAnnotationPresent(NodeEntity.class)) return true;
if (type.isAnnotationPresent(RelationshipEntity.class)) return false;
throw new MappingException("Target type for relationship "+ this.type +" field is invalid "+type);
throw new MappingException("Target type for relationship " + this.type + " field is invalid " + type);
}
public static RelationshipInfo fromField(Field field, TypeInformation<?> typeInformation) {
return new RelationshipInfo(field.getName(), Direction.OUTGOING, typeInformation);
return new RelationshipInfo(field.getName(), Direction.OUTGOING, typeInformation,null,true);
}
public static RelationshipInfo fromField(Field field, RelatedTo annotation, TypeInformation<?> typeInformation) {
return new RelationshipInfo(
annotation.type().isEmpty() ? field.getName() : annotation.type(),
annotation.direction(),
typeInformation);
annotation.direction().toNeo4jDir(),
typeInformation,
annotation.elementClass() != NodeBacked.class ? ClassTypeInformation.from(annotation.elementClass()) : null,
true);
}
public static RelationshipInfo fromField(Field field, RelatedToVia annotation, TypeInformation<?> typeInformation) {
return new RelationshipInfo(
annotation.type().isEmpty() ? field.getName() : annotation.type(),
annotation.direction(),
typeInformation);
annotation.direction().toNeo4jDir(),
typeInformation,
annotation.elementClass() != RelationshipBacked.class ? ClassTypeInformation.from(annotation.elementClass()) : null,
false);
}
public TypeInformation<?> getTargetType() {
return targetType;
}
public boolean targetsNodes() {
return targetsNodes;
}
public boolean isReadonly() {
return readonly;
}
}

View File

@@ -183,7 +183,7 @@ public class GraphDatabaseContext {
} else if (state instanceof Relationship && RelationshipBacked.class.isAssignableFrom(type)) {
return (TypeRepresentationStrategy<S, T>) relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
throw new IllegalArgumentException("Type "+type+" is not NodeBacked nor RelationshipBacked.");
}
@SuppressWarnings("unchecked")

View File

@@ -33,7 +33,7 @@ public class NodeEntityState<ENTITY extends NodeBacked> extends DefaultEntitySta
private final GraphDatabaseContext graphDatabaseContext;
public NodeEntityState(final Node underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory<NodeBacked> nodeDelegatingFieldAccessorFactory, Neo4JPersistentEntity<?> persistentEntity) {
public NodeEntityState(final Node underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory<NodeBacked> nodeDelegatingFieldAccessorFactory, Neo4JPersistentEntity<ENTITY> persistentEntity) {
super(underlyingState, entity, type, nodeDelegatingFieldAccessorFactory,persistentEntity);
this.graphDatabaseContext = graphDatabaseContext;
}

View File

@@ -47,7 +47,7 @@ public class NodeEntityStateFactory {
final NodeEntity graphEntityAnnotation = entityType.getAnnotation(NodeEntity.class); // todo cache ??
final Neo4JPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(entityType);
if (graphEntityAnnotation.partial()) {
final PartialNodeEntityState<NodeBacked> partialNodeEntityState = new PartialNodeEntityState<NodeBacked>(null, entity, entityType, graphDatabaseContext, getPersistenceUnitUtils(), delegatingFieldAccessorFactory, persistentEntity);
final PartialNodeEntityState<NodeBacked> partialNodeEntityState = new PartialNodeEntityState<NodeBacked>(null, entity, entityType, graphDatabaseContext, getPersistenceUnitUtils(), delegatingFieldAccessorFactory, (Neo4JPersistentEntity<NodeBacked>) persistentEntity);
return new DetachedEntityState<NodeBacked, Node>(partialNodeEntityState, graphDatabaseContext) {
@Override
protected boolean isDetached() {
@@ -55,7 +55,7 @@ public class NodeEntityStateFactory {
}
};
} else {
NodeEntityState<NodeBacked> nodeEntityState = new NodeEntityState<NodeBacked>(null, entity, entityType, graphDatabaseContext, nodeDelegatingFieldAccessorFactory, persistentEntity);
NodeEntityState<NodeBacked> nodeEntityState = new NodeEntityState<NodeBacked>(null, entity, entityType, graphDatabaseContext, nodeDelegatingFieldAccessorFactory, (Neo4JPersistentEntity<NodeBacked>) persistentEntity);
// alternative was return new NestedTransactionEntityState<NodeBacked, Node>(nodeEntityState,graphDatabaseContext);
return new DetachedEntityState<NodeBacked, Node>(nodeEntityState, graphDatabaseContext);
}

View File

@@ -26,6 +26,7 @@ import org.springframework.data.neo4j.annotation.RelatedTo;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.fieldaccess.*;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import javax.persistence.PersistenceUnitUtil;
@@ -45,7 +46,7 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
private final GraphDatabaseContext graphDatabaseContext;
private PersistenceUnitUtil persistenceUnitUtil;
public PartialNodeEntityState(final Node underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final GraphDatabaseContext graphDatabaseContext, PersistenceUnitUtil persistenceUnitUtil, final PartialNodeDelegatingFieldAccessorFactory delegatingFieldAccessorFactory, final Neo4JPersistentEntity<?> persistentEntity) {
public PartialNodeEntityState(final Node underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final GraphDatabaseContext graphDatabaseContext, PersistenceUnitUtil persistenceUnitUtil, final PartialNodeDelegatingFieldAccessorFactory delegatingFieldAccessorFactory, final Neo4JPersistentEntity<ENTITY> persistentEntity) {
super(underlyingState, entity, type, delegatingFieldAccessorFactory, persistentEntity);
this.graphDatabaseContext = graphDatabaseContext;
this.persistenceUnitUtil = persistenceUnitUtil;
@@ -86,7 +87,7 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
@Override
public boolean isWritable(Field field) {
final FieldAccessor<ENTITY> accessor = accessorFor(field);
final FieldAccessor<ENTITY> accessor = accessorFor(property(field));
if (accessor == null) return false; // difference to default behaviour, we don't care for non-managed fields here
return accessor.isWriteable(entity);
}
@@ -125,8 +126,8 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
newPropertyFieldAccessorFactory(),
newConvertingNodePropertyFieldAccessorFactory()) {
@Override
public boolean accept(Field f) {
return f.isAnnotationPresent(GraphProperty.class) && super.accept(f);
public boolean accept(Neo4JPersistentProperty property) {
return property.isAnnotationPresent(GraphProperty.class) && super.accept(property);
}
},
new JpaIdFieldAccessListenerFactory());
@@ -143,8 +144,8 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
newConvertingNodePropertyFieldAccessorFactory(),
new SingleRelationshipFieldAccessorFactory(getGraphDatabaseContext()) {
@Override
public boolean accept(Field f) {
return f.isAnnotationPresent(RelatedTo.class) && super.accept(f);
public boolean accept(Neo4JPersistentProperty property) {
return property.isAnnotationPresent(RelatedTo.class) && super.accept(property);
}
},
new OneToNRelationshipFieldAccessorFactory(getGraphDatabaseContext()),
@@ -156,7 +157,7 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
private ConvertingNodePropertyFieldAccessorFactory newConvertingNodePropertyFieldAccessorFactory() {
return new ConvertingNodePropertyFieldAccessorFactory(getGraphDatabaseContext().getConversionService()) {
@Override
public boolean accept(Field f) {
public boolean accept(Neo4JPersistentProperty f) {
return f.isAnnotationPresent(GraphProperty.class) && super.accept(f);
}
};
@@ -165,7 +166,7 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
private PropertyFieldAccessorFactory newPropertyFieldAccessorFactory() {
return new PropertyFieldAccessorFactory(getGraphDatabaseContext().getConversionService()) {
@Override
public boolean accept(Field f) {
public boolean accept(Neo4JPersistentProperty f) {
return f.isAnnotationPresent(GraphProperty.class) && super.accept(f);
}
};

View File

@@ -34,7 +34,7 @@ public class RelationshipEntityState<ENTITY extends RelationshipBacked> extends
private final GraphDatabaseContext graphDatabaseContext;
public RelationshipEntityState(final Relationship underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory<RelationshipBacked> delegatingFieldAccessorFactory, Neo4JPersistentEntity<?> persistentEntity) {
public RelationshipEntityState(final Relationship underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory<RelationshipBacked> delegatingFieldAccessorFactory, Neo4JPersistentEntity<ENTITY> persistentEntity) {
super(underlyingState, entity, type, delegatingFieldAccessorFactory, persistentEntity);
this.graphDatabaseContext = graphDatabaseContext;
}

View File

@@ -21,6 +21,7 @@ import org.springframework.data.neo4j.core.EntityState;
import org.springframework.data.neo4j.core.RelationshipBacked;
import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory;
import org.springframework.data.neo4j.mapping.Neo4JMappingContext;
import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
public class RelationshipEntityStateFactory {
@@ -32,7 +33,7 @@ public class RelationshipEntityStateFactory {
public EntityState<RelationshipBacked, Relationship> getEntityState(final RelationshipBacked entity) {
final Class<? extends RelationshipBacked> entityType = entity.getClass();
return new RelationshipEntityState<RelationshipBacked>(null,entity, entityType, graphDatabaseContext, relationshipDelegatingFieldAccessorFactory,mappingContext.getPersistentEntity(entityType));
return new RelationshipEntityState<RelationshipBacked>(null,entity, entityType, graphDatabaseContext, relationshipDelegatingFieldAccessorFactory, (Neo4JPersistentEntity<RelationshipBacked>) mappingContext.getPersistentEntity(entityType));
}
public void setGraphDatabaseContext(GraphDatabaseContext graphDatabaseContext) {

View File

@@ -24,8 +24,8 @@ import org.springframework.data.neo4j.annotation.*;
import org.springframework.data.neo4j.core.Direction;
import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder;
import org.springframework.data.neo4j.core.NodeBacked;
import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty;
import java.lang.reflect.Field;
import java.util.Collection;
@NodeEntity
@@ -114,7 +114,7 @@ public class Group {
private static class PeopleTraversalBuilder implements FieldTraversalDescriptionBuilder {
@Override
public TraversalDescription build(NodeBacked start, Field field, String...params) {
public TraversalDescription build(NodeBacked start, Neo4JPersistentProperty property, String...params) {
return new TraversalDescriptionImpl()
.relationships(DynamicRelationshipType.withName(params[0]))
.filter(Traversal.returnAllButStartNode());

View File

@@ -30,9 +30,13 @@ import org.springframework.data.neo4j.*;
import org.springframework.data.neo4j.Person;
import org.springframework.data.neo4j.PersonRepository;
import org.springframework.data.neo4j.support.node.Neo4jHelper;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
import java.util.Map;
@@ -47,6 +51,7 @@ import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:repository-namespace-config-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class GraphRepositoryTest {
protected final Log log = LogFactory.getLog(getClass());