diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/TraversalTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/TraversalTest.java index 28ad301f7..afb27a3d3 100644 --- a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/TraversalTest.java +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/aspects/support/TraversalTest.java @@ -53,7 +53,7 @@ public class TraversalTest extends EntityTestBase { group.setName("dev"); group.addPerson(p); final TraversalDescription traversalDescription = Traversal.description().relationships(DynamicRelationshipType.withName("persons")).evaluator(Evaluators.excludeStartPosition()); - Iterable people = neo4jTemplate.findAllByTraversal(group,Person.class, traversalDescription); + Iterable people = neo4jTemplate.traverse(group, Person.class, traversalDescription); final HashSet found = new HashSet(); for (Person person : people) { found.add(person); @@ -69,7 +69,7 @@ public class TraversalTest extends EntityTestBase { group.setName("dev"); group.addPerson(p); final TraversalDescription traversalDescription = Traversal.description().relationships(DynamicRelationshipType.withName("persons"), Direction.OUTGOING).evaluator(Evaluators.excludeStartPosition()); - Iterable> paths = (Iterable>) neo4jTemplate.>findAllByTraversal(group, EntityPath.class, traversalDescription); + Iterable> paths = (Iterable>) neo4jTemplate.>traverse(group, EntityPath.class, traversalDescription); for (EntityPath path : paths) { assertEquals(group, path.startEntity()); assertEquals(p, path.endEntity()); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java index dbc3bbef3..4eae5e582 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java @@ -104,7 +104,7 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory { @Override public Object getValue(final Object entity) { final TraversalDescription traversalDescription = fieldTraversalDescriptionBuilder.build(entity, property,params); - return doReturn(template.findAllByTraversal(entity,target, traversalDescription)); + return doReturn(template.traverse(entity, target, traversalDescription)); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java index 88e56872e..d6b517862 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java @@ -231,13 +231,13 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty extends AbstractGraphRepository imp @Override public Iterable findAllByTraversal(final N start, final TraversalDescription traversalDescription) { - return template.findAllByTraversal(start, clazz, traversalDescription); + return template.traverse(start, clazz, traversalDescription); } @SuppressWarnings("unchecked") diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructure.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructure.java index cd2c17be6..45a6e226e 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructure.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/MappingInfrastructure.java @@ -213,4 +213,8 @@ public class MappingInfrastructure { public CypherQueryExecutor getCypherQueryExecutor() { return cypherQueryExecutor; } + + public Neo4jMappingContext getMappingContext() { + return mappingContext; + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/Neo4jTemplate.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/Neo4jTemplate.java index 9b6cd84c9..22869f275 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/Neo4jTemplate.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/Neo4jTemplate.java @@ -25,9 +25,7 @@ import org.neo4j.helpers.collection.ClosableIterable; import org.springframework.core.convert.ConversionService; import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.QueryType; -import org.springframework.data.neo4j.annotation.RelationshipEntity; import org.springframework.data.neo4j.conversion.QueryResultBuilder; import org.springframework.data.neo4j.conversion.Result; import org.springframework.data.neo4j.conversion.ResultConverter; @@ -99,8 +97,10 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { } + @Override @SuppressWarnings({"unchecked"}) public GraphRepository repositoryFor(Class clazz) { + notNull(clazz,"entity type"); if (isNodeEntity(clazz)) return new NodeGraphRepository(clazz, this); if (isRelationshipEntity(clazz)) return new RelationshipGraphRepository(clazz, this); throw new IllegalArgumentException("Can't create graph repository for non graph entity of type " + clazz); @@ -108,13 +108,16 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { public Index getIndex(Class type) { + notNull(type, "entity type"); return infrastructure.getIndexProvider().getIndex(type, null); } public Index getIndex(String name) { + notNull(name, "index name"); return infrastructure.getIndexProvider().getIndex(null, name); } + @Override public Index getIndex(Class type, String indexName) { return infrastructure.getIndexProvider().getIndex(type, indexName, null); } @@ -132,33 +135,44 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { } + @Override public ClosableIterable findAll(final Class entityClass) { + notNull(entityClass,"entity type"); return infrastructure.getTypeRepresentationStrategies().findAll(entityClass); } + @Override public long count(final Class entityClass) { + notNull(entityClass,"entity type"); return infrastructure.getTypeRepresentationStrategies().count(entityClass); } public T createEntityFromStoredType(S state) { + notNull(state,"node or relationship"); return infrastructure.getEntityPersister().createEntityFromStoredType(state); } public T createEntityFromState(S state, Class type) { + notNull(state,"node or relationship",type,"entity class"); return infrastructure.getEntityPersister().createEntityFromState(state, type); } + @Override public T projectTo(Object entity, Class targetType) { + notNull(entity,"entity",targetType,"new entity class"); return infrastructure.getEntityPersister().projectTo(entity, targetType); } + @Override @SuppressWarnings("unchecked") public S getPersistentState(Object entity) { + notNull(entity,"entity"); return infrastructure.getEntityPersister().getPersistentState(entity); } @SuppressWarnings("unchecked") public T setPersistentState(T entity, S state) { + notNull(entity,"entity",state,"node or relationship"); infrastructure.getEntityPersister().setPersistentState(entity, state); return entity; } @@ -168,7 +182,9 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { infrastructure.getTypeRepresentationStrategies().postEntityCreation(node, entityClass); } + @Override public void remove(Object entity) { + notNull(entity,"entity"); infrastructure.getEntityRemover().remove(entity); } @@ -193,7 +209,8 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { return infrastructure.getGraphDatabase().createNode(properties); } - public T createNode(Class target, Map properties) { + @Override + public T createNodeAs(Class target, Map properties) { final Node node = createNode(properties); if (isNodeEntity(target)) { infrastructure.getTypeRepresentationStrategies().postEntityCreation(node, target); @@ -201,6 +218,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { return convert(node, target); } + @Override public Result createNodes(Map firstNode, Map... otherNodes) { Collection result = new ArrayList(otherNodes.length + 1); result.add(createNode(firstNode)); @@ -210,7 +228,8 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { return convert(result); } - public Iterable createNodes(Class target, Map firstNode, Map... otherNodes) { + @Override + public Iterable createNodesAs(Class target, Map firstNode, Map... otherNodes) { final TypeRepresentationStrategy nodeTypeRepresentationStrategy = isNodeEntity(target) ? infrastructure.getTypeRepresentationStrategies().getNodeTypeRepresentationStrategy() : null; Collection result = new ArrayList(otherNodes.length + 1); result.add(createNode(firstNode, target, nodeTypeRepresentationStrategy)); @@ -243,13 +262,14 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { public boolean isNodeEntity(Class targetType) { - return targetType.isAnnotationPresent(NodeEntity.class); + return infrastructure.getMappingContext().isNodeEntity(targetType); } public boolean isRelationshipEntity(Class targetType) { - return targetType.isAnnotationPresent(RelationshipEntity.class); + return infrastructure.getMappingContext().isRelationshipEntity(targetType); } + @Override @SuppressWarnings("unchecked") public T save(T entity) { return (T) infrastructure.getEntityPersister().persist(entity); @@ -259,6 +279,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { return infrastructure.getEntityStateHandler().isManaged(entity); } + @Override public Object query(String statement, Map params, final TypeInformation typeInformation) { final TypeInformation actualType = typeInformation.getActualType(); final Class targetType = actualType.getType(); @@ -271,6 +292,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { return infrastructure.getCypherQueryExecutor().queryForObject(statement, targetType, params); } + @Override public R getRelationshipBetween(Object start, Object end, Class relationshipEntityClass, String relationshipType) { notNull(start,"start",end,"end",relationshipEntityClass,"relationshipEntityClass",relationshipType,"relationshipType"); final Relationship relationship = infrastructure.getEntityStateHandler().getRelationshipTo(start, end, relationshipType); @@ -278,11 +300,13 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { return infrastructure.getEntityPersister().createEntityFromState(relationship, relationshipEntityClass); } + @Override public void removeRelationshipBetween(Object start, Object end, String type) { notNull(start,"start",end,"end",type,"relationshipType"); infrastructure.getEntityRemover().removeRelationshipTo(start, end, type); } + @Override public R createRelationshipBetween(Object start, Object end, Class relationshipEntityClass, String relationshipType, boolean allowDuplicates) { notNull(start,"start",end,"end",relationshipEntityClass,"relationshipEntityClass",relationshipType,"relationshipType"); final RelationshipResult result = infrastructure.getEntityStateHandler().relateTo(start, end, relationshipType, allowDuplicates); @@ -420,14 +444,16 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { } + @Override @SuppressWarnings("unchecked") - public Iterable findAllByTraversal(Object entity, Class targetType, TraversalDescription traversalDescription) { + public Iterable traverse(Object entity, Class targetType, TraversalDescription traversalDescription) { + notNull(entity,"entity",targetType,"target type",traversalDescription,"traversal description"); return traverse(entity, traversalDescription).to((Class) targetType); } @Override public Result traverse(Node startNode, TraversalDescription traversal) { - notNull(startNode, "startNode", traversal, "traversal"); + notNull(startNode, "start node", traversal, "traversal"); try { return this.convert(traversal.traverse(startNode)); } catch (RuntimeException e) { @@ -437,7 +463,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister { @Override public Result lookup(String indexName, String field, Object value) { - notNull(field, "field", value, "value", indexName, "indexName"); + notNull(field, "field", value, "value", indexName, "index name"); try { Index index = getIndex(null, indexName); return convert(index.get(field, value)); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ParameterCheck.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ParameterCheck.java index 3f70f3f76..49a7b933d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ParameterCheck.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ParameterCheck.java @@ -22,6 +22,18 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; * @since 17.10.11 */ public class ParameterCheck { + public static void notNull(Object value, String msg) { + if (value==null) throw new InvalidDataAccessApiUsageException("[Assertion failed] - " + msg + " is required; it must not be null"); + } + public static void notNull(Object value, String msg,Object value2, String msg2) { + if (value==null) throw new InvalidDataAccessApiUsageException("[Assertion failed] - " + msg + " is required; it must not be null"); + if (value2==null) throw new InvalidDataAccessApiUsageException("[Assertion failed] - " + msg2 + " is required; it must not be null"); + } + public static void notNull(Object value, String msg,Object value2, String msg2,Object value3, String msg3) { + if (value==null) throw new InvalidDataAccessApiUsageException("[Assertion failed] - " + msg + " is required; it must not be null"); + if (value2==null) throw new InvalidDataAccessApiUsageException("[Assertion failed] - " + msg2 + " is required; it must not be null"); + if (value3==null) throw new InvalidDataAccessApiUsageException("[Assertion failed] - " + msg3 + " is required; it must not be null"); + } public static void notNull(Object... pairs) { assert pairs.length % 2 == 0 : "wrong number of pairs to check"; for (int i = 0; i < pairs.length; i += 2) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java index 3217b6bed..7c10b8623 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/template/Neo4jOperations.java @@ -17,15 +17,20 @@ package org.springframework.data.neo4j.template; import org.neo4j.graphdb.*; +import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; +import org.neo4j.helpers.collection.ClosableIterable; import org.springframework.data.neo4j.annotation.QueryType; import org.springframework.data.neo4j.conversion.Result; +import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.neo4j.support.query.QueryEngine; +import org.springframework.data.util.TypeInformation; import java.util.Map; /** * A template with convenience operations, exception translation and implicit transaction for modifying methods + * * @author mh * @since 19.02.11 */ @@ -33,92 +38,72 @@ public interface Neo4jOperations { /** * Executes the callback in a NON-transactional context. + * * @param callback for executing graph operations NON-transactionally, not null - * @param return type + * @param return type * @return whatever the callback chooses to return - * @throws org.springframework.dao.DataAccessException subclasses + * @throws org.springframework.dao.DataAccessException + * subclasses */ T exec(GraphCallback callback); + GraphRepository repositoryFor(Class clazz); + + T getReferenceNode(Class target); + /** * Delegates to the GraphDatabase + * * @param id node id * @return the requested node of the underlying graph database * @throws NotFoundException */ Node getNode(long id); - /** - * Transactionally creates the node, sets the properties (if any) and indexes the given fields (if any). - * Two shortcut means of providing the properties (very short with static imports) - * template.createNode(Property._("name","value")); - * template.createNode(Property._("name","value","prop","anotherValue")); - * - * - * @param props properties to be set at node creation might be null - * @return the newly created node - */ - Node createNode(Map props); + Node createNode(Map props); + + Node createNode(); + + T createNodeAs(Class target, Map properties); + + Result createNodes(Map firstNode, Map... otherNodes); + + Iterable createNodesAs(Class target, Map firstNode, Map... otherNodes); + /** * Delegates to the GraphDatabase + * * @param id relationship id * @return the requested relationship of the underlying graph database * @throws NotFoundException */ Relationship getRelationship(long id); - /** - * Transactionally creates the relationship, sets the properties (if any) and indexes the given fielss (if any) - * Two shortcut means of providing the properties (very short with static imports) - * template.createRelationship(from,to,TYPE, Property._("name","value")); - * template.createRelationship(from,to,TYPE, Property._("name","value","prop","anotherValue")); - * - * @param startNode start-node of relationship - * @param endNode end-node of relationship - * @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name") - * @param props optional initial properties - * @return the newly created relationship - */ Relationship createRelationshipBetween(Node startNode, Node endNode, RelationshipType type, Map props); + R getRelationshipBetween(Object start, Object end, Class relationshipEntityClass, String relationshipType); + + void removeRelationshipBetween(Object start, Object end, String type); + + R createRelationshipBetween(Object start, Object end, Class relationshipEntityClass, String relationshipType, boolean allowDuplicates); + + + Index getIndex(Class type, String indexName); + /** * Indexes the given field and value for the element. + * * @param indexName Name of the index, will be checked against existing indexes according to the given element - * assumes a "node" node index or "relationship" relationship index for a null value - * @param element node or relationship to index - * @param field field to index - * @param value value to index - * @param the provided element type + * assumes a "node" node index or "relationship" relationship index for a null value + * @param element node or relationship to index + * @param field field to index + * @param value value to index + * @param the provided element type * @return the provided element for convenience */ T index(String indexName, T element, String field, Object value); - /** - * Converts the Iterable into a QueryResult object for uniform handling. E.g. - * template.convert(node.getRelationships()); - */ - Result convert(Iterable iterable); - - /** - * Runs the given cypher statement and packages the result in a QueryResult, simple conversions via the - * registered converter-factories are already executed via this method. - */ - Result> query(String statement,Map params); - - /** - * Executes the given Gremlin statement and returns the result packaged as QueryResult as Neo4j types, not - * Gremlin types. Table rows are converted to Map. - */ - Result execute(String statement, Map params); - - /** - * Traverses the graph starting at the given node with the provided traversal description. The Path's of the - * traversal will be packaged into a QueryResult which can be easily converted into Nodes, Relationships or - * Graph-Entities. - */ - Result traverse(Node startNode, TraversalDescription traversal); - /** * The value is looked up in the Neo4j index returning the IndexHits wrapped in a QueryResult to be converted * into Paths or Entities. @@ -131,20 +116,52 @@ public interface Neo4jOperations { */ Result lookup(String indexName, Object query); - Result traverse(Object start, TraversalDescription traversal); - Result lookup(Class indexedType, Object query); - Node createNode(); - - @SuppressWarnings("unchecked") - T convert(Object value, Class type); - - /** - * Delegates to the GraphDatabase - * @return the reference node of the underlying graph database - */ - T getReferenceNode(Class target); + Object query(String statement, Map params, TypeInformation typeInformation); QueryEngine queryEngineFor(QueryType type); + + /** + * Runs the given cypher statement and packages the result in a QueryResult, simple conversions via the + * registered converter-factories are already executed via this method. + */ + Result> query(String statement, Map params); + + /** + * Executes the given Gremlin statement and returns the result packaged as QueryResult as Neo4j types, not + * Gremlin types. Table rows are converted to Map. + */ + Result execute(String statement, Map params); + + /** + * Traverses the graph starting at the given node with the provided traversal description. The Path's of the + * traversal will be packaged into a QueryResult which can be easily converted into Nodes, Relationships or + * Graph-Entities. + */ + Result traverse(Node startNode, TraversalDescription traversal); + + Result traverse(Object start, TraversalDescription traversal); + + Iterable traverse(Object entity, Class targetType, TraversalDescription traversalDescription); + + /** + * Converts the Iterable into a QueryResult object for uniform handling. E.g. + * template.convert(node.getRelationships()); + */ + Result convert(Iterable iterable); + + T convert(Object value, Class type); + + ClosableIterable findAll(Class entityClass); + + long count(Class entityClass); + + T projectTo(Object entity, Class targetType); + + T save(T entity); + + void remove(Object entity); + + S getPersistentState(Object entity); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/GraphDatabaseContextTemplateTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/GraphDatabaseContextTemplateTest.java index c09a53411..244b2ba63 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/GraphDatabaseContextTemplateTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/template/GraphDatabaseContextTemplateTest.java @@ -203,7 +203,7 @@ public class GraphDatabaseContextTemplateTest { @Test @Transactional public void testCreateEntityWithProperties() throws Exception { - Person person = neo4jTemplate.createNode(Person.class, map("name", "name")); + Person person = neo4jTemplate.createNodeAs(Person.class, map("name", "name")); assertNotNull("created node", person); assertEquals("property created", "name", person.getName()); } @@ -211,7 +211,7 @@ public class GraphDatabaseContextTemplateTest { @Test @Transactional public void testCreateNodeTypeWithProperties() throws Exception { - Node person = neo4jTemplate.createNode(Node.class, map("name", "name")); + Node person = neo4jTemplate.createNodeAs(Node.class, map("name", "name")); assertNotNull("created node", person); assertEquals("property created", "name", person.getProperty("name")); }