Split the type representation strategy into one for rels and one for nodes (via a generic base interface). Moved all entity creation into the type strategies. Next step is to add full support for relationship finders.

This commit is contained in:
David Montag
2011-03-30 00:26:36 -07:00
parent ed4dde0d68
commit 29edd30b12
28 changed files with 1450 additions and 1051 deletions

View File

@@ -18,7 +18,7 @@ log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
log4j.category.org.springframework=WARN
#log4j.category.org.springframework.data.graph.neo4j.support.SubReferenceTypeRepresentationStrategy=DEBUG
#log4j.category.org.springframework.data.graph.neo4j.support.SubReferenceNodeTypeRepresentationStrategytegy=DEBUG
#log4j.category.org.springframework.data.graph.neo4j.fieldaccess=DEBUG
#log4j.category.org.springframework.data=TRACE
#log4j.category.org.springframework.data.support=TRACE

View File

@@ -90,22 +90,24 @@
<bean id="graphDatabaseContext" class="org.springframework.data.graph.neo4j.support.GraphDatabaseContext">
<property name="graphDatabaseService" ref="graphDatabaseService"/>
<property name="relationshipEntityInstantiator" ref="relationshipEntityInstantiator"/>
<property name="graphEntityInstantiator" ref="graphEntityInstantiator"/>
<property name="conversionService">
<bean class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
</property>
<property name="typeRepresentationStrategy" ref="typeRepresentationStrategy"/>
<property name="nodeTypeRepresentationStrategy" ref="nodeTypeRepresentationStrategy" />
<property name="relationshipTypeRepresentationStrategy" ref="relationshipTypeRepresentationStrategy" />
</bean>
<bean id="relationshipEntityInstantiator"
class="org.springframework.data.graph.neo4j.support.relationship.ConstructorBypassingGraphRelationshipInstantiator"/>
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactoryBean">
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactory">
<constructor-arg ref="graphDatabaseService"/>
<constructor-arg ref="graphEntityInstantiator"/>
<constructor-arg ref="relationshipEntityInstantiator"/>
</bean>
<bean id="nodeTypeRepresentationStrategy" factory-bean="typeRepresentationStrategyFactory" factory-method="getNodeTypeRepresentationStrategy" />
<bean id="relationshipTypeRepresentationStrategy" factory-bean="typeRepresentationStrategyFactory" factory-method="getRelationshipTypeRepresentationStrategy"/>
<bean id="graphEntityInstantiator"
class="org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator"/>

View File

@@ -0,0 +1,7 @@
package org.springframework.data.graph.core;
import org.neo4j.graphdb.Node;
public interface NodeTypeRepresentationStrategy extends TypeRepresentationStrategy<Node, NodeBacked> {
}

View File

@@ -0,0 +1,7 @@
package org.springframework.data.graph.core;
import org.neo4j.graphdb.Relationship;
public interface RelationshipTypeRepresentationStrategy extends TypeRepresentationStrategy<Relationship, RelationshipBacked> {
}

View File

