refactored FieldAccessors
This commit is contained in:
@@ -8,6 +8,9 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.datastore.graph.api.NodeBacked;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Michael Hunger
|
||||
* @since 11.09.2010
|
||||
@@ -27,16 +30,25 @@ public abstract class AbstractFieldAccessor implements FieldAccessor {
|
||||
|
||||
protected NodeBacked createSingleRelationship(NodeBacked entity, NodeBacked target) {
|
||||
if (target==null) return null;
|
||||
Node entityNode = entity.getUnderlyingNode();
|
||||
Node targetNode = target.getUnderlyingNode();
|
||||
switch(direction) {
|
||||
case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break;
|
||||
case INCOMING : targetNode.createRelationshipTo(entityNode, type); break;
|
||||
default : throw new IllegalArgumentException("invalid direction " + direction);
|
||||
case OUTGOING : {
|
||||
obtainSingleRelationship(entity.getUnderlyingNode(), target.getUnderlyingNode());
|
||||
break;
|
||||
}
|
||||
case INCOMING :
|
||||
obtainSingleRelationship(target.getUnderlyingNode(), entity.getUnderlyingNode());
|
||||
break;
|
||||
default : throw new InvalidDataAccessApiUsageException("invalid direction " + direction);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private Relationship obtainSingleRelationship(Node start, Node end) {
|
||||
final Relationship existingRelationship = start.getSingleRelationship(type, direction);
|
||||
if (existingRelationship!=null && existingRelationship.getOtherNode(start).equals(end)) return existingRelationship;
|
||||
return start.createRelationshipTo(end, type);
|
||||
}
|
||||
|
||||
protected void checkCircularReference(NodeBacked entity, NodeBacked target) {
|
||||
Node entityNode = entity.getUnderlyingNode();
|
||||
Node targetNode = target.getUnderlyingNode();
|
||||
@@ -90,4 +102,65 @@ public abstract class AbstractFieldAccessor implements FieldAccessor {
|
||||
private Iterable<Relationship> getRelationships(final NodeBacked entity) {
|
||||
return entity.getUnderlyingNode().getRelationships(type, direction);
|
||||
}
|
||||
|
||||
protected void removeMissingRelationships(NodeBacked entity, Set<NodeBacked> target) {
|
||||
Set<Node> newNodes = extractNodes(target);
|
||||
Node entityNode = entity.getUnderlyingNode();
|
||||
for ( Relationship relationship : entityNode.getRelationships(type, direction) ) {
|
||||
if (!newNodes.remove(relationship.getOtherNode(entityNode)))
|
||||
relationship.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Node> extractNodes(Set<NodeBacked> target) {
|
||||
Set<Node> newNodes=new HashSet<Node>();
|
||||
for (NodeBacked nodeBacked : target) {
|
||||
newNodes.add(nodeBacked.getUnderlyingNode());
|
||||
}
|
||||
return newNodes;
|
||||
}
|
||||
|
||||
protected void createNewRelationshipsFrom(NodeBacked entity, Set<NodeBacked> target) {
|
||||
for (NodeBacked nodeBacked : target) {
|
||||
createSingleRelationship(entity,nodeBacked);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkNoCircularReference(NodeBacked entity, Set<NodeBacked> target) {
|
||||
if (target.contains(entity)) throw new InvalidDataAccessApiUsageException("Cannot create a circular reference to "+target);
|
||||
}
|
||||
|
||||
protected Set<NodeBacked> checkTargetIsSetOfNodebacked(Object newVal) {
|
||||
if (!(newVal instanceof Set)) {
|
||||
throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass());
|
||||
}
|
||||
for (Object value : (Set<Object>) newVal) {
|
||||
if (!(value instanceof NodeBacked)) {
|
||||
throw new IllegalArgumentException("New value elements must be NodeBacked.");
|
||||
}
|
||||
}
|
||||
return (Set<NodeBacked>) newVal;
|
||||
}
|
||||
|
||||
protected ManagedFieldAccessorSet<NodeBacked> createManagedSet(NodeBacked entity, Set<NodeBacked> result) {
|
||||
return new ManagedFieldAccessorSet<NodeBacked>(entity, result, this);
|
||||
}
|
||||
|
||||
protected Set<NodeBacked> createEntitySetFromRelationshipEndNodes(NodeBacked entity) {
|
||||
final Set<Node> nodes = getStatesFromEntity(entity);
|
||||
final Set<NodeBacked> result = new HashSet<NodeBacked>();
|
||||
for (final Node otherNode : nodes) {
|
||||
result.add(graphEntityInstantiator.createEntityFromState(otherNode, relatedType));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Set<Node> getStatesFromEntity(NodeBacked entity) {
|
||||
final Node entityNode = entity.getUnderlyingNode();
|
||||
final Set<Node> result = new HashSet<Node>();
|
||||
for (final Relationship rel : entityNode.getRelationships(type, direction)) {
|
||||
result.add(rel.getOtherNode(entityNode));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,24 +25,19 @@ public class FieldAccessorFactory {
|
||||
GraphEntityRelationship relAnnotation = field.getAnnotation(GraphEntityRelationship.class);
|
||||
if (isSingleRelationshipField(field)) {
|
||||
if (relAnnotation != null) {
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(relAnnotation),
|
||||
dirFrom(relAnnotation), targetFrom(field), graphEntityInstantiator);
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphEntityInstantiator);
|
||||
}
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(field),
|
||||
Direction.OUTGOING, targetFrom(field), graphEntityInstantiator);
|
||||
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field), graphEntityInstantiator);
|
||||
}
|
||||
if (isOneToNRelationshipField(field)) {
|
||||
return new OneToNRelationshipFieldAccessor(typeFrom(relAnnotation),
|
||||
dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
|
||||
return new OneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
|
||||
}
|
||||
if (isReadOnlyOneToNRelationshipField(field)) {
|
||||
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(relAnnotation),
|
||||
dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
|
||||
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
|
||||
}
|
||||
if (isOneToNRelationshipEntityField(field)) {
|
||||
GraphEntityRelationshipEntity relEntityAnnotation = field.getAnnotation(GraphEntityRelationshipEntity.class);
|
||||
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation),
|
||||
dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), relationshipEntityInstantiator);
|
||||
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), relationshipEntityInstantiator);
|
||||
}
|
||||
throw new IllegalArgumentException("Not a Neo4j relationship field: " + field);
|
||||
}
|
||||
@@ -112,8 +107,9 @@ public class FieldAccessorFactory {
|
||||
|
||||
public static String getNeo4jPropertyName(Field field) {
|
||||
final Class<?> entityClass = field.getDeclaringClass();
|
||||
return useShortNames(entityClass) ? field.getName() : String.format("%s.%s", entityClass.getSimpleName(),field.getName());
|
||||
}
|
||||
if (useShortNames(entityClass)) return field.getName();
|
||||
return String.format("%s.%s", entityClass.getSimpleName(), field.getName());
|
||||
}
|
||||
|
||||
private static boolean useShortNames(Class<?> entityClass) {
|
||||
final GraphEntity graphEntity = entityClass.getAnnotation(GraphEntity.class);
|
||||
|
||||
@@ -11,31 +11,38 @@ import org.springframework.datastore.graph.api.NodeBacked;
|
||||
import org.springframework.datastore.graph.api.RelationshipBacked;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
|
||||
public class OneToNRelationshipEntityFieldAccessor implements FieldAccessor {
|
||||
public class OneToNRelationshipEntityFieldAccessor extends AbstractFieldAccessor {
|
||||
|
||||
private final RelationshipType type;
|
||||
private final Direction direction;
|
||||
private final Class<? extends RelationshipBacked> elementClass;
|
||||
private final EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
|
||||
|
||||
public OneToNRelationshipEntityFieldAccessor(RelationshipType type, Direction direction, Class<? extends RelationshipBacked> elementClass, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
|
||||
this.type = type;
|
||||
this.direction = direction;
|
||||
public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends RelationshipBacked> elementClass, final EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
|
||||
super(null,null,direction,type);
|
||||
this.elementClass = elementClass;
|
||||
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(NodeBacked entity, Object newVal) {
|
||||
public Object setValue(final NodeBacked entity, final Object newVal) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(NodeBacked entity) {
|
||||
Set<RelationshipBacked> result = new HashSet<RelationshipBacked>();
|
||||
for (Relationship rel : entity.getUnderlyingNode().getRelationships(type, direction)) {
|
||||
result.add(relationshipEntityInstantiator.createEntityFromState(rel, elementClass));
|
||||
}
|
||||
public Object getValue(final NodeBacked entity) {
|
||||
checkUnderlyingNode(entity);
|
||||
final Set<RelationshipBacked> result = createEntitySetFromRelationships(entity);
|
||||
return new ManagedFieldAccessorSet<RelationshipBacked>(entity, result, this);
|
||||
}
|
||||
|
||||
private Set<RelationshipBacked> createEntitySetFromRelationships(final NodeBacked entity) {
|
||||
final Set<RelationshipBacked> result = new HashSet<RelationshipBacked>();
|
||||
for (final Relationship rel : getStatesFromEntity(entity)) {
|
||||
result.add(relationshipEntityInstantiator.createEntityFromState(rel, elementClass));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Iterable<Relationship> getStatesFromEntity(NodeBacked entity) {
|
||||
return entity.getUnderlyingNode().getRelationships(type, direction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +1,39 @@
|
||||
package org.springframework.datastore.graph.neo4j.fieldaccess;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.datastore.graph.api.NodeBacked;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
|
||||
public class OneToNRelationshipFieldAccessor implements FieldAccessor {
|
||||
public class OneToNRelationshipFieldAccessor extends AbstractFieldAccessor {
|
||||
|
||||
private final RelationshipType type;
|
||||
private final Direction direction;
|
||||
private final Class<? extends NodeBacked> relatedType;
|
||||
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
|
||||
|
||||
public OneToNRelationshipFieldAccessor(RelationshipType type, Direction direction, Class<? extends NodeBacked> elementClass, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
|
||||
this.type = type;
|
||||
this.direction = direction;
|
||||
this.relatedType = elementClass;
|
||||
this.graphEntityInstantiator = graphEntityInstantiator;
|
||||
public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
|
||||
super(elementClass, graphEntityInstantiator, direction, type);
|
||||
}
|
||||
|
||||
public Object setValue(final NodeBacked entity, final Object newVal) {
|
||||
Node entityNode = entity.getUnderlyingNode();
|
||||
|
||||
Set<Node> newNodes=new HashSet<Node>();
|
||||
if (newVal != null) {
|
||||
if (!(newVal instanceof Set)) {
|
||||
throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass());
|
||||
}
|
||||
Set<Object> set = (Set<Object>) newVal;
|
||||
for (Object obj : set) {
|
||||
if (!(obj instanceof NodeBacked)) {
|
||||
throw new IllegalArgumentException("New value elements must be NodeBacked.");
|
||||
}
|
||||
Node newNode=((NodeBacked) obj).getUnderlyingNode();
|
||||
if (entityNode.equals(newNode)) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot create circular reference.");
|
||||
}
|
||||
newNodes.add(newNode);
|
||||
}
|
||||
}
|
||||
for ( Relationship relationship : entityNode.getRelationships(type, direction) ) {
|
||||
if (!newNodes.remove(relationship.getOtherNode(entityNode)))
|
||||
relationship.delete();
|
||||
}
|
||||
if (newVal == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Node newNode : newNodes) {
|
||||
switch(direction) {
|
||||
case OUTGOING : entityNode.createRelationshipTo(newNode, type); break;
|
||||
case INCOMING : newNode.createRelationshipTo(entityNode, type); break;
|
||||
default : throw new IllegalArgumentException("invalid direction " + direction);
|
||||
}
|
||||
}
|
||||
return new ManagedFieldAccessorSet<NodeBacked>(entity, newVal, this);
|
||||
checkUnderlyingNode(entity);
|
||||
if (newVal==null) {
|
||||
removeMissingRelationships(entity, Collections.<NodeBacked>emptySet());
|
||||
return null;
|
||||
}
|
||||
final Set<NodeBacked> target = checkTargetIsSetOfNodebacked(newVal);
|
||||
checkNoCircularReference(entity,target);
|
||||
removeMissingRelationships(entity, target);
|
||||
createNewRelationshipsFrom(entity,target);
|
||||
return createManagedSet(entity, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(NodeBacked entity) {
|
||||
Node entityNode = entity.getUnderlyingNode();
|
||||
if (entityNode == null) {
|
||||
throw new IllegalStateException("Entity must have a backing Node");
|
||||
}
|
||||
Set<NodeBacked> result = new HashSet<NodeBacked>();
|
||||
for (Relationship rel : entityNode.getRelationships(type, direction)) {
|
||||
result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), relatedType));
|
||||
}
|
||||
return new ManagedFieldAccessorSet<NodeBacked>(entity, result, this);
|
||||
|
||||
@Override
|
||||
public Object getValue(final NodeBacked entity) {
|
||||
checkUnderlyingNode(entity);
|
||||
final Set<NodeBacked> result = createEntitySetFromRelationshipEndNodes(entity);
|
||||
return createManagedSet(entity, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -11,35 +11,13 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.datastore.graph.api.NodeBacked;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
|
||||
public class ReadOnlyOneToNRelationshipFieldAccessor implements FieldAccessor {
|
||||
|
||||
private final RelationshipType type;
|
||||
private final Direction direction;
|
||||
private final Class<? extends NodeBacked> relatedType;
|
||||
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
|
||||
public class ReadOnlyOneToNRelationshipFieldAccessor extends OneToNRelationshipFieldAccessor {
|
||||
|
||||
public ReadOnlyOneToNRelationshipFieldAccessor(RelationshipType type, Direction direction, Class<? extends NodeBacked> elementClass, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
|
||||
this.type = type;
|
||||
this.direction = direction;
|
||||
this.relatedType = elementClass;
|
||||
this.graphEntityInstantiator = graphEntityInstantiator;
|
||||
super(type,direction,elementClass,graphEntityInstantiator);
|
||||
}
|
||||
|
||||
public Object setValue(final NodeBacked entity, final Object newVal) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(NodeBacked entity) {
|
||||
Node entityNode = entity.getUnderlyingNode();
|
||||
if (entityNode == null) {
|
||||
throw new IllegalStateException("Entity must have a backing Node");
|
||||
}
|
||||
Set<NodeBacked> result = new HashSet<NodeBacked>();
|
||||
for (Relationship rel : entityNode.getRelationships(type, direction)) {
|
||||
result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), relatedType));
|
||||
}
|
||||
return new ManagedFieldAccessorSet<NodeBacked>(entity, result, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import org.neo4j.graphdb.RelationshipType;
|
||||
import org.springframework.datastore.graph.api.NodeBacked;
|
||||
import org.springframework.persistence.support.EntityInstantiator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class SingleRelationshipFieldAccessor extends AbstractFieldAccessor {
|
||||
public SingleRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> clazz, final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
|
||||
super(clazz, graphEntityInstantiator, direction, type);
|
||||
@@ -15,20 +18,20 @@ public class SingleRelationshipFieldAccessor extends AbstractFieldAccessor {
|
||||
public Object setValue(final NodeBacked entity, final Object newVal) {
|
||||
checkUnderlyingNode(entity);
|
||||
if (newVal == null) {
|
||||
removeRelationships(entity);
|
||||
removeMissingRelationships(entity, Collections.<NodeBacked>emptySet());
|
||||
return null;
|
||||
}
|
||||
|
||||
final NodeBacked target=checkTargetTypeNodebacked(newVal);
|
||||
if (isExistingRelationship(entity, target)) return target;
|
||||
checkCircularReference(entity, target);
|
||||
removeRelationships(entity);
|
||||
return createSingleRelationship(entity, target);
|
||||
final Set<NodeBacked> target=checkTargetIsSetOfNodebacked(Collections.singleton(newVal));
|
||||
checkNoCircularReference(entity,target);
|
||||
removeMissingRelationships(entity, target);
|
||||
createNewRelationshipsFrom(entity,target);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final NodeBacked entity) {
|
||||
checkUnderlyingNode(entity);
|
||||
return createEntityFromRelationshipEndNode(entity.getUnderlyingNode());
|
||||
final Set<NodeBacked> result = createEntitySetFromRelationshipEndNodes(entity);
|
||||
return result.isEmpty() ? null : result.iterator().next();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user