From 34d95d3e346509638f37b8278fa4f910bf1913d7 Mon Sep 17 00:00:00 2001 From: David Montag Date: Tue, 24 Aug 2010 11:08:16 +0200 Subject: [PATCH] Added basic relationship entity support. Read access tbd. --- .../persistence/graph/Graph.java | 7 +- .../graph/neo4j/Neo4jNodeBacking.aj | 41 +++++++++- .../graph/neo4j/Neo4jRelationshipBacking.aj | 43 +--------- .../persistence/graph/neo4j/NodeBacked.java | 4 + .../graph/neo4j/RelationshipBacked.java | 10 --- .../persistence/test/Person.java | 26 +++++- .../test/graph/Neo4jGraphPersistenceTest.java | 81 ++++++------------- 7 files changed, 101 insertions(+), 111 deletions(-) diff --git a/src/main/java/org/springframework/persistence/graph/Graph.java b/src/main/java/org/springframework/persistence/graph/Graph.java index 4fdfec41b..6bb78bdb5 100644 --- a/src/main/java/org/springframework/persistence/graph/Graph.java +++ b/src/main/java/org/springframework/persistence/graph/Graph.java @@ -6,6 +6,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.persistence.graph.neo4j.NodeBacked; +import org.springframework.persistence.graph.neo4j.RelationshipBacked; public @interface Graph { @@ -19,7 +20,7 @@ public @interface Graph { String type(); - Direction direction(); + Direction direction() default Direction.OUTGOING; Class elementClass() default NodeBacked.class; } @@ -29,7 +30,9 @@ public @interface Graph { public @interface RelationshipEntity { String type(); - Direction direction(); + Direction direction() default Direction.OUTGOING; + + Class elementClass() default RelationshipBacked.class; } } 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 5384f165f..b0f1b525b 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj +++ b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj @@ -82,7 +82,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields elementClass; + + public OneToNRelationshipEntityInfo(RelationshipType type, Direction direction, Class elementClass) { + this.type = type; + this.direction = direction; + this.elementClass = elementClass; + + } + + @Override + public Object apply(NodeBacked entity, Object newVal) { + throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); + } + + @Override + public Object readObject(NodeBacked entity) { + // TODO Auto-generated method stub + return null; + } + } } diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jRelationshipBacking.aj b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jRelationshipBacking.aj index 45f796fd5..8cc3da98d 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jRelationshipBacking.aj +++ b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jRelationshipBacking.aj @@ -50,34 +50,14 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields // Introduced fields private Relationship RelationshipBacked.underlyingRelationship; - private Node RelationshipBacked.underlyingStartNode; - private Node RelationshipBacked.underlyingEndNode; public void RelationshipBacked.setUnderlyingRelationship(Relationship r) { this.underlyingRelationship = r; - underlyingStartNode = r.getStartNode(); - underlyingEndNode = r.getEndNode(); } public Relationship RelationshipBacked.getUnderlyingRelationship() { return underlyingRelationship; } - - public void RelationshipBacked.setUnderlyingStartNode(Node n) { - underlyingStartNode = n; - } - - public Node RelationshipBacked.getUnderlyingStartNode() { - return underlyingStartNode; - } - - public void RelationshipBacked.setUnderlyingEndNode(Node n) { - underlyingEndNode = n; - } - - public Node RelationshipBacked.getUnderlyingEndNode() { - return underlyingEndNode; - } public long RelationshipBacked.getId() { return underlyingRelationship.getId(); @@ -106,14 +86,14 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields Field f = fieldSignature.getField(); if (isStartNodeField(f)) { - Node startNode = entity.getUnderlyingStartNode(); + Node startNode = entity.getUnderlyingRelationship().getStartNode(); if (startNode == null) { return null; } return graphEntityInstantiator.createEntityFromState(startNode, (Class) f.getType()); } if (isEndNodeField(f)) { - Node endNode = entity.getUnderlyingEndNode(); + Node endNode = entity.getUnderlyingRelationship().getEndNode(); if (endNode == null) { return null; } @@ -138,23 +118,8 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields try { FieldSignature fieldSignature = (FieldSignature) thisJoinPoint.getSignature(); Field f = fieldSignature.getField(); - if (newVal instanceof NodeBacked - && isStartNodeField(f) - && entity.getUnderlyingStartNode() == null) { - NodeBacked newValNb = (NodeBacked) newVal; - entity.setUnderlyingStartNode(newValNb.getUnderlyingNode()); - if (entity.getUnderlyingEndNode() != null) { - entity.setUnderlyingRelationship(entity.getUnderlyingStartNode().createRelationshipTo(entity.getUnderlyingEndNode(), DynamicRelationshipType.withName(f.getDeclaringClass().getSimpleName()))); - } - } - if (newVal instanceof NodeBacked - && isEndNodeField(f) - && entity.getUnderlyingEndNode() == null) { - NodeBacked newValNb = (NodeBacked) newVal; - entity.setUnderlyingEndNode(newValNb.getUnderlyingNode()); - if (entity.getUnderlyingStartNode() != null) { - entity.setUnderlyingRelationship(entity.getUnderlyingStartNode().createRelationshipTo(entity.getUnderlyingEndNode(), DynamicRelationshipType.withName(f.getDeclaringClass().getSimpleName()))); - } + if (isStartNodeField(f) || isEndNodeField(f)) { + throw new InvalidDataAccessApiUsageException("Cannot change start node or end node of existing relationship."); } if (isPropertyType(f.getType())) { Relationship rel = entity.getUnderlyingRelationship(); diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/NodeBacked.java b/src/main/java/org/springframework/persistence/graph/neo4j/NodeBacked.java index 5b51fa841..f17472dac 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/NodeBacked.java +++ b/src/main/java/org/springframework/persistence/graph/neo4j/NodeBacked.java @@ -1,6 +1,8 @@ package org.springframework.persistence.graph.neo4j; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; /** * Interface introduced to objects annotated with GraphEntity @@ -12,5 +14,7 @@ public interface NodeBacked { Node getUnderlyingNode(); void setUnderlyingNode(Node n); + + Relationship relateTo(NodeBacked nb, RelationshipType type); } diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/RelationshipBacked.java b/src/main/java/org/springframework/persistence/graph/neo4j/RelationshipBacked.java index cfdbae0ab..58f159a13 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/RelationshipBacked.java +++ b/src/main/java/org/springframework/persistence/graph/neo4j/RelationshipBacked.java @@ -1,6 +1,5 @@ package org.springframework.persistence.graph.neo4j; -import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; public interface RelationshipBacked { @@ -8,13 +7,4 @@ public interface RelationshipBacked { Relationship getUnderlyingRelationship(); void setUnderlyingRelationship(Relationship r); - - void setUnderlyingStartNode(Node n); - - Node getUnderlyingStartNode(); - - void setUnderlyingEndNode(Node n); - - Node getUnderlyingEndNode(); - } diff --git a/src/test/java/org/springframework/persistence/test/Person.java b/src/test/java/org/springframework/persistence/test/Person.java index aa34879fa..a00452351 100644 --- a/src/test/java/org/springframework/persistence/test/Person.java +++ b/src/test/java/org/springframework/persistence/test/Person.java @@ -1,5 +1,9 @@ package org.springframework.persistence.test; +import java.util.Collection; + +import org.neo4j.graphdb.DynamicRelationshipType; +import org.neo4j.graphdb.Relationship; import org.springframework.persistence.graph.Direction; import org.springframework.persistence.graph.Graph; @@ -16,12 +20,15 @@ public class Person { private Person spouse; - @Graph.Entity.Relationship(type="mother", direction=Direction.OUTGOING) + @Graph.Entity.Relationship(type = "mother", direction = Direction.OUTGOING) private Person mother; - @Graph.Entity.Relationship(type="boss", direction=Direction.INCOMING) + @Graph.Entity.Relationship(type = "boss", direction = Direction.INCOMING) private Person boss; + @Graph.Entity.RelationshipEntity(type = "knows", elementClass = Friendship.class) + private Iterable friendships; + // @Property(serialize=SerializationPolicy.STRING, index=true, queryable=true, removeOnReset=true) // Date birthday; @@ -85,4 +92,19 @@ public class Person { public String toString() { return name; } + + public Iterable getFriendships() { + return friendships; + } + + public void setFriendships(Iterable f) { + friendships = f; + } + + public Friendship knows(Person p) { + Relationship rel = this.getUnderlyingNode().createRelationshipTo(p.getUnderlyingNode(), DynamicRelationshipType.withName("knows")); + Friendship friendship = new Friendship(); + friendship.setUnderlyingRelationship(rel); + return friendship; + } } 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 27445e47d..bd291b9a9 100644 --- a/src/test/java/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest.java +++ b/src/test/java/org/springframework/persistence/test/graph/Neo4jGraphPersistenceTest.java @@ -5,6 +5,7 @@ import junit.framework.Assert; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.neo4j.graphdb.Direction; @@ -50,8 +51,6 @@ public class Neo4jGraphPersistenceTest { @Autowired private FinderFactory finderFactory; - private Long insertedId = 0L; - @Test public void testStuffWasAutowired() { Assert.assertNotNull( graphDatabaseService ); @@ -70,9 +69,7 @@ public class Neo4jGraphPersistenceTest { 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 = graphEntityInstantiator.createEntityFromState(n, Person.class); + Person found = graphEntityInstantiator.createEntityFromState(graphDatabaseService.getNodeById(p.getId()), Person.class); Assert.assertEquals("Rod", found.getUnderlyingNode().getProperty("Person.name")); Assert.assertEquals(39, found.getUnderlyingNode().getProperty("Person.age")); } @@ -281,8 +278,8 @@ public class Neo4jGraphPersistenceTest { public void testRelationshipCreate() { Person p = new Person("Michael", 35); Person p2 = new Person("David", 25); - Friendship f = new Friendship(p, p2); - Relationship rel = p.getUnderlyingNode().getSingleRelationship(DynamicRelationshipType.withName("Friendship"), Direction.OUTGOING); + Friendship f = p.knows(p2); + Relationship rel = p.getUnderlyingNode().getSingleRelationship(DynamicRelationshipType.withName("knows"), Direction.OUTGOING); Assert.assertEquals(f.getUnderlyingRelationship(), rel); Assert.assertEquals(p2.getUnderlyingNode(), rel.getEndNode()); } @@ -292,7 +289,7 @@ public class Neo4jGraphPersistenceTest { public void testRelationshipSetProperty() { Person p = new Person("Michael", 35); Person p2 = new Person("David", 25); - Friendship f = new Friendship(p, p2); + Friendship f = p.knows(p2); f.setYears(1); Assert.assertEquals(1, f.getUnderlyingRelationship().getProperty("Friendship.years")); } @@ -302,67 +299,37 @@ public class Neo4jGraphPersistenceTest { public void testRelationshipGetProperty() { Person p = new Person("Michael", 35); Person p2 = new Person("David", 25); - Friendship f = new Friendship(p, p2); + Friendship f = p.knows(p2); f.getUnderlyingRelationship().setProperty("Friendship.years", 1); Assert.assertEquals(1, f.getYears()); } - @Test(expected = InvalidDataAccessApiUsageException.class) - @Transactional - public void testRelationshipSetPropertyBeforeCreated() { - Friendship f = new Friendship(); - f.setYears(1); - } - - @Test(expected = InvalidDataAccessApiUsageException.class) - @Transactional - public void testRelationshipGetPropertyBeforeCreated() { - Friendship f = new Friendship(); - f.getYears(); - } - - @Test - @Transactional - public void testRelationshipSetEndNodeBeforeStartNode() { - Person p = new Person("Michael", 35); - Person p2 = new Person("David", 25); - Friendship f = new Friendship(); - f.setPerson2(p2); - f.setPerson1(p); - Relationship rel = p.getUnderlyingNode().getSingleRelationship(DynamicRelationshipType.withName("Friendship"), Direction.OUTGOING); - Assert.assertEquals(f.getUnderlyingRelationship(), rel); - Assert.assertEquals(p2.getUnderlyingNode(), rel.getEndNode()); - } - @Test @Transactional public void testRelationshipGetStartNodeAndEndNode() { Person p = new Person("Michael", 35); Person p2 = new Person("David", 25); - Friendship f = new Friendship(p, p2); + Friendship f = p.knows(p2); Assert.assertEquals(p, f.getPerson1()); Assert.assertEquals(p2, f.getPerson2()); } - -// @Test -// public void printNeo4jData() { -// StringBuilder ret = new StringBuilder(); -// for (Node n : graphDatabaseService.getAllNodes()) { -// ret.append("ID: " + n.getId() + " ["); -// int x = 0; -// for (String prop : n.getPropertyKeys()) { -// if (x++ > 0) { -// ret.append(", "); -// } -// ret.append(prop + "=" + n.getProperty(prop)); -// } -// ret.append("] "); -// } -// System.out.println("*** NEO4J DATA: " + ret); -// } - private Node findPersonTestNode() { - return graphDatabaseService.getNodeById(insertedId); + @Test + @Ignore + @Transactional + public void testRelationshipGetEntities() { + Person p = new Person("Michael", 35); + Person p2 = new Person("David", 25); + Person p3 = new Person("Emil", 32); + Friendship f2 = p.knows(p2); + Friendship f3 = p.knows(p3); + Assert.assertEquals(new HashSet(Arrays.asList(f2, f3)), IteratorUtil.addToCollection(p.getFriendships().iterator(), new HashSet())); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + @Transactional + public void testRelationshipSetEntitiesShouldThrowException() { + Person p = new Person("Michael", 35); + p.setFriendships(new HashSet()); } - }