diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/Finder.java b/src/main/java/org/springframework/persistence/graph/neo4j/Finder.java new file mode 100644 index 000000000..0399867ac --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/Finder.java @@ -0,0 +1,47 @@ +package org.springframework.persistence.graph.neo4j; + +import java.util.ArrayList; +import java.util.List; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.NotFoundException; +import org.neo4j.graphdb.Relationship; +import org.springframework.persistence.support.EntityInstantiator; + +public class Finder { + + private final Class clazz; + private final GraphDatabaseService graphDatabaseService; + private final EntityInstantiator graphEntityInstantiator; + + public Finder(Class clazz, GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + this.clazz = clazz; + this.graphDatabaseService = graphDatabaseService; + this.graphEntityInstantiator = graphEntityInstantiator; + } + + public long count() { + return Neo4jHelper.count(clazz, graphDatabaseService); + } + + public Iterable findAll() { + Node subrefNode = Neo4jHelper.findSubreferenceNode(clazz, graphDatabaseService); + // TODO add lazy list on top of graph + List result = new ArrayList((int) count()); + for (Relationship rel : subrefNode.getRelationships(Neo4jHelper.INSTANCE_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) { + Node node = rel.getStartNode(); + result.add(graphEntityInstantiator.createEntityFromState(node, clazz)); + } + return result; + } + + public T findById(long id) { + try { + return graphEntityInstantiator.createEntityFromState(graphDatabaseService.getNodeById(id), clazz); + } catch(NotFoundException e) { + return null; + } + } +} diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/FinderFactory.java b/src/main/java/org/springframework/persistence/graph/neo4j/FinderFactory.java new file mode 100644 index 000000000..1fba0d40b --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/FinderFactory.java @@ -0,0 +1,21 @@ +package org.springframework.persistence.graph.neo4j; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.springframework.persistence.support.EntityInstantiator; + +public class FinderFactory { + + private final GraphDatabaseService graphDatabaseService; + private final EntityInstantiator graphEntityInstantiator; + + public FinderFactory(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + this.graphDatabaseService = graphDatabaseService; + this.graphEntityInstantiator = graphEntityInstantiator; + } + + public Finder getFinderForClass(Class clazz) { + return new Finder(clazz, graphDatabaseService, graphEntityInstantiator); + } + +} diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jHelper.java b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jHelper.java index 378c12632..e1d016c04 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jHelper.java +++ b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jHelper.java @@ -3,6 +3,7 @@ package org.springframework.persistence.graph.neo4j; import org.neo4j.graphdb.DynamicRelationshipType; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.util.GraphDatabaseUtil; @@ -21,12 +22,20 @@ public abstract class Neo4jHelper { } public static long count(Class entityClass, GraphDatabaseService gds) { - // TODO: Need to figure out what to do here - // Node subrefNode = findSubreferenceNode(Person.class, gds); - // If the subref node is new, there are 0 instances of this entity class - // int count = ((Integer) subrefNode.getProperty(SUBREFERENCE_NODE_COUNTER_KEY, 0)); - // return count; - return 1; + Node subrefNode = findSubreferenceNode(entityClass, gds); + return (Integer) subrefNode.getProperty(SUBREFERENCE_NODE_COUNTER_KEY, 0); + } + + public static void cleanDb(GraphDatabaseService graphDatabaseService) { + Node refNode = graphDatabaseService.getReferenceNode(); + for (Node node : graphDatabaseService.getAllNodes()) { + for (Relationship rel : node.getRelationships()) { + rel.delete(); + } + if (!refNode.equals(node)) { + node.delete(); + } + } } } diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj index dd5df47c1..850367c3d 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj +++ b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj @@ -2,9 +2,11 @@ package org.springframework.persistence.graph.neo4j; import java.lang.reflect.Field; import java.util.AbstractSet; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import org.aspectj.lang.ProceedingJoinPoint; @@ -100,7 +102,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields fieldType = f.getType(); if (isPropertyType(fieldType)) { String propName = getNeo4jPropertyName(f); entity.getUnderlyingNode().setProperty(propName, newVal); @@ -158,21 +160,21 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields Neo4J relationship with value=[" + newVal + "]"); - Object result=relInfo.apply(entity, newVal); + Object result = relInfo.apply(entity, newVal); return proceed(entity,result); } catch(NotInTransactionException e) { throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e); } } - private boolean isPropertyType(Class fieldType) { - return fieldType.isPrimitive() || fieldType.equals(String.class) || (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType)); - // TODO boolean, arrays, character - } - - private boolean isSingleRelationshipField(Field f) { - return f.getType().isAnnotationPresent(GraphEntity.class); - //return NodeBacked.class.isAssignableFrom(f.getType()); + private boolean isPropertyType(Class fieldType) { + // todo: add array support + return fieldType.isPrimitive() + || (fieldType.isArray() && !fieldType.getComponentType().isArray() && isPropertyType(fieldType.getComponentType())) + || fieldType.equals(String.class) + || fieldType.equals(Character.class) + || fieldType.equals(Boolean.class) + || (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType)); } private static String getNeo4jPropertyName(Field field) { diff --git a/src/test/java/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest.java b/src/test/java/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest.java index 01f09d480..b559f1c6f 100644 --- a/src/test/java/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest.java +++ b/src/test/java/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest.java @@ -14,9 +14,13 @@ import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; +import org.neo4j.helpers.collection.IteratorUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.persistence.graph.neo4j.Finder; +import org.springframework.persistence.graph.neo4j.FinderFactory; +import org.springframework.persistence.graph.neo4j.Neo4jHelper; import org.springframework.persistence.graph.neo4j.NodeBacked; import org.springframework.persistence.support.EntityInstantiator; import org.springframework.persistence.test.Group; @@ -25,6 +29,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -43,6 +48,9 @@ public class Neo4jGraphPersistenceTest { @Autowired protected GraphDatabaseService graphDatabaseService; + @Autowired + private FinderFactory finderFactory; + private Long insertedId = 0L; @Test @@ -50,14 +58,24 @@ public class Neo4jGraphPersistenceTest { Assert.assertNotNull( graphDatabaseService ); Assert.assertNotNull( nodeInstantiator ); } - + @Before @Transactional + public void cleanDb() { + Neo4jHelper.cleanDb(graphDatabaseService); + } + + @Test + @Transactional public void testUserConstructor() { Person p = new Person("Rod", 39); Assert.assertEquals(p.getName(), p.getUnderlyingNode().getProperty("Person.name")); Assert.assertEquals(p.getAge(), p.getUnderlyingNode().getProperty("Person.age")); insertedId = p.getId(); + Node n = findPersonTestNode(); + Person found = nodeInstantiator.createEntityFromState(n, Person.class); + Assert.assertEquals("Rod", found.getUnderlyingNode().getProperty("Person.name")); + Assert.assertEquals(39, found.getUnderlyingNode().getProperty("Person.age")); } @Test @@ -221,14 +239,42 @@ public class Neo4jGraphPersistenceTest { Assert.assertEquals(new HashSet(Arrays.asList(david,michael)), personsFromGet); Assert.assertTrue(Set.class.isAssignableFrom(personsFromGet.getClass())); } - + @Test @Transactional - public void testInstantiatedFinder() { - Node n = findPersonTestNode(); - Person found = nodeInstantiator.createEntityFromState(n, Person.class); - Assert.assertEquals("Rod", found.getUnderlyingNode().getProperty("Person.name")); - Assert.assertEquals(39, found.getUnderlyingNode().getProperty("Person.age")); + public void testFinderFindAll() { + Person p1 = new Person("Michael", 35); + Person p2 = new Person("David", 25); + Finder finder = finderFactory.getFinderForClass(Person.class); + Iterable allPersons = finder.findAll(); + Assert.assertEquals(new HashSet(Arrays.asList(p1, p2)), IteratorUtil.addToCollection(allPersons.iterator(), new HashSet())); + } + + @Test + @Transactional + public void testFinderFindById() { + Person p = new Person("Michael", 35); + Finder finder = finderFactory.getFinderForClass(Person.class); + Person pById = finder.findById(p.getId()); + Assert.assertEquals(p, pById); + } + + @Test + @Transactional + public void testFinderFindByIdNonexistent() { + Person p = new Person("Michael", 35); + Finder finder = finderFactory.getFinderForClass(Person.class); + Person p2 = finder.findById(589736218); + Assert.assertNull(p2); + } + + @Test + @Transactional + public void testFinderCount() { + Finder finder = finderFactory.getFinderForClass(Person.class); + Assert.assertEquals(0, finder.count()); + Person p = new Person("Michael", 35); + Assert.assertEquals(1, finder.count()); } @Test diff --git a/src/test/resources/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest-context.xml b/src/test/resources/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest-context.xml index cd4d53323..3a5ba3ce8 100644 --- a/src/test/resources/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest-context.xml +++ b/src/test/resources/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest-context.xml @@ -81,7 +81,12 @@ - + + + + + +