Added finder support via FinderFactory. Improved check for neo4j supported types. Fixed tests to clean db between test cases.

This commit is contained in:
David Montag
2010-08-16 20:49:24 +02:00
parent b6cc858a72
commit 3f319b33a2
6 changed files with 155 additions and 25 deletions

View File

@@ -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<T extends NodeBacked> {
private final Class<T> clazz;
private final GraphDatabaseService graphDatabaseService;
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
public Finder(Class<T> clazz, GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
this.clazz = clazz;
this.graphDatabaseService = graphDatabaseService;
this.graphEntityInstantiator = graphEntityInstantiator;
}
public long count() {
return Neo4jHelper.count(clazz, graphDatabaseService);
}
public Iterable<T> findAll() {
Node subrefNode = Neo4jHelper.findSubreferenceNode(clazz, graphDatabaseService);
// TODO add lazy list on top of graph
List<T> result = new ArrayList<T>((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;
}
}
}

View File

@@ -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<NodeBacked, Node> graphEntityInstantiator;
public FinderFactory(GraphDatabaseService graphDatabaseService, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
this.graphDatabaseService = graphDatabaseService;
this.graphEntityInstantiator = graphEntityInstantiator;
}
public <T extends NodeBacked> Finder<T> getFinderForClass(Class<T> clazz) {
return new Finder<T>(clazz, graphDatabaseService, graphEntityInstantiator);
}
}

View File

@@ -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<? extends NodeBacked> 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();
}
}
}
}

View File

@@ -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<GraphEn
public long NodeBacked.getId() {
return underlyingNode.getId();
}
//-------------------------------------------------------------------------
// Equals and hashCode for Neo4j entities.
@@ -144,7 +146,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
FieldSignature fieldSignature=(FieldSignature) thisJoinPoint.getSignature();
Field f = fieldSignature.getField();
// TODO fix arrays
Class fieldType=f.getType();
Class<?> fieldType = f.getType();
if (isPropertyType(fieldType)) {
String propName = getNeo4jPropertyName(f);
entity.getUnderlyingNode().setProperty(propName, newVal);
@@ -158,21 +160,21 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
return proceed(entity, newVal);
}
log.info("SET " + f + " -> 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) {

View File

@@ -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<Person>(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<Person> finder = finderFactory.getFinderForClass(Person.class);
Iterable<Person> allPersons = finder.findAll();
Assert.assertEquals(new HashSet<Person>(Arrays.asList(p1, p2)), IteratorUtil.addToCollection(allPersons.iterator(), new HashSet<Person>()));
}
@Test
@Transactional
public void testFinderFindById() {
Person p = new Person("Michael", 35);
Finder<Person> 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<Person> finder = finderFactory.getFinderForClass(Person.class);
Person p2 = finder.findById(589736218);
Assert.assertNull(p2);
}
@Test
@Transactional
public void testFinderCount() {
Finder<Person> finder = finderFactory.getFinderForClass(Person.class);
Assert.assertEquals(0, finder.count());
Person p = new Person("Michael", 35);
Assert.assertEquals(1, finder.count());
}
@Test

View File

@@ -81,7 +81,12 @@
<constructor-arg index="0" value="${neo4j.databaseDirectory}" />
</bean>
<bean class="org.springframework.persistence.graph.neo4j.Neo4jConstructorEntityInstantiator" />
<bean id="finderFactory" class="org.springframework.persistence.graph.neo4j.FinderFactory">
<constructor-arg ref="graphDbService" />
<constructor-arg ref="entityInstantiator" />
</bean>
<bean id="entityInstantiator" class="org.springframework.persistence.graph.neo4j.Neo4jConstructorEntityInstantiator" />
<!--
-->