@@ -29,47 +29,70 @@ import org.neo4j.graphdb.PropertyContainer;
* @author Michael Hunger
* @since 13.09.2010
*/
public interface TypeRepresentationStrategy {
public interface TypeRepresentationStrategy<S extends PropertyContainer, T extends GraphBacked<S>> {
/**
* callback on entity creation for setting up type representation
* @param entity
* @param state
* @param type
*/
void postEntityCreation(GraphBacked<?> entity);
void postEntityCreation(S state, Class<? extends T> type);
/**
* @param clazz Type whose instances should be iterated over
* @param <T> Type parameter for generified return value
* @param <U> Type parameter for generified return value
* @return lazy Iterable over all instances of the given type
*/
<T extends GraphBacked<?>> Iterable<T> findAll(final Class<T> clazz);
<U extends T> Iterable<U> findAll(final Class<U> clazz);
/**
* @param entityClass
* @return number of instances of this class contained in the graph
*/
long count(final Class<? extends GraphBacked<?>> entityClass);
long count(final Class<? extends T> entityClass);
/**
* @param primitive
* @param <T>
* @param state
* @return java type that of the node entity of this node
*/
<T extends GraphBacked<?>> Class<T> getJavaType(PropertyContainer primitive);
<U extends T> Class<U> getJavaType(S state);
/**
* callback for lifecycle management before node entity removal
* @param entity
*/
void preEntityRemoval(GraphBacked<?> entity);
void preEntityRemoval(T entity);
/**
* Instantiate the entity given its state. The type of the entity is inferred by the strategy
* from the state.
*
* @param node
* @param type
* @param <T>
* @throws IllegalArgumentException if the specified type did not match the stored one
* @throws IllegalStateException if the primitive has no type stored
* @return Concrete type for primitive, or throws exception
* @param state Backing state of entity to be instantiated
* @param <U> Helper parameter for castless use
* @throws IllegalStateException If the strategy is unable to infer any type from the state
* @return Entity instance
*/
<T extends GraphBacked<?>> Class<T> confirmType(PropertyContainer node, Class<T> type);
<U extends T> U createEntity(S state) throws IllegalStateException;
/**
* Instantiate the entity given its state. The type of the desired entity is also specified.
* If the type is not compatible with what the strategy can infer from the state,
* {@link java.lang.IllegalArgumentException} is thrown.
*
* @param state Backing state of entity to be instantiated
* @param type Type of entity to be instantiated
* @throws IllegalStateException If the strategy is unable to infer any type from the state
* @throws IllegalArgumentException If the specified type does not match the inferred type
* @return Entity instance
*/
<U extends T> U createEntity(S state, Class<U> type) throws IllegalStateException, IllegalArgumentException;
/**
* Instantiate the entity of the given type, with the given state as backing state. No checking
* is done by the strategy.
*
* @param state Backing state of entity to be instantiated
* @param type Type of entity to be instantiated
* @return Entity instance.
*/
<U extends T> U projectEntity(S state, Class<U> type);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.graph.neo4j.config;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.kernel.impl.transaction.SpringTransactionManager;
import org.neo4j.kernel.impl.transaction.UserTransactionImpl;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,21 +27,22 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean;
import org.springframework.data.graph.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory;
import org.springframework.data.graph.neo4j.fieldaccess.NodeEntityStateFactory;
import org.springframework.data.graph.neo4j.fieldaccess.RelationshipEntityStateFactory;
import org.springframework.data.graph.neo4j.repository.DirectGraphRepositoryFactory;
import org.springframework.data.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactoryBean;
import org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactory;
import org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator;
import org.springframework.data.graph.neo4j.support.node.Neo4jNodeBacking;
import org.springframework.data.graph.neo4j.support.node.PartialNeo4jEntityInstantiator;
import org.springframework.data.graph.neo4j.support.relationship.ConstructorBypassingGraphRelationshipInstantiator;
import org.springframework.data.graph.neo4j.support.relationship.Neo4jRelationshipBacking;
import org.springframework.data.graph.neo4j.transaction.ChainedTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.data.persistence.EntityInstantiator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
@@ -88,16 +90,17 @@ public class Neo4jConfiguration {
@Bean
public GraphDatabaseContext graphDatabaseContext() throws Exception {
GraphDatabaseContext gdc = new GraphDatabaseContext();
gdc.setGraphDatabaseService(getGraphDatabaseService());
ConstructorBypassingGraphRelationshipInstantiator relationshipEntityInstantiator = graphRelationshipInstantiator();
gdc.setRelationshipEntityInstantiator(relationshipEntityInstantiator);
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator = graphEntityInstantiator();
gdc.setGraphEntityInstantiator(graphEntityInstantiator);
gdc.setConversionService(conversionService());
TypeRepresentationStrategyFactoryBean typeRepresentationStrategyFactoryBean =
new TypeRepresentationStrategyFactoryBean(graphDatabaseService, graphEntityInstantiator, relationshipEntityInstantiator);
gdc.setTypeRepresentationStrategy(typeRepresentationStrategyFactoryBean.getObject());
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator = graphRelationshipInstantiator();
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator = graphEntityInstantiator();
TypeRepresentationStrategyFactory typeRepresentationStrategyFactory =
new TypeRepresentationStrategyFactory(graphDatabaseService, graphEntityInstantiator, relationshipEntityInstantiator);
GraphDatabaseContext gdc = new GraphDatabaseContext();
gdc.setGraphDatabaseService(getGraphDatabaseService());
gdc.setConversionService(conversionService());
gdc.setNodeTypeRepresentationStrategy(typeRepresentationStrategyFactory.getNodeTypeRepresentationStrategy());
gdc.setRelationshipTypeRepresentationStrategy(typeRepresentationStrategyFactory.getRelationshipTypeRepresentationStrategy());
if (validator!=null) {
gdc.setValidator(validator);
}

View File

@@ -54,7 +54,7 @@ public class NodeEntityState<ENTITY extends NodeBacked> extends DefaultEntitySta
final Node node = graphDatabaseContext.createNode();
setPersistentState(node);
if (log.isInfoEnabled()) log.info("User-defined constructor called on class " + entity.getClass() + "; created Node [" + getPersistentState() + "]; Updating metamodel");
graphDatabaseContext.postEntityCreation(entity);
graphDatabaseContext.postEntityCreation(node, type);
} catch (NotInTransactionException e) {
throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e);
}

View File

@@ -119,7 +119,7 @@ public class PartialNodeEntityState<ENTITY extends NodeBacked> extends DefaultEn
persistForeignId(node, id);
setPersistentState(node);
log.info("User-defined constructor called on class " + entity.getClass() + "; created Node [" + entity.getPersistentState() + "]; Updating metamodel");
graphDatabaseContext.postEntityCreation(entity);
graphDatabaseContext.postEntityCreation(node, type);
} else {
setPersistentState(node);
entity.setPersistentState(node);

View File

@@ -25,11 +25,7 @@ import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
import org.neo4j.kernel.AbstractGraphDatabase;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.annotation.Indexed;
import org.springframework.data.graph.core.GraphBacked;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.TypeRepresentationStrategy;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.persistence.EntityInstantiator;
import org.springframework.data.graph.core.*;
import javax.transaction.Status;
import javax.transaction.SystemException;
@@ -53,13 +49,10 @@ public class GraphDatabaseContext {
private GraphDatabaseService graphDatabaseService;
public EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
public EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
private ConversionService conversionService;
private TypeRepresentationStrategy typeRepresentationStrategy;
private NodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
private RelationshipTypeRepresentationStrategy relationshipTypeRepresentationStrategy;
private Validator validator;
@@ -73,25 +66,23 @@ public class GraphDatabaseContext {
this.graphDatabaseService = graphDatabaseService;
}
public EntityInstantiator<NodeBacked, Node> getGraphEntityInstantiator() {
return graphEntityInstantiator;
}
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy() {
return nodeTypeRepresentationStrategy;
}
public void setGraphEntityInstantiator(
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
this.graphEntityInstantiator = graphEntityInstantiator;
}
public void setNodeTypeRepresentationStrategy(NodeTypeRepresentationStrategy nodeTypeRepresentationStrategy) {
this.nodeTypeRepresentationStrategy = nodeTypeRepresentationStrategy;
}
public EntityInstantiator<RelationshipBacked, Relationship> getRelationshipEntityInstantiator() {
return relationshipEntityInstantiator;
}
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy() {
return relationshipTypeRepresentationStrategy;
}
public void setRelationshipEntityInstantiator(
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
}
public void setRelationshipTypeRepresentationStrategy(RelationshipTypeRepresentationStrategy relationshipTypeRepresentationStrategy) {
this.relationshipTypeRepresentationStrategy = relationshipTypeRepresentationStrategy;
}
public ConversionService getConversionService() {
public ConversionService getConversionService() {
return conversionService;
}
@@ -99,14 +90,6 @@ public class GraphDatabaseContext {
this.conversionService = conversionService;
}
public TypeRepresentationStrategy getTypeRepresentationStrategy() {
return typeRepresentationStrategy;
}
public void setTypeRepresentationStrategy(TypeRepresentationStrategy typeRepresentationStrategy) {
this.typeRepresentationStrategy = typeRepresentationStrategy;
}
public Node createNode() {
return graphDatabaseService.createNode();
}
@@ -136,16 +119,18 @@ public class GraphDatabaseContext {
* but all modifications will throw an exception
* @param entity to remove
*/
// TODO: What about connected relationship entities?
public void removeNodeEntity(NodeBacked entity) {
Node node = entity.getPersistentState();
if (node==null) return;
this.typeRepresentationStrategy.preEntityRemoval(entity);
nodeTypeRepresentationStrategy.preEntityRemoval(entity);
for (Relationship relationship : node.getRelationships()) {
removeRelationship(relationship);
}
removeFromIndexes(node);
node.delete();
}
public void removeRelationshipEntity(RelationshipBacked entity) {
Relationship relationship = entity.getPersistentState();
if (relationship==null) return;
@@ -170,10 +155,12 @@ public class GraphDatabaseContext {
*/
public <S, T extends GraphBacked> T createEntityFromState(final S state, final Class<T> type) {
if (state==null) throw new IllegalArgumentException("state has to be either a Node or Relationship, not null");
if (state instanceof Node)
return (T) graphEntityInstantiator.createEntityFromState((Node) state, typeRepresentationStrategy.confirmType((Node)state, (Class<? extends NodeBacked>)type));
if (state instanceof Node && NodeBacked.class.isAssignableFrom(type))
return (T) nodeTypeRepresentationStrategy.createEntity((Node) state, (Class<NodeBacked>) type);
// return (T) graphEntityInstantiator.createEntityFromState((Node) state, typeRepresentationStrategy.confirmType((Node)state, (Class<? extends NodeBacked>)type));
else
return (T) relationshipEntityInstantiator.createEntityFromState((Relationship) state, (Class<? extends RelationshipBacked>) type);
return (T) relationshipTypeRepresentationStrategy.createEntity((Relationship) state, (Class<RelationshipBacked>) type);
// return (T) relationshipEntityInstantiator.createEntityFromState((Relationship) state, (Class<? extends RelationshipBacked>) type);
}
private IndexManager getIndexManager() {
@@ -215,10 +202,19 @@ public class GraphDatabaseContext {
/**
* delegates to the configured @{link TypeRepresentationStrategy} for after entity creation operations
* @param entity
* @param node
* @param entityClass
*/
public void postEntityCreation(final NodeBacked entity) {
typeRepresentationStrategy.postEntityCreation(entity);
public void postEntityCreation(Node node, final Class<? extends NodeBacked> entityClass) {
nodeTypeRepresentationStrategy.postEntityCreation(node, entityClass);
}
/**
* delegates to the configured @{link TypeRepresentationStrategy} for after entity creation operations
* @param relationship
* @param entityClass
*/
public void postEntityCreation(Relationship relationship, final Class<? extends RelationshipBacked> entityClass) {
relationshipTypeRepresentationStrategy.postEntityCreation(relationship, entityClass);
}
/**
@@ -230,7 +226,7 @@ public class GraphDatabaseContext {
*/
public <T extends GraphBacked> Iterable<T> findAll(final Class<T> clazz) {
if (!checkIsNodeBacked(clazz)) throw new UnsupportedOperationException("No support for relationships");
return (Iterable<T>) typeRepresentationStrategy.findAll((Class<NodeBacked>)clazz);
return (Iterable<T>) nodeTypeRepresentationStrategy.findAll((Class<NodeBacked>)clazz);
}
/**
@@ -247,7 +243,7 @@ public class GraphDatabaseContext {
*/
public long count(final Class<? extends GraphBacked> entityClass) {
if (!checkIsNodeBacked(entityClass)) throw new UnsupportedOperationException("No support for relationships");
return typeRepresentationStrategy.count((Class<NodeBacked>)entityClass);
return nodeTypeRepresentationStrategy.count((Class<NodeBacked>)entityClass);
}
/**
@@ -258,7 +254,7 @@ public class GraphDatabaseContext {
* @throws IllegalStateException for nodes that are not instance backing nodes of a known type
*/
public <T extends NodeBacked> Class<T> getJavaType(final Node node) {
return typeRepresentationStrategy.getJavaType(node);
return nodeTypeRepresentationStrategy.getJavaType(node);
}
/**
@@ -313,9 +309,9 @@ public class GraphDatabaseContext {
public <T extends GraphBacked> T projectTo(GraphBacked entity, Class<T> targetType) {
final Object state = entity.getPersistentState();
if (state instanceof Node)
return (T) graphEntityInstantiator.createEntityFromState((Node) state, (Class<? extends NodeBacked>) targetType);
return (T) nodeTypeRepresentationStrategy.projectEntity((Node) state, (Class<NodeBacked>) targetType);
else
return (T) relationshipEntityInstantiator.createEntityFromState((Relationship) state, (Class<? extends RelationshipBacked>) targetType);
return (T) relationshipTypeRepresentationStrategy.projectEntity((Relationship) state, (Class<RelationshipBacked>) targetType);
}
public Validator getValidator() {
@@ -327,7 +323,7 @@ public class GraphDatabaseContext {
}
public <T extends NodeBacked> T createEntityFromStoredType(Node node) {
return (T)graphEntityInstantiator.createEntityFromState(node, typeRepresentationStrategy.<NodeBacked>getJavaType(node));
return nodeTypeRepresentationStrategy.createEntity(node);
}
}

View File

@@ -0,0 +1,146 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.Predicate;
import org.neo4j.helpers.collection.FilteringIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.data.graph.core.GraphBacked;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.NodeTypeRepresentationStrategy;
import org.springframework.data.persistence.EntityInstantiator;
import java.util.HashMap;
import java.util.Map;
public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
public static final String INDEX_NAME = "__types__";
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
private EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private GraphDatabaseService graphDb;
private final Map<String,Class<?>> cache=new HashMap<String, Class<?>>();
public IndexingNodeTypeRepresentationStrategy(GraphDatabaseService graphDb,
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
this.graphDb = graphDb;
this.graphEntityInstantiator = graphEntityInstantiator;
}
private Index<Node> getNodeTypesIndex() {
return graphDb.index().forNodes(INDEX_NAME);
}
private Index<Relationship> getRelTypesIndex() {
return graphDb.index().forRelationships(INDEX_NAME);
}
@Override
public void postEntityCreation(Node state, Class<? extends NodeBacked> type) {
addToNodeTypesIndex(state, type);
state.setProperty(TYPE_PROPERTY_NAME, type.getName());
}
private void addToNodeTypesIndex(Node node, Class<? extends NodeBacked> entityClass) {
Class<?> klass = entityClass;
while (klass.getAnnotation(NodeEntity.class) != null) {
getNodeTypesIndex().add(node, INDEX_KEY, klass.getName());
klass = klass.getSuperclass();
}
}
@Override
public <U extends NodeBacked> Iterable<U> findAll(Class<U> clazz) {
return findAllNodeBacked(clazz);
}
private <ENTITY extends NodeBacked> Iterable<ENTITY> findAllNodeBacked(Class<ENTITY> clazz) {
final IndexHits<Node> allEntitiesOfType = getNodeTypesIndex().get(INDEX_KEY, clazz.getName());
return new FilteringIterable<ENTITY>(new IterableWrapper<ENTITY, Node>(allEntitiesOfType) {
@Override
@SuppressWarnings("unchecked")
protected ENTITY underlyingObjectToObject(Node node) {
Class<ENTITY> javaType = (Class<ENTITY>) getJavaType(node);
if (javaType == null) return null;
return graphEntityInstantiator.createEntityFromState(node, javaType);
}
}, new Predicate<ENTITY>() {
@Override
public boolean accept(ENTITY item) {
return item != null;
}
});
}
@Override
public long count(Class<? extends NodeBacked> entityClass) {
long count = 0;
for (Object o : getNodeTypesIndex().get(INDEX_KEY, entityClass.getName())) {
count += 1;
}
return count;
}
@Override
public Class<? extends NodeBacked> getJavaType(Node node) {
if (node == null) throw new IllegalArgumentException("Node is null");
String className = (String) node.getProperty(TYPE_PROPERTY_NAME);
return getClassForName(className);
}
@SuppressWarnings({"unchecked"})
private <ENTITY extends GraphBacked<?>> Class<ENTITY> getClassForName(String className) {
try {
Class<ENTITY> result= (Class<ENTITY>) cache.get(className);
if (result!=null) return result;
synchronized (cache) {
result= (Class<ENTITY>) cache.get(className);
if (result!=null) return result;
result = (Class<ENTITY>) Class.forName(className);
cache.put(className,result);
return result;
}
} catch (NotFoundException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
@Override
public void preEntityRemoval(NodeBacked entity) {
getNodeTypesIndex().remove(entity.getPersistentState());
}
@Override
@SuppressWarnings("unchecked")
public <U extends NodeBacked> U createEntity(Node state) {
Class<? extends NodeBacked> javaType = getJavaType(state);
if (javaType == null) {
throw new IllegalStateException("No type stored on node.");
}
return (U) graphEntityInstantiator.createEntityFromState(state, javaType);
}
@Override
@SuppressWarnings("unchecked")
public <U extends NodeBacked> U createEntity(Node state, Class<U> type) {
Class<? extends NodeBacked> javaType = getJavaType(state);
if (javaType == null) {
throw new IllegalStateException("No type stored on node.");
}
if (type.isAssignableFrom(javaType)) {
return (U) graphEntityInstantiator.createEntityFromState(state, javaType);
}
throw new IllegalArgumentException(String.format("Entity is not of type: %s (was %s)", type, javaType));
}
@Override
public <U extends NodeBacked> U projectEntity(Node state, Class<U> type) {
return graphEntityInstantiator.createEntityFromState(state, type);
}
}

View File

@@ -0,0 +1,151 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.Predicate;
import org.neo4j.helpers.collection.FilteringIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.springframework.data.graph.annotation.RelationshipEntity;
import org.springframework.data.graph.core.GraphBacked;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.core.RelationshipTypeRepresentationStrategy;
import org.springframework.data.persistence.EntityInstantiator;
import java.util.HashMap;
import java.util.Map;
public class IndexingRelationshipTypeRepresentationStrategy implements RelationshipTypeRepresentationStrategy {
public static final String INDEX_NAME = "__types__";
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
private EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
private GraphDatabaseService graphDb;
private final Map<String,Class<?>> cache=new HashMap<String, Class<?>>();
public IndexingRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDb,
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.graphDb = graphDb;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
}
private Index<Node> getNodeTypesIndex() {
return graphDb.index().forNodes(INDEX_NAME);
}
private Index<Relationship> getRelTypesIndex() {
return graphDb.index().forRelationships(INDEX_NAME);
}
@Override
public void postEntityCreation(Relationship state, Class<? extends RelationshipBacked> type) {
addToTypesIndex(state, type);
state.setProperty(TYPE_PROPERTY_NAME, type.getName());
}
private void addToTypesIndex(Relationship node, Class<? extends RelationshipBacked> entityClass) {
Class<?> klass = entityClass;
while (klass.getAnnotation(RelationshipEntity.class) != null) {
getRelTypesIndex().add(node, INDEX_KEY, klass.getName());
klass = klass.getSuperclass();
}
}
@Override
public <U extends RelationshipBacked> Iterable<U> findAll(Class<U> clazz) {
return findAllRelBacked(clazz);
}
private <ENTITY extends RelationshipBacked> Iterable<ENTITY> findAllRelBacked(Class<ENTITY> clazz) {
final IndexHits<Relationship> allEntitiesOfType = getRelTypesIndex().get(INDEX_KEY, clazz.getName());
return new FilteringIterable<ENTITY>(new IterableWrapper<ENTITY, Relationship>(allEntitiesOfType) {
@Override
@SuppressWarnings("unchecked")
protected ENTITY underlyingObjectToObject(Relationship rel) {
Class<ENTITY> javaType = (Class<ENTITY>) getJavaType(rel);
if (javaType == null) return null;
return relationshipEntityInstantiator.createEntityFromState(rel, javaType);
}
}, new Predicate<ENTITY>() {
@Override
public boolean accept(ENTITY item) {
return item != null;
}
});
}
@Override
public long count(Class<? extends RelationshipBacked> entityClass) {
long count = 0;
for (Object o : getRelTypesIndex().get(INDEX_KEY, entityClass.getName())) {
count += 1;
}
return count;
}
@Override
@SuppressWarnings("unchecked")
public Class<? extends RelationshipBacked> getJavaType(Relationship relationship) {
if (relationship == null) throw new IllegalArgumentException("Node is null");
String className = (String) relationship.getProperty(TYPE_PROPERTY_NAME);
return getClassForName(className);
}
@SuppressWarnings({"unchecked"})
private <ENTITY extends GraphBacked<?>> Class<ENTITY> getClassForName(String className) {
try {
Class<ENTITY> result= (Class<ENTITY>) cache.get(className);
if (result!=null) return result;
synchronized (cache) {
result= (Class<ENTITY>) cache.get(className);
if (result!=null) return result;
result = (Class<ENTITY>) Class.forName(className);
cache.put(className,result);
return result;
}
} catch (NotFoundException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
@Override
public void preEntityRemoval(RelationshipBacked entity) {
getRelTypesIndex().remove(entity.getPersistentState());
}
@Override
@SuppressWarnings("unchecked")
public <U extends RelationshipBacked> U createEntity(Relationship state) {
Class<? extends RelationshipBacked> javaType = getJavaType(state);
if (javaType == null) {
throw new IllegalStateException("No type stored on relationship.");
}
return (U) relationshipEntityInstantiator.createEntityFromState(state, javaType);
}
@Override
@SuppressWarnings("unchecked")
public <U extends RelationshipBacked> U createEntity(Relationship state, Class<U> type) {
Class<? extends RelationshipBacked> javaType = getJavaType(state);
if (javaType == null) {
throw new IllegalStateException("No type stored on relationship.");
}
if (type.isAssignableFrom(javaType)) {
return (U) relationshipEntityInstantiator.createEntityFromState(state, javaType);
}
throw new IllegalArgumentException(String.format("Entity is not of type: %s (was %s)", type, javaType));
}
@Override
public <U extends RelationshipBacked> U projectEntity(Relationship state, Class<U> type) {
return relationshipEntityInstantiator.createEntityFromState(state, type);
}
}

View File

@@ -1,183 +0,0 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.Predicate;
import org.neo4j.helpers.collection.FilteringIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.data.graph.core.GraphBacked;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.core.TypeRepresentationStrategy;
import org.springframework.data.persistence.EntityInstantiator;
import java.util.HashMap;
import java.util.Map;
public class IndexingTypeRepresentationStrategy implements TypeRepresentationStrategy {
public static final String INDEX_NAME = "__types__";
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
private EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
private GraphDatabaseService graphDb;
private final Map<String,Class<?>> cache=new HashMap<String, Class<?>>();
public IndexingTypeRepresentationStrategy(GraphDatabaseService graphDb,
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator,
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.graphDb = graphDb;
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
}
private Index<Node> getNodeTypesIndex() {
return graphDb.index().forNodes(INDEX_NAME);
}
private Index<Relationship> getRelTypesIndex() {
return graphDb.index().forRelationships(INDEX_NAME);
}
@Override
public void postEntityCreation(GraphBacked<?> entity) {
if (entity instanceof NodeBacked) {
NodeBacked nodeBacked = (NodeBacked) entity;
Node node = nodeBacked.getPersistentState();
Class<? extends NodeBacked> entityClass = nodeBacked.getClass();
addToNodeTypesIndex(node, entityClass);
node.setProperty(TYPE_PROPERTY_NAME, entityClass.getName());
} else if (entity instanceof RelationshipBacked) {
RelationshipBacked relationshipBacked = (RelationshipBacked) entity;
Relationship rel = relationshipBacked.getPersistentState();
Class<? extends RelationshipBacked> entityClass = relationshipBacked.getClass();
addToRelTypesIndex(rel, entityClass);
rel.setProperty(TYPE_PROPERTY_NAME, entityClass.getName());
}
}
private void addToRelTypesIndex(Relationship rel, Class<? extends RelationshipBacked> entityClass) {
getRelTypesIndex().add(rel, INDEX_KEY, entityClass.getName());
}
private void addToNodeTypesIndex(Node node, Class<? extends NodeBacked> entityClass) {
Class<?> klass = entityClass;
while (klass.getAnnotation(NodeEntity.class) != null) {
getNodeTypesIndex().add(node, INDEX_KEY, klass.getName());
klass = klass.getSuperclass();
}
}
@Override
public <ENTITY extends GraphBacked<?>> Iterable<ENTITY> findAll(Class<ENTITY> clazz) {
if (NodeBacked.class.isAssignableFrom(clazz)) {
return (Iterable<ENTITY>) findAllNodeBacked((Class<? extends NodeBacked>) clazz);
} else if (RelationshipBacked.class.isAssignableFrom(clazz)) {
return (Iterable<ENTITY>) findAllRelBacked((Class<? extends RelationshipBacked>) clazz);
}
throw new UnsupportedOperationException();
}
private <ENTITY extends RelationshipBacked> Iterable<ENTITY> findAllRelBacked(Class<ENTITY> clazz) {
final IndexHits<Relationship> allEntitiesOfType = getRelTypesIndex().get(INDEX_KEY, clazz.getName());
return new FilteringIterable<ENTITY>(new IterableWrapper<ENTITY, Relationship>(allEntitiesOfType) {
@Override
@SuppressWarnings("unchecked")
protected ENTITY underlyingObjectToObject(Relationship rel) {
Class<ENTITY> javaType = (Class<ENTITY>) getJavaType(rel);
if (javaType == null) return null;
return relationshipEntityInstantiator.createEntityFromState(rel, javaType);
}
}, new Predicate<ENTITY>() {
@Override
public boolean accept(ENTITY item) {
return item != null;
}
});
}
private <ENTITY extends NodeBacked> Iterable<ENTITY> findAllNodeBacked(Class<ENTITY> clazz) {
final IndexHits<Node> allEntitiesOfType = getNodeTypesIndex().get(INDEX_KEY, clazz.getName());
return new FilteringIterable<ENTITY>(new IterableWrapper<ENTITY, Node>(allEntitiesOfType) {
@Override
@SuppressWarnings("unchecked")
protected ENTITY underlyingObjectToObject(Node node) {
Class<ENTITY> javaType = (Class<ENTITY>) getJavaType(node);
if (javaType == null) return null;
return graphEntityInstantiator.createEntityFromState(node, javaType);
}
}, new Predicate<ENTITY>() {
@Override
public boolean accept(ENTITY item) {
return item != null;
}
});
}
@Override
public long count(Class<? extends GraphBacked<?>> entityClass) {
long count = 0;
for (Object o : getIndexForType(entityClass).get(INDEX_KEY, entityClass.getName())) {
count += 1;
}
return count;
}
private Index<?> getIndexForType(Class<? extends GraphBacked<?>> entityClass) {
if (NodeBacked.class.isAssignableFrom(entityClass)) {
return getNodeTypesIndex();
} else if (RelationshipBacked.class.isAssignableFrom(entityClass)) {
return getRelTypesIndex();
}
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("unchecked")
public <ENTITY extends GraphBacked<?>> Class<ENTITY> getJavaType(PropertyContainer primitive) {
if (primitive == null) throw new IllegalArgumentException("Node is null");
String className = (String) primitive.getProperty(TYPE_PROPERTY_NAME);
return getClassForName(className);
}
@SuppressWarnings({"unchecked"})
private <ENTITY extends GraphBacked<?>> Class<ENTITY> getClassForName(String className) {
try {
Class<ENTITY> result= (Class<ENTITY>) cache.get(className);
if (result!=null) return result;
synchronized (cache) {
result= (Class<ENTITY>) cache.get(className);
if (result!=null) return result;
result = (Class<ENTITY>) Class.forName(className);
cache.put(className,result);
return result;
}
} catch (NotFoundException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
@Override
public void preEntityRemoval(GraphBacked<?> entity) {
if (entity instanceof NodeBacked) {
getNodeTypesIndex().remove(((NodeBacked)entity).getPersistentState());
} else if (entity instanceof RelationshipBacked) {
getRelTypesIndex().remove(((RelationshipBacked)entity).getPersistentState());
}
}
@Override
public <T extends GraphBacked<?>> Class<T> confirmType(PropertyContainer primitive, Class<T> type) {
Class<T> javaType = getJavaType(primitive);
if (javaType == null) throw new IllegalStateException("No type stored on node.");
if (type.isAssignableFrom(javaType)) return javaType;
throw new IllegalArgumentException(String.format("%s does not correspond to the stored type %s of %s %s",
type, javaType, primitive instanceof Node ? "node" : "relationship", primitive));
}
}

View File

@@ -1,36 +1,92 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.data.graph.core.GraphBacked;
import org.springframework.data.graph.core.TypeRepresentationStrategy;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.NodeTypeRepresentationStrategy;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.core.RelationshipTypeRepresentationStrategy;
public class NoopTypeRepresentationStrategy implements TypeRepresentationStrategy {
public class NoopTypeRepresentationStrategy {
public static class NoopNodeStrategy implements NodeTypeRepresentationStrategy {
@Override
public void postEntityCreation(GraphBacked<?> entity) {
@Override
public void postEntityCreation(Node state, Class<? extends NodeBacked> type) {
}
@Override
public <U extends NodeBacked> Iterable<U> findAll(Class<U> clazz) {
throw new UnsupportedOperationException("findAll not supported by NoopTypeRepresentationStrategy.");
}
@Override
public long count(Class<? extends NodeBacked> entityClass) {
throw new UnsupportedOperationException("count not supported by NoopTypeRepresentationStrategy.");
}
@Override
public Class<? extends NodeBacked> getJavaType(Node state) {
throw new UnsupportedOperationException("getJavaType not supported by NoopTypeRepresentationStrategy.");
}
@Override
public void preEntityRemoval(NodeBacked entity) {
}
@Override
public <U extends NodeBacked> U createEntity(Node state) {
throw new UnsupportedOperationException("Creation with stored type not supported by NoopTypeRepresentationStrategy.");
}
@Override
public <U extends NodeBacked> U createEntity(Node state, Class<U> type) {
return projectEntity(state, type);
}
@Override
public <U extends NodeBacked> U projectEntity(Node state, Class<U> type) {
return null;
}
}
@Override
public <T extends GraphBacked<?>> Iterable<T> findAll(Class<T> clazz) {
throw new UnsupportedOperationException("findAll not supported by NoopTypeRepresentationStrategy.");
}
public static class NoopRelationshipStrategy implements RelationshipTypeRepresentationStrategy {
@Override
public long count(Class<? extends GraphBacked<?>> entityClass) {
throw new UnsupportedOperationException("count not supported by NoopTypeRepresentationStrategy.");
}
@Override
public void postEntityCreation(Relationship state, Class<? extends RelationshipBacked> type) {
}
@Override
public <T extends GraphBacked<?>> Class<T> getJavaType(PropertyContainer primitive) {
throw new UnsupportedOperationException("getJavaType not supported NoopTypeRepresentationStrategy.");
}
@Override
public <U extends RelationshipBacked> Iterable<U> findAll(Class<U> clazz) {
throw new UnsupportedOperationException("findAll not supported by NoopTypeRepresentationStrategy.");
}
@Override
public void preEntityRemoval(GraphBacked<?> entity) {
}
@Override
public long count(Class<? extends RelationshipBacked> entityClass) {
throw new UnsupportedOperationException("count not supported by NoopTypeRepresentationStrategy.");
}
@Override
public <T extends GraphBacked<?>> Class<T> confirmType(PropertyContainer node, Class<T> type) {
return type;
@Override
public Class<? extends RelationshipBacked> getJavaType(Relationship state) {
throw new UnsupportedOperationException("getJavaType not supported by NoopTypeRepresentationStrategy.");
}
@Override
public void preEntityRemoval(RelationshipBacked entity) {
}
@Override
public <U extends RelationshipBacked> U createEntity(Relationship state) {
throw new UnsupportedOperationException("Creation with stored type not supported by NoopTypeRepresentationStrategy.");
}
@Override
public <U extends RelationshipBacked> U createEntity(Relationship state, Class<U> type) {
return projectEntity(state, type);
}
@Override
public <U extends RelationshipBacked> U projectEntity(Relationship state, Class<U> type) {
return null;
}
}
}

View File

@@ -0,0 +1,252 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.graph.neo4j.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.CombiningIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.kernel.Traversal;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.NodeTypeRepresentationStrategy;
import org.springframework.data.persistence.EntityInstantiator;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A {@link org.springframework.data.graph.core.TypeRepresentationStrategy} that uses a hierarchy of reference nodes to represent the java type of the entity in the
* graph database. Entity nodes are related to their concrete type via an INSTANCE_OF relationship, the type hierarchy is
* related to supertypes via SUBCLASS_OF relationships. Each concrete subreference node keeps a count property with the number of
* instances of this class in the graph.
*
* @author Michael Hunger
* @since 13.09.2010
*/
public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
private final static Log log = LogFactory.getLog(SubReferenceNodeTypeRepresentationStrategy.class);
public final static RelationshipType INSTANCE_OF_RELATIONSHIP_TYPE = DynamicRelationshipType.withName("INSTANCE_OF");
public final static RelationshipType SUBCLASS_OF_RELATIONSHIP_TYPE = DynamicRelationshipType.withName("SUBCLASS_OF");
public static final String SUBREFERENCE_NODE_COUNTER_KEY = "count";
public static final String SUBREF_PREFIX = "SUBREF_";
public static final String SUBREF_CLASS_KEY = "class";
private GraphDatabaseService graphDatabaseService;
private EntityInstantiator<NodeBacked, Node> entityInstantiator;
public SubReferenceNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> entityInstantiator) {
this.graphDatabaseService = graphDatabaseService;
this.entityInstantiator = entityInstantiator;
}
public static Node getSingleOtherNode(Node node, RelationshipType type,
Direction direction) {
Relationship rel = node.getSingleRelationship(type, direction);
return rel == null ? null : rel.getOtherNode(node);
}
public static Integer incrementAndGetCounter(Node node, String propertyKey) {
acquireWriteLock(node);
int value = (Integer) node.getProperty(propertyKey, 0);
value++;
node.setProperty(propertyKey, value);
return value;
}
public static Integer decrementAndGetCounter(Node node, String propertyKey,
int notLowerThan) {
int value = (Integer) node.getProperty(propertyKey, 0);
value--;
value = value < notLowerThan ? notLowerThan : value;
node.setProperty(propertyKey, value);
return value;
}
public static void acquireWriteLock(PropertyContainer entity) {
// TODO At the moment this is the best way of doing it, if you don't want to use
// the LockManager (and release the lock yourself)
entity.removeProperty("___dummy_property_for_locking___");
}
@Override
public void postEntityCreation(Node state, Class<? extends NodeBacked> type) {
final Node subReference = obtainSubreferenceNode(type);
state.createRelationshipTo(subReference, INSTANCE_OF_RELATIONSHIP_TYPE);
subReference.setProperty(SUBREF_CLASS_KEY, type.getName());
if (log.isDebugEnabled()) log.debug("Created link to subref node: " + subReference + " with type: " + type.getName());
incrementAndGetCounter(subReference, SUBREFERENCE_NODE_COUNTER_KEY);
updateSuperClassSubrefs(type, subReference);
}
/**
* removes instanceof relationship and decrements instance counters for type nodes
* @param entity
*/
@Override
public void preEntityRemoval(NodeBacked entity) {
Class<? extends NodeBacked> clazz = entity.getClass();
final Node subReference = obtainSubreferenceNode(clazz);
Node subRefNode = entity.getPersistentState();
Relationship instanceOf = subRefNode.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
instanceOf.delete();
if (log.isDebugEnabled()) log.debug("Removed link to subref node: " + subReference + " with type: " + clazz.getName());
TraversalDescription traversal = Traversal.description().depthFirst().relationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
for (Node node : traversal.traverse(subReference).nodes()) {
Integer count = (Integer) node.getProperty(SUBREFERENCE_NODE_COUNTER_KEY);
Integer newCount = decrementAndGetCounter(node, SUBREFERENCE_NODE_COUNTER_KEY, 0);
if (log.isDebugEnabled()) log.debug("count on ref " + node + " was " + count + " new " + newCount);
}
}
// @Override
// public <T extends NodeBacked> Class<T> confirmType(Node node, Class<T> type) {
// Class<T> nodeType = this.<T>getJavaType(node);
// if (type.isAssignableFrom(nodeType)) return nodeType;
// throw new IllegalArgumentException(String.format("%s does not correspond to the node type %s of node %s",type,nodeType,node));
// }
private void updateSuperClassSubrefs(Class<?> clazz, Node subReference) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
Node superClassSubref = obtainSubreferenceNode(superClass);
if (getSingleOtherNode(subReference, SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.OUTGOING) == null) {
subReference.createRelationshipTo(superClassSubref, SUBCLASS_OF_RELATIONSHIP_TYPE);
}
superClassSubref.setProperty(SUBREF_CLASS_KEY, superClass.getName());
Integer count = incrementAndGetCounter(superClassSubref, SUBREFERENCE_NODE_COUNTER_KEY);
if (log.isDebugEnabled()) log.debug("count on ref " + superClassSubref + " for class " + superClass.getSimpleName() + " = " + count);
updateSuperClassSubrefs(superClass, superClassSubref);
}
}
@Override
public long count(final Class<? extends NodeBacked> entityClass) {
final Node subrefNode = findSubreferenceNode(entityClass);
if (subrefNode == null) return 0;
return (Integer) subrefNode.getProperty(SUBREFERENCE_NODE_COUNTER_KEY, 0);
}
@Override
@SuppressWarnings("unchecked")
public <T extends NodeBacked> Class<T> getJavaType(Node node) {
if (node==null) throw new IllegalArgumentException("Node is null");
Relationship instanceOfRelationship = node.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
if (instanceOfRelationship==null) throw new IllegalArgumentException("The node "+node+" is not attached to a type hierarchy.");
Node subrefNode = instanceOfRelationship.getEndNode();
try {
Class<T> clazz = (Class<T>) Class.forName((String) subrefNode.getProperty(SUBREF_CLASS_KEY)).asSubclass(NodeBacked.class);
if (log.isDebugEnabled()) log.debug("Found class " + clazz.getSimpleName() + " for node: " + node);
return clazz;
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Unable to get type for node: " + node, e);
}
}
@Override
public <T extends NodeBacked> Iterable<T> findAll(final Class<T> clazz) {
final Node subrefNode = findSubreferenceNode(clazz);
if (log.isDebugEnabled()) log.debug("Subref: " + subrefNode);
Iterable<Iterable<T>> relIterables = findEntityIterables(subrefNode);
return new CombiningIterable<T>(relIterables);
}
private <T extends NodeBacked> List<Iterable<T>> findEntityIterables(Node subrefNode) {
if (subrefNode == null) return Collections.emptyList();
List<Iterable<T>> result = new LinkedList<Iterable<T>>();
for (Relationship relationship : subrefNode.getRelationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) {
result.addAll((Collection<? extends Iterable<T>>) findEntityIterables(relationship.getStartNode()));
}
Iterable<T> t = new IterableWrapper<T, Relationship>(subrefNode.getRelationships(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) {
@Override
protected T underlyingObjectToObject(final Relationship rel) {
final Node node = rel.getStartNode();
T entity = (T) entityInstantiator.createEntityFromState(node, getJavaType(node));
if (log.isDebugEnabled()) log.debug("Converting node: " + node + " to entity: " + entity);
return entity;
}
};
result.add(t);
return result;
}
public Node obtainSubreferenceNode(final Class<?> entityClass) {
return getOrCreateSubReferenceNode(subRefRelationshipType(entityClass));
}
public Node findSubreferenceNode(final Class<? extends NodeBacked> entityClass) {
final Relationship subrefRelationship = graphDatabaseService.getReferenceNode().getSingleRelationship(subRefRelationshipType(entityClass), Direction.OUTGOING);
return subrefRelationship != null ? subrefRelationship.getEndNode() : null;
}
private DynamicRelationshipType subRefRelationshipType(Class<?> clazz) {
return DynamicRelationshipType.withName(SUBREF_PREFIX + clazz.getName());
}
public Node getOrCreateSubReferenceNode(final RelationshipType relType) {
return getOrCreateSingleOtherNode(graphDatabaseService.getReferenceNode(), relType, Direction.OUTGOING);
}
private Node getOrCreateSingleOtherNode(Node fromNode, RelationshipType type,
Direction direction) {
Relationship singleRelationship = fromNode.getSingleRelationship(type, direction);
if (singleRelationship != null) {
return singleRelationship.getOtherNode(fromNode);
}
Node otherNode = graphDatabaseService.createNode();
fromNode.createRelationshipTo(otherNode, type);
return otherNode;
}
@Override
public <U extends NodeBacked> U createEntity(Node state) {
Class<? extends NodeBacked> javaType = getJavaType(state);
if (javaType == null) {
throw new IllegalStateException("No type stored on node.");
}
return (U) entityInstantiator.createEntityFromState(state, javaType);
}
@Override
public <U extends NodeBacked> U createEntity(Node state, Class<U> type) {
Class<? extends NodeBacked> javaType = getJavaType(state);
if (javaType == null) {
throw new IllegalStateException("No type stored on node.");
}
if (type.isAssignableFrom(javaType)) {
return (U) entityInstantiator.createEntityFromState(state, javaType);
}
throw new IllegalArgumentException(String.format("Entity is not of type: %s (was %s)", type, javaType));
}
@Override
public <U extends NodeBacked> U projectEntity(Node state, Class<U> type) {
return entityInstantiator.createEntityFromState(state, type);
}
}

View File

@@ -1,252 +0,0 @@
/*
* Copyright 2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.graph.neo4j.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.graphdb.*;
import org.springframework.data.graph.core.GraphBacked;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.TypeRepresentationStrategy;
import org.springframework.data.persistence.EntityInstantiator;
/**
* A {@link org.springframework.data.graph.core.TypeRepresentationStrategy} that uses a hierarchy of reference nodes to represent the java type of the entity in the
* graph database. Entity nodes are related to their concrete type via an INSTANCE_OF relationship, the type hierarchy is
* related to supertypes via SUBCLASS_OF relationships. Each concrete subreference node keeps a count property with the number of
* instances of this class in the graph.
*
* @author Michael Hunger
* @since 13.09.2010
*/
public class SubReferenceTypeRepresentationStrategy implements TypeRepresentationStrategy {
private final static Log log = LogFactory.getLog(SubReferenceTypeRepresentationStrategy.class);
public final static RelationshipType INSTANCE_OF_RELATIONSHIP_TYPE = DynamicRelationshipType.withName("INSTANCE_OF");
public final static RelationshipType SUBCLASS_OF_RELATIONSHIP_TYPE = DynamicRelationshipType.withName("SUBCLASS_OF");
public static final String SUBREFERENCE_NODE_COUNTER_KEY = "count";
public static final String SUBREF_PREFIX = "SUBREF_";
public static final String SUBREF_CLASS_KEY = "class";
private GraphDatabaseService graphDatabaseService;
private EntityInstantiator<NodeBacked, Node> entityInstantiator;
public SubReferenceTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> entityInstantiator) {
this.graphDatabaseService = graphDatabaseService;
this.entityInstantiator = entityInstantiator;
}
//
// public static Node getSingleOtherNode(Node node, RelationshipType type,
// Direction direction) {
// Relationship rel = node.getSingleRelationship(type, direction);
// return rel == null ? null : rel.getOtherNode(node);
// }
//
// public static Integer incrementAndGetCounter(Node node, String propertyKey) {
// acquireWriteLock(node);
// int value = (Integer) node.getProperty(propertyKey, 0);
// value++;
// node.setProperty(propertyKey, value);
// return value;
// }
//
// public static Integer decrementAndGetCounter(Node node, String propertyKey,
// int notLowerThan) {
// int value = (Integer) node.getProperty(propertyKey, 0);
// value--;
// value = value < notLowerThan ? notLowerThan : value;
// node.setProperty(propertyKey, value);
// return value;
// }
//
// public static void acquireWriteLock(PropertyContainer entity) {
// // TODO At the moment this is the best way of doing it, if you don't want to use
// // the LockManager (and release the lock yourself)
// entity.removeProperty("___dummy_property_for_locking___");
// }
//
// /**
// * lifecycle method, creates instanceof relationship to type node, creates the type nodes of the inheritance
// * hierarchy if necessary and increments instance counters
// * @param entity
// */
// @Override
// public void postEntityCreation(final NodeBacked entity) {
// Class<? extends NodeBacked> clazz = entity.getClass();
//
// final Node subReference = obtainSubreferenceNode(clazz);
// entity.getPersistentState().createRelationshipTo(subReference, INSTANCE_OF_RELATIONSHIP_TYPE);
// subReference.setProperty(SUBREF_CLASS_KEY, clazz.getName());
// if (log.isDebugEnabled()) log.debug("Created link to subref node: " + subReference + " with type: " + clazz.getName());
//
// incrementAndGetCounter(subReference, SUBREFERENCE_NODE_COUNTER_KEY);
//
// updateSuperClassSubrefs(clazz, subReference);
// }
//
// /**
// * removes instanceof relationship and decrements instance counters for type nodes
// * @param entity
// */
// @Override
// public void preEntityRemoval(NodeBacked entity) {
// Class<? extends NodeBacked> clazz = entity.getClass();
//
// final Node subReference = obtainSubreferenceNode(clazz);
// Node subRefNode = entity.getPersistentState();
// Relationship instanceOf = subRefNode.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
// instanceOf.delete();
// if (log.isDebugEnabled()) log.debug("Removed link to subref node: " + subReference + " with type: " + clazz.getName());
// TraversalDescription traversal = new TraversalDescriptionImpl().depthFirst().relationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
// for (Node node : traversal.traverse(subReference).nodes()) {
// Integer count = (Integer) node.getProperty(SUBREFERENCE_NODE_COUNTER_KEY);
// Integer newCount = decrementAndGetCounter(node, SUBREFERENCE_NODE_COUNTER_KEY, 0);
// if (log.isDebugEnabled()) log.debug("count on ref " + node + " was " + count + " new " + newCount);
// }
// }
//
// @Override
// public <T extends NodeBacked> Class<T> confirmType(Node node, Class<T> type) {
// Class<T> nodeType = this.<T>getJavaType(node);
// if (type.isAssignableFrom(nodeType)) return nodeType;
// throw new IllegalArgumentException(String.format("%s does not correspond to the node type %s of node %s",type,nodeType,node));
// }
//
// private void updateSuperClassSubrefs(Class<?> clazz, Node subReference) {
// Class<?> superClass = clazz.getSuperclass();
// if (superClass != null) {
// Node superClassSubref = obtainSubreferenceNode(superClass);
// if (getSingleOtherNode(subReference, SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.OUTGOING) == null) {
// subReference.createRelationshipTo(superClassSubref, SUBCLASS_OF_RELATIONSHIP_TYPE);
// }
// superClassSubref.setProperty(SUBREF_CLASS_KEY, superClass.getName());
// Integer count = incrementAndGetCounter(superClassSubref, SUBREFERENCE_NODE_COUNTER_KEY);
// if (log.isDebugEnabled()) log.debug("count on ref " + superClassSubref + " for class " + superClass.getSimpleName() + " = " + count);
// updateSuperClassSubrefs(superClass, superClassSubref);
// }
// }
//
// @Override
// public long count(final Class<? extends NodeBacked> entityClass) {
// final Node subrefNode = findSubreferenceNode(entityClass);
// if (subrefNode == null) return 0;
// return (Integer) subrefNode.getProperty(SUBREFERENCE_NODE_COUNTER_KEY, 0);
// }
//
// @Override
// @SuppressWarnings("unchecked")
// public <T extends NodeBacked> Class<T> getJavaType(Node node) {
// if (node==null) throw new IllegalArgumentException("Node is null");
// Relationship instanceOfRelationship = node.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
// if (instanceOfRelationship==null) throw new IllegalArgumentException("The node "+node+" is not attached to a type hierarchy.");
// Node subrefNode = instanceOfRelationship.getEndNode();
// try {
// Class<T> clazz = (Class<T>) Class.forName((String) subrefNode.getProperty(SUBREF_CLASS_KEY)).asSubclass(NodeBacked.class);
// if (log.isDebugEnabled()) log.debug("Found class " + clazz.getSimpleName() + " for node: " + node);
// return clazz;
// } catch (ClassNotFoundException e) {
// throw new IllegalStateException("Unable to get type for node: " + node, e);
// }
// }
//
// @Override
// public <T extends NodeBacked> Iterable<T> findAll(final Class<T> clazz) {
// final Node subrefNode = findSubreferenceNode(clazz);
// if (log.isDebugEnabled()) log.debug("Subref: " + subrefNode);
// Iterable<Iterable<T>> relIterables = findEntityIterables(subrefNode);
// return new CombiningIterable<T>(relIterables);
// }
//
// private <T extends NodeBacked> List<Iterable<T>> findEntityIterables(Node subrefNode) {
// if (subrefNode == null) return Collections.emptyList();
// List<Iterable<T>> result = new LinkedList<Iterable<T>>();
// for (Relationship relationship : subrefNode.getRelationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) {
// result.addAll((Collection<? extends Iterable<T>>) findEntityIterables(relationship.getStartNode()));
// }
// Iterable<T> t = new IterableWrapper<T, Relationship>(subrefNode.getRelationships(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) {
// @Override
// protected T underlyingObjectToObject(final Relationship rel) {
// final Node node = rel.getStartNode();
// T entity = (T) entityInstantiator.createEntityFromState(node, getJavaType(node));
// if (log.isDebugEnabled()) log.debug("Converting node: " + node + " to entity: " + entity);
// return entity;
// }
// };
// result.add(t);
// return result;
// }
//
//
// public Node obtainSubreferenceNode(final Class<?> entityClass) {
// return getOrCreateSubReferenceNode(subRefRelationshipType(entityClass));
// }
//
// public Node findSubreferenceNode(final Class<? extends NodeBacked> entityClass) {
// final Relationship subrefRelationship = graphDatabaseService.getReferenceNode().getSingleRelationship(subRefRelationshipType(entityClass), Direction.OUTGOING);
// return subrefRelationship != null ? subrefRelationship.getEndNode() : null;
// }
//
// private DynamicRelationshipType subRefRelationshipType(Class<?> clazz) {
// return DynamicRelationshipType.withName(SUBREF_PREFIX + clazz.getName());
// }
//
// public Node getOrCreateSubReferenceNode(final RelationshipType relType) {
// return getOrCreateSingleOtherNode(graphDatabaseService.getReferenceNode(), relType, Direction.OUTGOING);
// }
//
// private Node getOrCreateSingleOtherNode(Node fromNode, RelationshipType type,
// Direction direction) {
// Relationship singleRelationship = fromNode.getSingleRelationship(type, direction);
// if (singleRelationship != null) {
// return singleRelationship.getOtherNode(fromNode);
// }
//
// Node otherNode = graphDatabaseService.createNode();
// fromNode.createRelationshipTo(otherNode, type);
// return otherNode;
//
// }
@Override
public void postEntityCreation(GraphBacked<?> entity) {
}
@Override
public <T extends GraphBacked<?>> Iterable<T> findAll(Class<T> clazz) {
return null;
}
@Override
public long count(Class<? extends GraphBacked<?>> entityClass) {
return 0;
}
@Override
public <T extends GraphBacked<?>> Class<T> getJavaType(PropertyContainer primitive) {
return null;
}
@Override
public void preEntityRemoval(GraphBacked<?> entity) {
}
@Override
public <T extends GraphBacked<?>> Class<T> confirmType(PropertyContainer node, Class<T> type) {
return null;
}
}

View File

@@ -0,0 +1,90 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.springframework.data.graph.core.*;
import org.springframework.data.persistence.EntityInstantiator;
public class TypeRepresentationStrategyFactory {
private GraphDatabaseService graphDatabaseService;
private EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private EntityInstantiator<RelationshipBacked,Relationship> relationshipEntityInstantiator;
private Strategy strategy;
public TypeRepresentationStrategyFactory(GraphDatabaseService graphDatabaseService,
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator,
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.graphDatabaseService = graphDatabaseService;
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
strategy = chooseStrategy();
}
private Strategy chooseStrategy() {
if (isAlreadyIndexed()) return Strategy.Indexed;
if (isAlreadySubRef()) return Strategy.SubRef;
return Strategy.Indexed;
}
private boolean isAlreadyIndexed() {
return graphDatabaseService.index().existsForNodes(IndexingNodeTypeRepresentationStrategy.INDEX_NAME);
}
private boolean isAlreadySubRef() {
for (Relationship rel : graphDatabaseService.getReferenceNode().getRelationships()) {
if (rel.getType().name().startsWith(SubReferenceNodeTypeRepresentationStrategy.SUBREF_PREFIX)) {
return true;
}
}
return false;
}
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy() {
return strategy.getNodeTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator);
}
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy() {
return strategy.getRelationshipTypeRepresentationStrategy(graphDatabaseService, relationshipEntityInstantiator);
}
private enum Strategy {
SubRef {
@Override
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
return new SubReferenceNodeTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator);
}
@Override
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
return new NoopTypeRepresentationStrategy.NoopRelationshipStrategy();
}
},
Indexed {
@Override
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
return new IndexingNodeTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator);
}
@Override
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
return new IndexingRelationshipTypeRepresentationStrategy(graphDatabaseService, relationshipEntityInstantiator);
}
},
Noop {
@Override
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
return new NoopTypeRepresentationStrategy.NoopNodeStrategy();
}
@Override
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
return new NoopTypeRepresentationStrategy.NoopRelationshipStrategy();
}
};
public abstract NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator);
public abstract RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator);
}
}

