Merge branch 'master' of github.com:SpringSource/spring-data-graph

This commit is contained in:
Michael Hunger
2011-03-18 15:31:29 +01:00
7 changed files with 435 additions and 17 deletions

View File

@@ -0,0 +1,95 @@
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.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.NodeBacked;
import org.springframework.data.graph.core.NodeTypeStrategy;
import org.springframework.persistence.support.EntityInstantiator;
public class IndexingNodeTypeStrategy implements NodeTypeStrategy {
private EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private GraphDatabaseService graphDb;
public IndexingNodeTypeStrategy(GraphDatabaseService graphDb, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
this.graphDb = graphDb;
this.graphEntityInstantiator = graphEntityInstantiator;
}
private Index<Node> getTypesIndex() {
return graphDb.index().forNodes("__types__");
}
@Override
public void postEntityCreation(NodeBacked entity) {
Node node = entity.getPersistentState();
Class<? extends NodeBacked> entityClass = entity.getClass();
addToTypesIndex(node, entityClass);
node.setProperty("__type__", entityClass.getName());
}
private void addToTypesIndex(Node node, Class<? extends NodeBacked> entityClass) {
Class<?> klass = entityClass;
while (klass.getAnnotation(NodeEntity.class) != null) {
getTypesIndex().add(node, "className", klass.getName());
klass = klass.getSuperclass();
}
}
@Override
public <ENTITY extends NodeBacked> Iterable<ENTITY> findAll(Class<ENTITY> clazz) {
final IndexHits<Node> allEntitiesOfType = getTypesIndex().get("className", 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) {
return getTypesIndex().get("className", entityClass.getName()).size();
}
@Override
@SuppressWarnings("unchecked")
public <ENTITY extends NodeBacked> Class<ENTITY> getJavaType(Node node) {
if (node == null) throw new IllegalArgumentException("Node is null");
try {
return (Class<ENTITY>) Class.forName((String) node.getProperty("__type__"));
} catch (NotFoundException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
@Override
public void preEntityRemoval(NodeBacked entity) {
getTypesIndex().remove(entity.getPersistentState(), "className", entity.getClass().getName());
}
@Override
public <T extends NodeBacked> Class<T> confirmType(Node node, Class<T> type) {
Class<T> javaType = getJavaType(node);
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 node type %s of node %s", type, javaType, node));
}
}

View File

@@ -0,0 +1,35 @@
package org.springframework.data.graph.neo4j.support;
import org.neo4j.graphdb.Node;
import org.springframework.data.graph.core.NodeBacked;
import org.springframework.data.graph.core.NodeTypeStrategy;
public class NoopNodeTypeStrategy implements NodeTypeStrategy {
@Override
public void postEntityCreation(NodeBacked entity) {
}
@Override
public <T extends NodeBacked> Iterable<T> findAll(Class<T> clazz) {
throw new UnsupportedOperationException("findAll not supported by NoopNodeTypeStrategy.");
}
@Override
public long count(Class<? extends NodeBacked> entityClass) {
throw new UnsupportedOperationException("count not supported by NoopNodeTypeStrategy.");
}
@Override
public <T extends NodeBacked> Class<T> getJavaType(Node node) {
throw new UnsupportedOperationException("getJavaType not supported NoopNodeTypeStrategy.");
}
@Override
public void preEntityRemoval(NodeBacked entity) {
}
@Override
public <T extends NodeBacked> Class<T> confirmType(Node node, Class<T> type) {
return type;
}
}

View File

@@ -0,0 +1,159 @@
package org.springframework.data.graph.neo4j.support;
import org.junit.Before;
import org.junit.Ignore;
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.neo4j.support.node.Neo4jHelper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.BeforeTransaction;
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;
@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"})
@Ignore
public class IndexingNodeTypeStrategyTest {
@Autowired
private GraphDatabaseService graphDatabaseService;
@Autowired
private IndexingNodeTypeStrategy nodeTypeStrategy;
private Thing thing;
private SubThing subThing;
@BeforeTransaction
public void cleanDb() {
Neo4jHelper.cleanDb(graphDatabaseService);
}
@Before
public void setUp() throws Exception {
if (thing == null) {
createThings();
}
}
@Test
@Transactional
public void testPostEntityCreation() throws Exception {
Index<Node> typesIndex = graphDatabaseService.index().forNodes("__types__");
IndexHits<Node> thingHits = typesIndex.get("className", thing.getClass().getName());
assertEquals(set(node(thing), node(subThing)), IteratorUtil.addToCollection((Iterable<Node>)thingHits, new HashSet<Node>()));
assertEquals(thing.getClass().getName(), node(thing).getProperty("__type__"));
assertEquals(subThing.getClass().getName(), node(subThing).getProperty("__type__"));
}
@Test
public void testFindAll() throws Exception {
assertEquals("Did not find all things.",
Arrays.asList(thing, subThing), IteratorUtil.addToCollection(nodeTypeStrategy.findAll(Thing.class), new ArrayList<Thing>()));
}
@Test
public void testCount() throws Exception {
assertEquals(2, nodeTypeStrategy.count(Thing.class));
}
@Test
public void testGetJavaType() throws Exception {
assertEquals(Thing.class, nodeTypeStrategy.getJavaType(node(thing)));
assertEquals(SubThing.class, nodeTypeStrategy.getJavaType(node(subThing)));
}
@Test
public void testPreEntityRemoval() throws Exception {
manualCleanDb();
Transaction tx;
tx = graphDatabaseService.beginTx();
try {
nodeTypeStrategy.preEntityRemoval(thing);
nodeTypeStrategy.preEntityRemoval(subThing);
tx.success();
} finally {
tx.finish();
}
Index<Node> typesIndex = graphDatabaseService.index().forNodes("__types__");
IndexHits<Node> thingHits = typesIndex.get("className", thing.getClass().getName());
assertEquals(0, thingHits.size());
}
@Test
public void testConfirmType() throws Exception {
assertEquals(Thing.class, nodeTypeStrategy.confirmType(node(thing), Thing.class));
assertEquals(SubThing.class, nodeTypeStrategy.confirmType(node(subThing), Thing.class));
}
private static Node node(Thing thing) {
return thing.getPersistentState();
}
private Thing createThings() {
Transaction tx = graphDatabaseService.beginTx();
try {
thing = new Thing(graphDatabaseService.createNode());
nodeTypeStrategy.postEntityCreation(thing);
subThing = new SubThing(graphDatabaseService.createNode());
nodeTypeStrategy.postEntityCreation(subThing);
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Thing {
String name;
public Thing() {
}
public Thing(Node n) {
setPersistentState(n);
}
}
public static class SubThing extends Thing {
public SubThing() {
super();
}
public SubThing(Node n) {
super(n);
}
}
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,89 @@
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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
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"})
public class NoopNodeTypeStrategyTest {
@Autowired
private GraphDatabaseContext graphDatabaseContext;
@Autowired
private NoopNodeTypeStrategy 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);
}
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
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="nodeTypeStrategy" class="org.springframework.data.graph.neo4j.support.IndexingNodeTypeStrategy">
<constructor-arg ref="graphDatabaseService" />
<constructor-arg ref="graphEntityInstantiator" />
</bean>
</beans>

View File

@@ -82,26 +82,27 @@
<constructor-arg index="0" value="${neo4j.databaseDirectory}" />
</bean>
<bean id="graphDatabaseContext" class="org.springframework.data.graph.neo4j.support.GraphDatabaseContext">
<property name="graphDatabaseService" ref="graphDatabaseService"/>
<property name="relationshipEntityInstantiator">
<bean class="org.springframework.data.graph.neo4j.support.relationship.ConstructorBypassingGraphRelationshipInstantiator" />
</property>
<property name="graphEntityInstantiator">
<bean class="org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator" />
</property>
<property name="conversionService">
<bean class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
</property>
<property name="nodeTypeStrategy">
<bean class="org.springframework.data.graph.neo4j.support.SubReferenceNodeTypeStrategy">
<constructor-arg index="0" ref="graphDatabaseContext"/>
</bean>
</property>
<bean id="graphDatabaseContext" class="org.springframework.data.graph.neo4j.support.GraphDatabaseContext">
<property name="graphDatabaseService" ref="graphDatabaseService"/>
<property name="relationshipEntityInstantiator">
<bean class="org.springframework.data.graph.neo4j.support.relationship.ConstructorBypassingGraphRelationshipInstantiator"/>
</property>
<property name="graphEntityInstantiator" ref="graphEntityInstantiator"/>
<property name="conversionService">
<bean class="org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean"/>
</property>
<property name="nodeTypeStrategy" ref="nodeTypeStrategy"/>
<property name="validator">
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
</bean>
</bean>
<bean id="graphEntityInstantiator"
class="org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator"/>
<bean id="nodeTypeStrategy" class="org.springframework.data.graph.neo4j.support.SubReferenceNodeTypeStrategy">
<constructor-arg index="0" ref="graphDatabaseContext"/>
</bean>
<bean id="nodeEntityStateFactory" class="org.springframework.data.graph.neo4j.fieldaccess.NodeEntityStateFactory">
<property name="nodeDelegatingFieldAccessorFactory">

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
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="nodeTypeStrategy" class="org.springframework.data.graph.neo4j.support.NoopNodeTypeStrategy" />
</beans>