View File

@@ -1,99 +0,0 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.RelationshipBacked;
import org.springframework.data.graph.core.TypeRepresentationStrategy;
import org.springframework.data.persistence.EntityInstantiator;
public class TypeRepresentationStrategyFactoryBean implements FactoryBean<TypeRepresentationStrategy> {
private GraphDatabaseService graphDatabaseService;
private EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private EntityInstantiator<RelationshipBacked,Relationship> relationshipEntityInstantiator;
private Strategy strategy;
public TypeRepresentationStrategyFactoryBean(GraphDatabaseService graphDatabaseService,
EntityInstantiator<NodeBacked, Node> graphEntityInstantiator,
EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.graphDatabaseService = graphDatabaseService;
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
strategy = chooseStrategy();
}
private Strategy chooseStrategy() {
if (isAlreadyIndexed()) return Strategy.Indexed;
if (isAlreadySubRef()) return Strategy.SubRef;
return Strategy.Indexed;
}
private boolean isAlreadyIndexed() {
return graphDatabaseService.index().existsForNodes(IndexingTypeRepresentationStrategy.INDEX_NAME);
}
private boolean isAlreadySubRef() {
for (Relationship rel : graphDatabaseService.getReferenceNode().getRelationships()) {
if (rel.getType().name().startsWith(SubReferenceTypeRepresentationStrategy.SUBREF_PREFIX)) {
return true;
}
}
return false;
}
@Override
public TypeRepresentationStrategy getObject() throws Exception {
return strategy.getObject(graphDatabaseService, graphEntityInstantiator, relationshipEntityInstantiator);
}
@Override
public Class<?> getObjectType() {
return strategy.getObjectType();
}
@Override
public boolean isSingleton() {
return false;
}
private enum Strategy {
SubRef {
@Override
TypeRepresentationStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
return new SubReferenceTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator);
}
@Override
Class<? extends TypeRepresentationStrategy> getObjectType() {
return SubReferenceTypeRepresentationStrategy.class;
}
},
Indexed {
@Override
TypeRepresentationStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
return new IndexingTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator, relationshipEntityInstantiator);
}
@Override
Class<? extends TypeRepresentationStrategy> getObjectType() {
return IndexingTypeRepresentationStrategy.class;
}
},
Noop {
@Override
TypeRepresentationStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
return new NoopTypeRepresentationStrategy();
}
@Override
Class<? extends TypeRepresentationStrategy> getObjectType() {
return NoopTypeRepresentationStrategy.class;
}
};
abstract TypeRepresentationStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator);
abstract Class<? extends TypeRepresentationStrategy> getObjectType();
}
}

View File

@@ -179,7 +179,10 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields<No
if (relationshipType==null) throw new IllegalArgumentException("Relationshiptype is null");
Relationship rel = this.relateTo(target,relationshipType);
return (R)Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipClass);
GraphDatabaseContext gdc = Neo4jNodeBacking.aspectOf().graphDatabaseContext;
gdc.postEntityCreation(rel, relationshipClass);
return (R) gdc.createEntityFromState(rel, relationshipClass);
}
public void NodeBacked.remove() {

View File

@@ -0,0 +1,221 @@
package org.springframework.data.graph.neo4j.support;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.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.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
"classpath:org/springframework/data/graph/neo4j/support/IndexingTypeRepresentationStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class IndexingNodeTypeRepresentationStrategyTest {
@Autowired
private GraphDatabaseService graphDatabaseService;
@Autowired
private IndexingNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
private Thing thing;
private SubThing subThing;
@BeforeTransaction
public void cleanDb() {
Neo4jHelper.cleanDb(graphDatabaseService);
}
@Before
public void setUp() throws Exception {
if (thing == null) {
createThingsAndLinks();
}
}
@Test
@Transactional
public void testPostEntityCreation() throws Exception {
Index<Node> typesIndex = graphDatabaseService.index().forNodes(IndexingNodeTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Node> thingHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, thing.getClass().getName());
assertEquals(set(node(thing), node(subThing)), IteratorUtil.addToCollection((Iterable<Node>)thingHits, new HashSet<Node>()));
IndexHits<Node> subThingHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, subThing.getClass().getName());
assertEquals(node(subThing), subThingHits.getSingle());
assertEquals(thing.getClass().getName(), node(thing).getProperty(IndexingNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
assertEquals(subThing.getClass().getName(), node(subThing).getProperty(IndexingNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
}
@Test
public void testPreEntityRemoval() throws Exception {
manualCleanDb();
createThingsAndLinks();
Index<Node> typesIndex = graphDatabaseService.index().forNodes(IndexingNodeTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Node> thingHits;
IndexHits<Node> subThingHits;
Transaction tx = graphDatabaseService.beginTx();
try
{
nodeTypeRepresentationStrategy.preEntityRemoval(thing);
tx.success();
}
finally
{
tx.finish();
}
thingHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, thing.getClass().getName());
assertEquals(node(subThing), thingHits.getSingle());
subThingHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, subThing.getClass().getName());
assertEquals(node(subThing), subThingHits.getSingle());
tx = graphDatabaseService.beginTx();
try
{
nodeTypeRepresentationStrategy.preEntityRemoval(subThing);
tx.success();
}
finally
{
tx.finish();
}
thingHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, thing.getClass().getName());
assertNull(thingHits.getSingle());
subThingHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, subThing.getClass().getName());
assertNull(subThingHits.getSingle());
}
@Test
@Transactional
public void testFindAll() throws Exception {
assertEquals("Did not find all things.",
new HashSet<Thing>(Arrays.asList(subThing, thing)),
IteratorUtil.addToCollection(nodeTypeRepresentationStrategy.findAll(Thing.class), new HashSet<Thing>()));
}
@Test
@Transactional
public void testCount() throws Exception {
assertEquals(2, nodeTypeRepresentationStrategy.count(Thing.class));
}
@Test
@Transactional
public void testGetJavaType() throws Exception {
assertEquals(Thing.class, nodeTypeRepresentationStrategy.getJavaType(node(thing)));
assertEquals(SubThing.class, nodeTypeRepresentationStrategy.getJavaType(node(subThing)));
}
@Test
@Transactional
public void testCreateEntityAndInferType() throws Exception {
Thing newThing = nodeTypeRepresentationStrategy.createEntity(node(thing));
assertEquals(thing, newThing);
}
@Test
@Transactional
public void testCreateEntityAndSpecifyType() throws Exception {
Thing newThing = nodeTypeRepresentationStrategy.createEntity(node(subThing), Thing.class);
assertEquals(subThing, newThing);
}
@Test
@Transactional
public void testProjectEntity() throws Exception {
Unrelated other = nodeTypeRepresentationStrategy.projectEntity(node(thing), Unrelated.class);
assertEquals("thing", other.getName());
}
private static Node node(Thing thing) {
return thing.getPersistentState();
}
private Thing createThingsAndLinks() {
Transaction tx = graphDatabaseService.beginTx();
try {
Node n1 = graphDatabaseService.createNode();
thing = new Thing(n1);
nodeTypeRepresentationStrategy.postEntityCreation(n1, Thing.class);
thing.setName("thing");
Node n2 = graphDatabaseService.createNode();
subThing = new SubThing(n2);
nodeTypeRepresentationStrategy.postEntityCreation(n2, SubThing.class);
subThing.setName("subThing");
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Unrelated {
String name;
public String getName() {
return name;
}
}
@NodeEntity
public static class Thing {
String name;
public Thing(Node node) {
setPersistentState(node);
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class SubThing extends Thing {
public SubThing(Node node) {
super(node);
}
}
private static Set<Node> set(Node... nodes) {
return new HashSet<Node>(Arrays.asList(nodes));
}
private void manualCleanDb() {
Transaction tx = graphDatabaseService.beginTx();
try {
cleanDb();
tx.success();
} finally {
tx.finish();
}
}
}

View File

@@ -0,0 +1,200 @@
package org.springframework.data.graph.neo4j.support;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.graph.annotation.EndNode;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.data.graph.annotation.RelationshipEntity;
import org.springframework.data.graph.annotation.StartNode;
import org.springframework.data.graph.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.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
"classpath:org/springframework/data/graph/neo4j/support/IndexingTypeRepresentationStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class IndexingRelationshipTypeRepresentationStrategyTest {
@Autowired
private GraphDatabaseService graphDatabaseService;
@Autowired
private IndexingRelationshipTypeRepresentationStrategy relationshipTypeRepresentationStrategy;
private Link link;
@BeforeTransaction
public void cleanDb() {
Neo4jHelper.cleanDb(graphDatabaseService);
}
@Before
public void setUp() throws Exception {
if (link == null) {
createThingsAndLinks();
}
}
@Test
@Transactional
public void testPostEntityCreationOfRelationshipBacked() throws Exception {
Index<Relationship> typesIndex = graphDatabaseService.index().forRelationships(IndexingNodeTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Relationship> linkHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, link.getClass().getName());
Relationship rel = linkHits.getSingle();
assertEquals(rel(link), rel);
assertEquals(link.getClass().getName(), rel.getProperty("__type__"));
}
@Test
public void testPreEntityRemovalOfRelationshipBacked() throws Exception {
manualCleanDb();
createThingsAndLinks();
Index<Relationship> typesIndex = graphDatabaseService.index().forRelationships(IndexingNodeTypeRepresentationStrategy.INDEX_NAME);
Transaction tx = graphDatabaseService.beginTx();
try
{
relationshipTypeRepresentationStrategy.preEntityRemoval(link);
tx.success();
}
finally
{
tx.finish();
}
IndexHits<Relationship> linkHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, link.getClass().getName());
assertNull(linkHits.getSingle());
}
@Test
@Transactional
public void testFindAllOfRelationshipBacked() throws Exception {
assertEquals("Did not find all links.",
Arrays.asList(link),
IteratorUtil.addToCollection(relationshipTypeRepresentationStrategy.findAll(Link.class), new ArrayList<Link>()));
}
@Test
@Transactional
public void testCountOfRelationshipBacked() throws Exception {
assertEquals(1, relationshipTypeRepresentationStrategy.count(Link.class));
}
@Test
@Transactional
public void testGetJavaTypeOfRelationshipBacked() throws Exception {
assertEquals(Link.class, relationshipTypeRepresentationStrategy.getJavaType(rel(link)));
}
@Test
@Transactional
public void testCreateEntityAndInferType() throws Exception {
Link newLink = relationshipTypeRepresentationStrategy.createEntity(rel(link));
assertEquals(link, newLink);
}
@Test
@Transactional
public void testCreateEntityAndSpecifyType() throws Exception {
Link newLink = relationshipTypeRepresentationStrategy.createEntity(rel(link), Link.class);
assertEquals(link, newLink);
}
@Test
@Transactional
public void testProjectEntity() throws Exception {
UnrelatedLink other = relationshipTypeRepresentationStrategy.projectEntity(rel(link), UnrelatedLink.class);
assertEquals("link", other.getLabel());
}
private static Relationship rel(Link link) {
return link.getPersistentState();
}
private void createThingsAndLinks() {
Transaction tx = graphDatabaseService.beginTx();
try {
Node n1 = graphDatabaseService.createNode();
Node n2 = graphDatabaseService.createNode();
Relationship rel = n1.createRelationshipTo(n2, DynamicRelationshipType.withName("link"));
link = new Link(rel);
relationshipTypeRepresentationStrategy.postEntityCreation(rel, Link.class);
link.setLabel("link");
tx.success();
} finally {
tx.finish();
}
}
@RelationshipEntity
public static class UnrelatedLink {
String label;
public String getLabel() {
return label;
}
}
@RelationshipEntity
public static class Link {
String label;
public Link() {
}
public Link(Relationship rel) {
setPersistentState(rel);
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
}
public static class SubLink extends Link {
public SubLink() {
}
public SubLink(Relationship rel) {
super(rel);
}
}
private static Set<Node> set(Node... nodes) {
return new HashSet<Node>(Arrays.asList(nodes));
}
private void manualCleanDb() {
Transaction tx = graphDatabaseService.beginTx();
try {
cleanDb();
tx.success();
} finally {
tx.finish();
}
}
}

View File

@@ -1,274 +0,0 @@
package org.springframework.data.graph.neo4j.support;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.graph.annotation.EndNode;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.data.graph.annotation.RelationshipEntity;
import org.springframework.data.graph.annotation.StartNode;
import org.springframework.data.graph.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.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
"classpath:org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class IndexingTypeRepresentationStrategyTest {
@Autowired
private GraphDatabaseService graphDatabaseService;
@Autowired
private IndexingTypeRepresentationStrategy typeRepresentationStrategy;
private Thing thing;
private SubThing subThing;
private Link link;
@BeforeTransaction
public void cleanDb() {
Neo4jHelper.cleanDb(graphDatabaseService);
}
@Before
public void setUp() throws Exception {
if (thing == null) {
createThingsAndLinks();
}
}
@Test
@Transactional
public void testPostEntityCreationOfNodeBacked() throws Exception {
Index<Node> typesIndex = graphDatabaseService.index().forNodes(IndexingTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Node> thingHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, thing.getClass().getName());
assertEquals(set(node(thing), node(subThing)), IteratorUtil.addToCollection((Iterable<Node>)thingHits, new HashSet<Node>()));
IndexHits<Node> subThingHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, subThing.getClass().getName());
assertEquals(node(subThing), subThingHits.getSingle());
assertEquals(thing.getClass().getName(), node(thing).getProperty(IndexingTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
assertEquals(subThing.getClass().getName(), node(subThing).getProperty(IndexingTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
}
@Test
public void testPreEntityRemovalOfNodeBacked() throws Exception {
manualCleanDb();
createThingsAndLinks();
Index<Node> typesIndex = graphDatabaseService.index().forNodes(IndexingTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Node> thingHits;
IndexHits<Node> subThingHits;
Transaction tx = graphDatabaseService.beginTx();
try
{
typeRepresentationStrategy.preEntityRemoval(thing);
tx.success();
}
finally
{
tx.finish();
}
thingHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, thing.getClass().getName());
assertEquals(node(subThing), thingHits.getSingle());
subThingHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, subThing.getClass().getName());
assertEquals(node(subThing), subThingHits.getSingle());
tx = graphDatabaseService.beginTx();
try
{
typeRepresentationStrategy.preEntityRemoval(subThing);
tx.success();
}
finally
{
tx.finish();
}
thingHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, thing.getClass().getName());
assertNull(thingHits.getSingle());
subThingHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, subThing.getClass().getName());
assertNull(subThingHits.getSingle());
}
@Test
@Transactional
public void testFindAllOfNodeBacked() throws Exception {
assertEquals("Did not find all things.",
new HashSet<Thing>(Arrays.asList(subThing, thing)),
IteratorUtil.addToCollection(typeRepresentationStrategy.findAll(Thing.class), new HashSet<Thing>()));
}
@Test
@Transactional
public void testCountOfNodeBacked() throws Exception {
assertEquals(2, typeRepresentationStrategy.count(Thing.class));
}
@Test
@Transactional
public void testGetJavaTypeOfNodeBacked() throws Exception {
assertEquals(Thing.class, typeRepresentationStrategy.getJavaType(node(thing)));
assertEquals(SubThing.class, typeRepresentationStrategy.getJavaType(node(subThing)));
}
@Test
@Transactional
public void testConfirmTypeOfNodeBacked() throws Exception {
assertEquals(Thing.class, typeRepresentationStrategy.confirmType(node(thing), Thing.class));
assertEquals(SubThing.class, typeRepresentationStrategy.confirmType(node(subThing), Thing.class));
}
@Test
@Transactional
public void testPostEntityCreationOfRelationshipBacked() throws Exception {
Index<Relationship> typesIndex = graphDatabaseService.index().forRelationships(IndexingTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Relationship> linkHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, link.getClass().getName());
Relationship rel = linkHits.getSingle();
assertEquals(rel(link), rel);
assertEquals(link.getClass().getName(), rel.getProperty("__type__"));
}
@Test
public void testPreEntityRemovalOfRelationshipBacked() throws Exception {
manualCleanDb();
createThingsAndLinks();
Index<Relationship> typesIndex = graphDatabaseService.index().forRelationships(IndexingTypeRepresentationStrategy.INDEX_NAME);
Transaction tx = graphDatabaseService.beginTx();
try
{
typeRepresentationStrategy.preEntityRemoval(link);
tx.success();
}
finally
{
tx.finish();
}
IndexHits<Relationship> linkHits = typesIndex.get(IndexingTypeRepresentationStrategy.INDEX_KEY, link.getClass().getName());
assertNull(linkHits.getSingle());
}
@Test
@Transactional
public void testFindAllOfRelationshipBacked() throws Exception {
assertEquals("Did not find all links.",
Arrays.asList(link),
IteratorUtil.addToCollection(typeRepresentationStrategy.findAll(Link.class), new ArrayList<Link>()));
}
@Test
@Transactional
public void testCountOfRelationshipBacked() throws Exception {
assertEquals(1, typeRepresentationStrategy.count(Link.class));
}
@Test
@Transactional
public void testGetJavaTypeOfRelationshipBacked() throws Exception {
assertEquals(Link.class, typeRepresentationStrategy.getJavaType(rel(link)));
}
@Test
@Transactional
public void testConfirmTypeOfRelationshipBacked() throws Exception {
assertEquals(Link.class, typeRepresentationStrategy.confirmType(rel(link), Link.class));
}
private static Node node(Thing thing) {
return thing.getPersistentState();
}
private static Relationship rel(Link link) {
return link.getPersistentState();
}
private Thing createThingsAndLinks() {
Transaction tx = graphDatabaseService.beginTx();
try {
thing = new Thing(graphDatabaseService.createNode());
typeRepresentationStrategy.postEntityCreation(thing);
subThing = new SubThing(graphDatabaseService.createNode());
typeRepresentationStrategy.postEntityCreation(subThing);
link = thing.linkTo(subThing);
typeRepresentationStrategy.postEntityCreation(link);
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Thing {
String name;
Link link;
public Thing(Node node) {
setPersistentState(node);
}
public Link linkTo(Thing thing) {
return relateTo(thing, Link.class, "link");
}
}
public static class SubThing extends Thing {
public SubThing(Node node) {
super(node);
}
}
@RelationshipEntity
public static class Link {
String label;
@StartNode
Thing start;
@EndNode
Thing end;
public Link() {
}
public Link(String label) {
this.label = label;
}
}
private static Set<Node> set(Node... nodes) {
return new HashSet<Node>(Arrays.asList(nodes));
}
private void manualCleanDb() {
Transaction tx = graphDatabaseService.beginTx();
try {
cleanDb();
tx.success();
} finally {
tx.finish();
}
}
}

View File

@@ -1,12 +1,7 @@
package org.springframework.data.graph.neo4j.support;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@@ -14,81 +9,79 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import static org.junit.Assert.assertEquals;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
"classpath:org/springframework/data/graph/neo4j/support/NoopNodeTypeStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class NoopTypeRepresentationStrategyTest {
@Autowired
private GraphDatabaseContext graphDatabaseContext;
@Autowired
private NoopTypeRepresentationStrategy nodeTypeStrategy;
private Thing thing;
@Before
public void setUp() throws Exception {
thing = createThing();
}
//
// @Autowired
// private GraphDatabaseContext graphDatabaseContext;
// @Autowired
// private NoopTypeRepresentationStrategy nodeTypeStrategy;
//
// private Thing thing;
//
// @Before
// public void setUp() throws Exception {
// thing = createThing();
// }
@Test
public void testPostEntityCreation() throws Exception {
}
@Test(expected = UnsupportedOperationException.class)
public void testFindAll() throws Exception {
nodeTypeStrategy.findAll(Thing.class);
}
@Test(expected = UnsupportedOperationException.class)
public void testCount() throws Exception {
nodeTypeStrategy.count(Thing.class);
}
@Test(expected = UnsupportedOperationException.class)
public void testGetJavaType() throws Exception {
nodeTypeStrategy.getJavaType(node(thing));
}
@Test
public void testPreEntityRemoval() throws Exception {
nodeTypeStrategy.preEntityRemoval(thing);
}
@Test
public void testConfirmType() throws Exception {
assertEquals(Thing.class, nodeTypeStrategy.confirmType(node(thing), Thing.class));
}
private static Node node(Thing thing) {
return thing.getPersistentState();
}
private Thing createThing() {
Transaction tx = graphDatabaseContext.beginTx();
try {
Node node = graphDatabaseContext.createNode();
Thing thing = new Thing(node);
nodeTypeStrategy.postEntityCreation(thing);
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Thing {
String name;
public Thing() {
}
public Thing(Node n) {
setPersistentState(n);
}
}
//
// @Test(expected = UnsupportedOperationException.class)
// public void testFindAll() throws Exception {
// nodeTypeStrategy.findAll(Thing.class);
// }
//
// @Test(expected = UnsupportedOperationException.class)
// public void testCount() throws Exception {
// nodeTypeStrategy.count(Thing.class);
// }
//
// @Test(expected = UnsupportedOperationException.class)
// public void testGetJavaType() throws Exception {
// nodeTypeStrategy.getJavaType(node(thing));
// }
//
// @Test
// public void testPreEntityRemoval() throws Exception {
// nodeTypeStrategy.preEntityRemoval(thing);
// }
//
// @Test
// public void testConfirmType() throws Exception {
// assertEquals(Thing.class, nodeTypeStrategy.confirmType(node(thing), Thing.class));
// }
//
// private static Node node(Thing thing) {
// return thing.getPersistentState();
// }
//
// private Thing createThing() {
// Transaction tx = graphDatabaseContext.beginTx();
// try {
// Node node = graphDatabaseContext.createNode();
// Thing thing = new Thing(node);
// nodeTypeStrategy.postEntityCreation(thing);
// tx.success();
// return thing;
// } finally {
// tx.finish();
// }
// }
//
// @NodeEntity
// public static class Thing {
// String name;
//
// public Thing() {
// }
//
// public Thing(Node n) {
// setPersistentState(n);
// }
// }
}

View File

@@ -2,9 +2,7 @@ package org.springframework.data.graph.neo4j.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.Direction;
@@ -14,7 +12,6 @@ import org.neo4j.graphdb.Transaction;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.graph.annotation.NodeEntity;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.neo4j.Car;
import org.springframework.data.graph.neo4j.Person;
import org.springframework.data.graph.neo4j.Toyota;
@@ -32,8 +29,10 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import static org.springframework.data.graph.neo4j.Person.persistedPerson;
/**
@@ -42,10 +41,9 @@ import static org.springframework.data.graph.neo4j.Person.persistedPerson;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml",
"classpath:org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyOverride-context.xml"})
"classpath:org/springframework/data/graph/neo4j/support/SubReferenceTypeRepresentationStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
@Ignore
public class SubReferenceTypeRepresentationStrategyTest {
public class SubReferenceNodeTypeRepresentationStrategyTest {
protected final Log log = LogFactory.getLog(getClass());
@@ -54,9 +52,11 @@ public class SubReferenceTypeRepresentationStrategyTest {
@Autowired
private DirectGraphRepositoryFactory graphRepositoryFactory;
@Autowired
private SubReferenceTypeRepresentationStrategy nodeTypeStrategy;
private SubReferenceNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
private Node thingNode;
private Thing thing;
private SubThing subThing;
private Node subThingNode;
@BeforeTransaction
@@ -66,92 +66,96 @@ public class SubReferenceTypeRepresentationStrategyTest {
@Before
public void setUp() {
thingNode = createThing();
createThing();
}
@Test
@Transactional
public void testPostEntityCreation() throws Exception {
Node typeNode = getInstanceofRelationship().getOtherNode(thingNode);
Assert.assertNotNull("type node for thing exists", typeNode);
Assert.assertEquals("type node has property of type Thing.class", Thing.class.getName(), typeNode.getProperty(SubReferenceTypeRepresentationStrategy.SUBREF_CLASS_KEY));
Assert.assertEquals("one thing has been created", 1, typeNode.getProperty(SubReferenceTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
Node typeNode = getInstanceofRelationship(thingNode).getOtherNode(thingNode);
assertNotNull("type node for thing exists", typeNode);
assertEquals("type node has property of type Thing.class", Thing.class.getName(), typeNode.getProperty(SubReferenceNodeTypeRepresentationStrategy.SUBREF_CLASS_KEY));
assertEquals("one thing has been created", 2, typeNode.getProperty(SubReferenceNodeTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
}
@Test(expected = IllegalArgumentException.class)
public void gettingTypeFromNonTypeNodeShouldThrowAnDescriptiveException() throws Exception {
Node referenceNode = graphDatabaseContext.getReferenceNode();
nodeTypeStrategy.getJavaType(referenceNode);
}
@Test(expected = IllegalArgumentException.class)
public void confirmingTypeOfNonTypeNodeShouldThrowAnDescriptiveException() throws Exception {
Node referenceNode = graphDatabaseContext.getReferenceNode();
nodeTypeStrategy.confirmType(referenceNode, Thing.class);
nodeTypeRepresentationStrategy.getJavaType(referenceNode);
}
@Test(expected = IllegalArgumentException.class)
public void gettingTypeFromNullShouldFail() throws Exception {
nodeTypeStrategy.getJavaType(null);
nodeTypeRepresentationStrategy.getJavaType(null);
}
private Node createThing() {
private void createThing() {
Transaction tx = graphDatabaseContext.beginTx();
try {
Node node = graphDatabaseContext.createNode();
thing = new Thing(node);
nodeTypeStrategy.postEntityCreation(thing);
thingNode = graphDatabaseContext.createNode();
thing = new Thing(thingNode);
nodeTypeRepresentationStrategy.postEntityCreation(thingNode, Thing.class);
thing.setName("thing");
subThingNode = graphDatabaseContext.createNode();
subThing = new SubThing(subThingNode);
nodeTypeRepresentationStrategy.postEntityCreation(subThingNode, SubThing.class);
subThing.setName("subThing");
tx.success();
return node;
} finally {
tx.finish();
}
}
private static Node node(Thing thing) {
return thing.getPersistentState();
}
@Test
@Transactional
public void testPreEntityRemoval() throws Exception {
Node typeNode = getInstanceofRelationship().getOtherNode(thingNode);
nodeTypeStrategy.preEntityRemoval(thing);
Assert.assertNull("instanceof relationship was removed", getInstanceofRelationship());
Assert.assertEquals("no things left after removal", 0, typeNode.getProperty(SubReferenceTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
Node typeNode = getInstanceofRelationship(thingNode).getOtherNode(thingNode);
nodeTypeRepresentationStrategy.preEntityRemoval(thing);
assertNull("instanceof relationship was removed", getInstanceofRelationship(thingNode));
assertNotNull("instanceof relationship was removed", getInstanceofRelationship(subThingNode));
assertEquals("no things left after removal", 1, typeNode.getProperty(SubReferenceNodeTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
nodeTypeRepresentationStrategy.preEntityRemoval(subThing);
assertNull("instanceof relationship was removed", getInstanceofRelationship(subThingNode));
assertEquals("no things left after removal", 0, typeNode.getProperty(SubReferenceNodeTypeRepresentationStrategy.SUBREFERENCE_NODE_COUNTER_KEY));
}
@Transactional
private Relationship getInstanceofRelationship() {
return thingNode.getSingleRelationship(SubReferenceTypeRepresentationStrategy.INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
private Relationship getInstanceofRelationship(Node node) {
return node.getSingleRelationship(SubReferenceNodeTypeRepresentationStrategy.INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING);
}
@Test
@Transactional
public void testCount() throws Exception {
Assert.assertEquals("one thing created", 1, nodeTypeStrategy.count(Thing.class));
assertEquals("one thing created", 2, nodeTypeRepresentationStrategy.count(Thing.class));
assertEquals("one thing created", 1, nodeTypeRepresentationStrategy.count(SubThing.class));
}
@Test
@Transactional
public void testGetJavaType() throws Exception {
Assert.assertEquals("class in graph is thing", Thing.class, nodeTypeStrategy.<NodeBacked>getJavaType(thingNode));
assertEquals("class in graph is thing", Thing.class, nodeTypeRepresentationStrategy.getJavaType(thingNode));
}
@Test
@Transactional
public void testConfirmType() throws Exception {
Assert.assertEquals("class in graph is thing", Thing.class, nodeTypeStrategy.confirmType(thingNode,Thing.class));
public void testFindAllThings() throws Exception {
Collection<Thing> things = IteratorUtil.asCollection(nodeTypeRepresentationStrategy.findAll(Thing.class));
assertEquals("one thing created and found", 2, things.size());
}
@Test
@Transactional
public void testFindAll() throws Exception {
Collection<Thing> things = IteratorUtil.asCollection(nodeTypeStrategy.findAll(Thing.class));
Assert.assertEquals("one thing created and found", 1, things.size());
Assert.assertTrue("result only contains Thing", things.iterator().next() instanceof Thing);
public void testFindAllSubThings() {
Collection<SubThing> things = IteratorUtil.asCollection(nodeTypeRepresentationStrategy.findAll(SubThing.class));
assertEquals("one thing created and found", Collections.<SubThing>singleton(subThing), new HashSet<SubThing>(things));
}
@Test
@Transactional
public void testInstantiateConcreteClass() {
@@ -191,6 +195,37 @@ public class SubReferenceTypeRepresentationStrategyTest {
assertEquals("Wrong Person instance count.", (Long)2L, graphRepositoryFactory.createNodeEntityRepository(Person.class).count());
}
@Test
@Transactional
public void testCreateEntityAndInferType() throws Exception {
Thing newThing = nodeTypeRepresentationStrategy.createEntity(node(thing));
assertEquals(thing, newThing);
}
@Test
@Transactional
public void testCreateEntityAndSpecifyType() throws Exception {
Thing newThing = nodeTypeRepresentationStrategy.createEntity(node(subThing), Thing.class);
assertEquals(subThing, newThing);
}
@Test
@Transactional
public void testProjectEntity() throws Exception {
Unrelated other = nodeTypeRepresentationStrategy.projectEntity(node(thing), Unrelated.class);
assertEquals("thing", other.getName());
}
@NodeEntity
public static class Unrelated {
String name;
public String getName() {
return name;
}
}
@NodeEntity
public static class Thing {
String name;
@@ -201,5 +236,22 @@ public class SubReferenceTypeRepresentationStrategyTest {
public Thing(Node n) {
setPersistentState(n);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class SubThing extends Thing {
public SubThing(Node n) {
super(n);
}
public SubThing() {
}
}
}

View File

@@ -88,29 +88,29 @@
<bean id="graphDatabaseContext" class="org.springframework.data.graph.neo4j.support.GraphDatabaseContext">
<property name="graphDatabaseService" ref="graphDatabaseService"/>
<property name="relationshipEntityInstantiator" ref="relationshipEntityInstantiator"/>
<property name="graphEntityInstantiator">
<bean class="org.springframework.data.graph.neo4j.support.node.PartialNeo4jEntityInstantiator">
<constructor-arg ref="graphEntityInstantiator"/>
<constructor-arg ref="entityManagerFactory"/>
</bean>
</property>
<property name="conversionService">
<bean class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
</property>
<property name="typeRepresentationStrategy" ref="typeRepresentationStrategy"/>
<property name="nodeTypeRepresentationStrategy" ref="nodeTypeRepresentationStrategy"/>
<property name="relationshipTypeRepresentationStrategy" ref="relationshipTypeRepresentationStrategy"/>
</bean>
<bean id="graphEntityInstantiator" class="org.springframework.data.graph.neo4j.support.node.PartialNeo4jEntityInstantiator">
<constructor-arg>
<bean class="org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator" />
</constructor-arg>
<constructor-arg ref="entityManagerFactory"/>
</bean>
<bean id="relationshipEntityInstantiator"
class="org.springframework.data.graph.neo4j.support.relationship.ConstructorBypassingGraphRelationshipInstantiator"/>
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactoryBean">
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactory">
<constructor-arg ref="graphDatabaseService"/>
<constructor-arg ref="graphEntityInstantiator"/>
<constructor-arg ref="relationshipEntityInstantiator"/>
</bean>
<bean id="graphEntityInstantiator"
class="org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator"/>
<bean id="nodeTypeRepresentationStrategy" factory-bean="typeRepresentationStrategyFactory" factory-method="getNodeTypeRepresentationStrategy" />
<bean id="relationshipTypeRepresentationStrategy" factory-bean="typeRepresentationStrategyFactory" factory-method="getRelationshipTypeRepresentationStrategy"/>
<bean id="nodeEntityStateFactory" class="org.springframework.data.graph.neo4j.fieldaccess.NodeEntityStateFactory">
<property name="nodeDelegatingFieldAccessorFactory">

View File

@@ -14,9 +14,12 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.IndexingTypeRepresentationStrategy">
<bean id="nodeTypeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.IndexingNodeTypeRepresentationStrategy">
<constructor-arg ref="graphDatabaseService" />
<constructor-arg ref="graphEntityInstantiator" />
</bean>
<bean id="relationshipTypeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.IndexingRelationshipTypeRepresentationStrategy">
<constructor-arg ref="graphDatabaseService" />
<constructor-arg ref="relationshipEntityInstantiator" />
</bean>
</beans>

View File

@@ -84,12 +84,11 @@
<bean id="graphDatabaseContext" class="org.springframework.data.graph.neo4j.support.GraphDatabaseContext">
<property name="graphDatabaseService" ref="graphDatabaseService"/>
<property name="relationshipEntityInstantiator" ref="relationshipEntityInstantiator"/>
<property name="graphEntityInstantiator" ref="graphEntityInstantiator"/>
<property name="conversionService">
<bean class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
</property>
<property name="typeRepresentationStrategy" ref="typeRepresentationStrategy"/>
<property name="nodeTypeRepresentationStrategy" ref="nodeTypeRepresentationStrategy"/>
<property name="relationshipTypeRepresentationStrategy" ref="relationshipTypeRepresentationStrategy"/>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
@@ -101,12 +100,15 @@
<bean id="graphEntityInstantiator"
class="org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator"/>
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactoryBean">
<constructor-arg ref="graphDatabaseService" />
<constructor-arg ref="graphEntityInstantiator" />
<constructor-arg ref="relationshipEntityInstantiator" />
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.graph.neo4j.support.TypeRepresentationStrategyFactory">
<constructor-arg ref="graphDatabaseService"/>
<constructor-arg ref="graphEntityInstantiator"/>
<constructor-arg ref="relationshipEntityInstantiator"/>
</bean>
<bean id="nodeTypeRepresentationStrategy" factory-bean="typeRepresentationStrategyFactory" factory-method="getNodeTypeRepresentationStrategy" />
<bean id="relationshipTypeRepresentationStrategy" factory-bean="typeRepresentationStrategyFactory" factory-method="getRelationshipTypeRepresentationStrategy"/>
<bean id="nodeEntityStateFactory" class="org.springframework.data.graph.neo4j.fieldaccess.NodeEntityStateFactory">
<property name="nodeDelegatingFieldAccessorFactory">
<bean class="org.springframework.data.graph.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory">

View File

@@ -14,5 +14,5 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.NoopTypeRepresentationStrategy" />
<!--<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.NoopTypeRepresentationStrategy" />-->
</beans>

View File

@@ -14,7 +14,7 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.SubReferenceTypeRepresentationStrategy">
<bean id="typeRepresentationStrategy" class="org.springframework.data.graph.neo4j.support.SubReferenceNodeTypeRepresentationStrategy">
<constructor-arg ref="graphDatabaseService"/>
<constructor-arg ref="graphEntityInstantiator" />
</bean>