From 92f91e026d530b51e1d0565ec57b151bf53aa292 Mon Sep 17 00:00:00 2001 From: David Montag Date: Fri, 8 Apr 2011 17:02:44 -0700 Subject: [PATCH 1/5] Fixed typo --- src/docbkx/reference/neo4j.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docbkx/reference/neo4j.xml b/src/docbkx/reference/neo4j.xml index 2cab12268..0f364d7fd 100644 --- a/src/docbkx/reference/neo4j.xml +++ b/src/docbkx/reference/neo4j.xml @@ -64,7 +64,7 @@ indexes (via the IndexManager), database life cycle callbacks, transaction management, and more. - The EmbeddedGraphDatabaseService is an implementation of GraphDatabaseService that is used to + The EmbeddedGraphDatabase is an implementation of GraphDatabaseService that is used to embed Neo4j in a Java application. This implementation is used so as to provide the highest and tightest integration with the database. Besides the embedded mode, the Neo4j server From b7077e89f625caecad5994aa515f4306fc7f547d Mon Sep 17 00:00:00 2001 From: David Montag Date: Mon, 11 Apr 2011 15:17:47 -0700 Subject: [PATCH 2/5] Removed backreferences stuff. Added cascading of persist of dirty fields pointing to node entities or collections thereof. --- .../data/graph/core/EntityState.java | 2 - .../data/graph/core/NodeBacked.java | 3 - .../neo4j/fieldaccess/BackReferences.java | 59 ------------- .../neo4j/fieldaccess/DefaultEntityState.java | 6 -- .../fieldaccess/DetachedEntityState.java | 82 +++++++------------ .../neo4j/support/node/Neo4jNodeBacking.aj | 3 - .../neo4j/support/HasRelationshipMatcher.java | 4 + .../ModificationOutsideOfTransactionTest.java | 55 ++++++++++++- 8 files changed, 85 insertions(+), 129 deletions(-) delete mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/BackReferences.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/EntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/EntityState.java index aa895dbe5..594c9ad94 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/EntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/EntityState.java @@ -62,6 +62,4 @@ public interface EntityState,STATE> { STATE getPersistentState(); ENTITY persist(); - - boolean refersTo(GraphBacked target); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/NodeBacked.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/NodeBacked.java index db6fdbec4..50eaa7e97 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/NodeBacked.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/NodeBacked.java @@ -147,7 +147,4 @@ public interface NodeBacked extends GraphBacked { * @return the newly created relationship to the target node */ Relationship relateTo(NodeBacked target, String type); - - // will possibly be used for object graphs - boolean refersTo(GraphBacked target); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/BackReferences.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/BackReferences.java deleted file mode 100644 index 353896902..000000000 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/BackReferences.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.graph.neo4j.fieldaccess; - -import org.springframework.data.graph.core.EntityState; -import org.springframework.data.graph.core.GraphBacked; -import org.springframework.data.graph.core.NodeBacked; - -import java.util.*; - -/** - * @author mh - * @since 12.03.11 - */ -public class BackReferences { - private List backrefs=new ArrayList(); - private EntityState entityState; - - public BackReferences(EntityState entityState) { - - this.entityState = entityState; - } - - public void addBackReferences(Collection backReference) { - this.backrefs.addAll(backReference); - } - - - private void pruneInvalidBackRefs() { - GraphBacked entity = entityState.getEntity(); - for (Iterator it = backrefs.iterator(); it.hasNext();) { - NodeBacked backRef = it.next(); - if (backRef.refersTo(entity)) continue; - it.remove(); - } - } - - - public void persistNeighbours() { - pruneInvalidBackRefs(); - for (NodeBacked backref : backrefs) { - backref.persist(); - } - } -} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java index e9a25b3d7..cd8f42c2d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java @@ -108,7 +108,6 @@ public abstract class DefaultEntityState, STAT } } - protected Object getIdFromEntity() { final Field idField = fieldAccessorFactoryProviders.getIdField(); if (idField==null) return null; @@ -120,9 +119,4 @@ public abstract class DefaultEntityState, STAT return null; } } - - @Override - public boolean refersTo(GraphBacked target) { - return false; - } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java index 27a1ad3ef..1f9190d37 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DetachedEntityState.java @@ -19,15 +19,17 @@ package org.springframework.data.graph.neo4j.fieldaccess; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.neo4j.graphdb.Transaction; -import org.springframework.data.graph.annotation.RelatedTo; +import org.springframework.data.graph.core.EntityState; import org.springframework.data.graph.core.GraphBacked; import org.springframework.data.graph.core.NodeBacked; -import org.springframework.data.graph.core.EntityState; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; import org.springframework.util.ObjectUtils; import java.lang.reflect.Field; -import java.util.*; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.Map; import static org.springframework.data.graph.neo4j.support.DoReturn.unwrap; @@ -40,11 +42,9 @@ public class DetachedEntityState, STATE> imple protected final EntityState delegate; private final static Log log = LogFactory.getLog(DetachedEntityState.class); private GraphDatabaseContext graphDatabaseContext; - private final BackReferences backReferences = null; public DetachedEntityState(final EntityState delegate, GraphDatabaseContext graphDatabaseContext) { this.delegate = delegate; this.graphDatabaseContext = graphDatabaseContext; - //this.backReferences = new BackReferences(this); } @Override @@ -152,14 +152,31 @@ public class DetachedEntityState, STATE> imple // createAndAssignState(); throw new IllegalStateException("Flushing detached entity without a persistent state, this had to be created first."); } + if (isDirty()) { - for (final Map.Entry entry : dirty.entrySet()) { - final Field field = entry.getKey(); - if (log.isDebugEnabled()) log.debug("Flushing dirty Entity new node " + entity.getPersistentState() + " field " + field+ " with value "+getValueFromEntity(field)); - checkConcurrentModification(entity, entry, field); - delegate.setValue(field, getValueFromEntity(field)); - } + final Map dirtyCopy = new HashMap(dirty); clearDirty(); + for (final Map.Entry entry : dirtyCopy.entrySet()) { + final Field field = entry.getKey(); + Object valueFromEntity = getValueFromEntity(field); + cascadePersist(valueFromEntity); + if (log.isDebugEnabled()) log.debug("Flushing dirty Entity new node " + entity.getPersistentState() + " field " + field+ " with value "+ valueFromEntity); + checkConcurrentModification(entity, entry, field); + delegate.setValue(field, valueFromEntity); + } + } + } + + private void cascadePersist(Object valueFromEntity) { + if (valueFromEntity instanceof NodeBacked) { + ((NodeBacked) valueFromEntity).persist(); + } + if (valueFromEntity instanceof Collection) { + for (Object o : (Collection)valueFromEntity) { + if (o instanceof NodeBacked) { + ((NodeBacked) o).persist(); + } + } } } @@ -217,7 +234,6 @@ public class DetachedEntityState, STATE> imple Transaction tx = graphDatabaseContext.beginTx(); try { ENTITY result = delegate.persist(); - //persistNeighbours(); flushDirty(); tx.success(); @@ -226,46 +242,4 @@ public class DetachedEntityState, STATE> imple tx.finish(); } } - - private void persistNeighbours() { - backReferences.persistNeighbours(); - for (NodeBacked nodeBacked : getOutboundDirtyNodeEntities()) { - nodeBacked.persist(); - } - } - - private Set getOutboundDirtyNodeEntities() { - HashSet result = new HashSet(); - for (Field field : dirty.keySet()) { - if (handleSingleField(result, field)) continue; - handleOneToMany(result, field); - } - return result; - } - - private boolean handleOneToMany(HashSet result, Field field) { - if ((Collection.class.isAssignableFrom(field.getType())) && field.isAnnotationPresent(RelatedTo.class)) { - result.addAll((Collection) getValueFromEntity(field)); - return true; - } - return false; - } - - - private boolean handleSingleField(HashSet result, Field field) { - if (NodeBacked.class.isAssignableFrom(field.getType())) { - Object value = getValueFromEntity(field); - if (value!=null) { - result.add((NodeBacked) value); - } - return true; - } - return false; - } - public boolean refersTo(GraphBacked target) { - return getOutboundDirtyNodeEntities().contains(target); - } } - - - diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj index e42c1c4a6..2d7675872 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jNodeBacking.aj @@ -119,9 +119,6 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields T NodeBacked.persist() { return (T)this.entityState.persist(); } - public boolean NodeBacked.refersTo(GraphBacked target) { - return this.entityState.refersTo(target); - } public void NodeBacked.setPersistentState(Node n) { if (this.entityState == null) { diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/HasRelationshipMatcher.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/HasRelationshipMatcher.java index 24410ce16..bc2f9780d 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/HasRelationshipMatcher.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/HasRelationshipMatcher.java @@ -67,6 +67,10 @@ class HasRelationshipMatcher extends TypeSafeMatcher { description.appendText( "Expected relationship named " + relationshipTypeName + " to " +(other==null ? "unspecified": other)+"\r\n got: " ); + if (relationships == null) { + description.appendValue("[]"); + return; + } List types = new ArrayList(); for ( Relationship rel : relationships ) { diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java index a24a49209..17f005dbf 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/ModificationOutsideOfTransactionTest.java @@ -23,7 +23,10 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.NotFoundException; +import org.neo4j.graphdb.NotInTransactionException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.graph.neo4j.Friendship; import org.springframework.data.graph.neo4j.Group; import org.springframework.data.graph.neo4j.Person; import org.springframework.data.graph.neo4j.repository.DirectGraphRepositoryFactory; @@ -43,7 +46,6 @@ import static org.springframework.data.graph.neo4j.support.HasRelationshipMatche @RunWith( SpringJUnit4ClassRunner.class ) @ContextConfiguration( locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"} ) - public class ModificationOutsideOfTransactionTest { @@ -71,7 +73,6 @@ public class ModificationOutsideOfTransactionTest } @Test - @Ignore("ignored until subgraph persisting is added") public void testCreateSubgraphOutsideOfTransactionPersistInDirectionOfRel() { Person michael = new Person("Michael", 35); Person emil = new Person("Emil", 31); @@ -87,6 +88,48 @@ public class ModificationOutsideOfTransactionTest } + @Test + public void testCreateSubgraphOutsideOfTransactionPersistWithImmediateCycle() { + Person michael = new Person("Michael", 35); + Person emil = new Person("Emil", 31); + + michael.setBoss(emil); + emil.setBoss(michael); + + assertEquals(emil, michael.getBoss()); + assertEquals(michael, emil.getBoss()); + assertFalse(hasPersistentState(michael)); + assertFalse(hasPersistentState(emil)); + michael.persist(); + assertThat(nodeFor(michael), hasRelationship("boss", nodeFor(emil))); + assertThat(nodeFor(emil), hasRelationship("boss", nodeFor(michael))); + } + + @Test + public void testCreateSubgraphOutsideOfTransactionPersistWithCycle() { + Person michael = new Person("Michael", 35); + Person david = new Person("David", 27); + Person emil = new Person("Emil", 31); + + michael.setBoss(emil); + david.setBoss(michael); + emil.setBoss(david); + + assertEquals(emil, michael.getBoss()); + assertEquals(michael, david.getBoss()); + assertEquals(david, emil.getBoss()); + assertFalse(hasPersistentState(michael)); + assertFalse(hasPersistentState(david)); + assertFalse(hasPersistentState(emil)); + michael.persist(); + assertThat(nodeFor(michael), hasRelationship("boss", nodeFor(emil))); + assertThat(nodeFor(michael), hasRelationship("boss", nodeFor(david))); + assertThat(nodeFor(david), hasRelationship("boss", nodeFor(michael))); + assertThat(nodeFor(david), hasRelationship("boss", nodeFor(emil))); + assertThat(nodeFor(emil), hasRelationship("boss", nodeFor(david))); + assertThat(nodeFor(emil), hasRelationship("boss", nodeFor(michael))); + } + @Ignore("ignored until subgraph persisting is added") @Test public void testCreateSubgraphOutsideOfTransactionPersistInReverseDirectionOfRel() { @@ -103,6 +146,14 @@ public class ModificationOutsideOfTransactionTest assertThat(nodeFor(emil), hasRelationship("boss", nodeFor(michael))); } + // TODO: Would be nice if this worked outside of a tx + @Test(expected = NotInTransactionException.class) + public void foo() { + Person p = persistedPerson("Michael", 35); + Person p2 = persistedPerson("David", 26); + Friendship f = p.knows(p2); + } + @Test public void testSetPropertyOutsideTransaction() { From 5e194890537915fcf875695e295dd30e8be166ea Mon Sep 17 00:00:00 2001 From: David Montag Date: Mon, 11 Apr 2011 15:20:13 -0700 Subject: [PATCH 3/5] Edited reference docs. Mainly around detached entities, but also other parts. --- .../reference/programming-model/aspectj.xml | 2 +- .../programming-model/attachdetach.xml | 137 +++++++++++++++--- .../programming-model/beanvalidation.xml | 20 +-- .../reference/programming-model/indexing.xml | 9 +- .../programming-model/node-entities.xml | 122 ++++++++-------- .../programming-model/relationships.xml | 5 +- .../programming-model/repositories.xml | 16 +- .../typerepresentationstrategy.xml | 2 +- 8 files changed, 207 insertions(+), 106 deletions(-) diff --git a/src/docbkx/reference/programming-model/aspectj.xml b/src/docbkx/reference/programming-model/aspectj.xml index 12c6f201a..70324572a 100644 --- a/src/docbkx/reference/programming-model/aspectj.xml +++ b/src/docbkx/reference/programming-model/aspectj.xml @@ -5,7 +5,7 @@ Behind the scenes, Spring Data Graph leverages AspectJ aspects to modify the behavior of simple annotated POJO entities - (see ). Each node entity is backed by a graph node that holds its + (see ). Each node entity is backed by a graph node that holds its properties and relationships to other entities. AspectJ is used for intercepting field access, so that Spring Data Graph can retrieve the information from the entity's backing node or relationship in the database. diff --git a/src/docbkx/reference/programming-model/attachdetach.xml b/src/docbkx/reference/programming-model/attachdetach.xml index dc52d1e41..6eb95c4eb 100644 --- a/src/docbkx/reference/programming-model/attachdetach.xml +++ b/src/docbkx/reference/programming-model/attachdetach.xml @@ -1,36 +1,129 @@
- Detached entities + Detached node entities - By default newly created node entities are in a detached state. When persist() is called on the - entity it is attached to the graph store and its properties and relationships are persisted as well. Changing - an attached entity inside a transaction will write through the changes to the datastore. Whenever an entity - is changed outside of a transaction it will be considered detached. The changed data is stored in the entity - itself and not written back to the datastore. + Node entities can be in two different persistence state: attached or detached. By default, newly created node + entities are in the detached state. When persist() is called on the entity, it becomes + attached to the graph, and its properties and relationships are stores in the database. If + persist() is not called within a transaction, it automatically creates an implicit + transaction for the operation. - All entities that are returned by library functions are initially in an attached state. Changing them outside - of a transaction detaches them. For writing the changes back it is necessary to persist() them - again. + Changing an attached entity inside a transaction will immediately write through the changes to + the datastore. Whenever an entity is changed outside of a transaction it becomes detached. The + changes are stored in the entity itself until the next call to persist(). - Persisting an entity not only persists that single entity but will traverse its existing and new relationships - and persist the cluster of detached entities that it is part of. The borders of this cluster are formed by - attached entities. The persist operation creates its own, implicit transaction. When it is called withina - external transaction it participates otherwise it is an atomic operation. + All entities returned by library functions are initially in an attached state. + Just as with any other entity, changing them outside of a transaction detaches them, and they + must be reattached with persist() for the data to be saved. - - Please keep in mind that the session handling behaviour is still heavily developed. The defaults and also - other aspects of the behaviour are likely to change in subsequent releases. At the moment there is no support - for the creation of relationships outside of transactions and also more complex operations like creating - whole subgraphs outside of transactions is not supported. - - --> + + + + + + + Persisting entities + + +
+ Relating detached entities + + As mentioned above, an entity simply created with the new keyword starts out detached. + It also has no state assigned to it. If you create a new entity with new and then throw + it away, the database won't be touched at all. + + + Now consider this scenario: + + Relationships outside of transactions + + + + + Neither the actor nor the movie has been assigned a node in the graph. If we were to call + movie.persist(), then Spring Data Graph would first create a node for the movie. + It would then note that there is a relationship to an actor, so it would call actor.persist() + in a cascading fashion. Once the actor has been persisted, it will create the relationship + from the movie to the actor. All of this will be done atomically in one transaction. + + + Important to note here is that if actor.persist() is called instead, then only + the actor will be persisted. The reason for this is that the actor entity knows nothing about + the movie entity. It is the movie entity that has the reference to the actor. Also note that + this behavior is not dependent on any configured relationship direction on the annotations. + It is a matter of Java references and is not related to the data model in the database. + + + If the relationships form a cycle, then the entities will first all be assigned a node in + the database, and then the relationships will be created. The cascading of persist() + is however only cascaded to related entity fields that have been modified. + + + In the following example, the actor and the movie are both attached entites, having both been + previously persisted to the graph: + + Cascade for modified fields + + + In this case, even though the movie has a reference to the actor, the name change on the actor + will not be persisted by the call to movie.persist(). The reason for this is, as + mentioned above, that cascading will only be done for fields that have been modified. Since the + movie.topActor field has not been modified, it will not cascade the persist operation + to the actor. + +
+ + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/docbkx/reference/programming-model/beanvalidation.xml b/src/docbkx/reference/programming-model/beanvalidation.xml index 8ce969cbe..344edef98 100644 --- a/src/docbkx/reference/programming-model/beanvalidation.xml +++ b/src/docbkx/reference/programming-model/beanvalidation.xml @@ -3,22 +3,24 @@
Bean validation (JSR-303) - Spring Data Graph supports property based validation support. So, whenever a property is changed, it is - checked against the annotated constraints (.e.g @Min, @Max, @Size, etc). - Validation errors throw a ValidationException. For evaluating the constraints the validation support that - comes with Spring is used. To use it a validator has to be registered with the GraphDatabaseContext, if there - is none, no validation will be performed (any registered Validator or (Local)ValidatorFactoryBean will be - used). + Spring Data Graph supports property-based validation support. When a property is changed, it is + checked against the annotated constraints, e.g. @Min, @Max, + @Size, etc. Validation errors throw a ValidationException. The validation + support that comes with Spring is used for evaluating the constraints. To use this feature, a validator + has to be registered with the GraphDatabaseContext. - + Bean validation + + +
diff --git a/src/docbkx/reference/programming-model/indexing.xml b/src/docbkx/reference/programming-model/indexing.xml index 8d10a60b7..a391c853e 100644 --- a/src/docbkx/reference/programming-model/indexing.xml +++ b/src/docbkx/reference/programming-model/indexing.xml @@ -29,7 +29,8 @@ package. - The indexes can be queried by using a repository (see ). + The indexes can be queried by using a repository (see + ). Typically, the repository is an instance of org.springframework.data.graph.neo4j.repository.DirectGraphRepositoryFactory. The methods findByPropertyValue() and findAllByPropertyValue() work on @@ -45,7 +46,8 @@ class Person { @Indexed int age; } -GraphRepository graphRepository = graphRepositoryFactory.createGraphRepository(Person.class); +GraphRepository graphRepository = graphRepositoryFactory + .createGraphRepository(Person.class); // Exact match, in named index Person mark = graphRepository.findByPropertyValue("people", "name", "mark"); @@ -84,7 +86,8 @@ class Person { @Indexed(indexName = "person-name", fulltext=true) String name; } -GraphRepository graphRepository = graphRepositoryFactory.createGraphRepository(Person.class); +GraphRepository graphRepository = graphRepositoryFactory + .createGraphRepository(Person.class); Person mark = graphRepository.findAllByQuery("people-search", "name", "ma*"); ]]> diff --git a/src/docbkx/reference/programming-model/node-entities.xml b/src/docbkx/reference/programming-model/node-entities.xml index 4b32cc494..98ead57b0 100644 --- a/src/docbkx/reference/programming-model/node-entities.xml +++ b/src/docbkx/reference/programming-model/node-entities.xml @@ -9,92 +9,90 @@
@NodeEntity: The basic building block - The @NodeEntity annotation is used to declare a POJO entity to be backed by a node in the - graph store. Simple fields on the entity are mapped by default to properties of the node. Object - references to other NodeEntities (whether single or Collection) are mapped via relationships. If - the annotation parameter useShortNames is set to false, the properties and relationship - names used will be prepended with the class name of the entity. - - If the partial - parameter is set to true, this entity takes part in a cross-store setting /) - where only the specifically annotated parts of the entity not handled by JPA will be mapped to the graph store. + The @NodeEntity annotation is used to turn a POJO class into an entity backed by a node + in the graph database. Fields on the entity are by default mapped to properties of the node. Fields + referencing other node entities (or collections thereof) are linked with relationships. If the + useShortNames attribute overridden to false, the property and relationship names will + have the class name of the entity prepended. - Entity fields can be annotated with @GraphProperty, @RelatedTo, @RelatedToVia, @Indexed, @GraphId and - @GraphTraversal. + + If the partial attribute is set to true, this entity takes part in a cross-store setting, + where the entity lives in both the graph database and a JPA data source. See + for more information. - - Simple Node Entity - + Entity fields can be annotated with @GraphProperty, @RelatedTo, + @RelatedToVia, @Indexed, @GraphId and + @GraphTraversal. + + + Simple node entity + - +
+
- @GraphProperty: Optional Annotation for Property Fields - It is not necessary to annotate fields as they are persisted by default; all fields that contain primitive - values are persisted directly to the graph. All fields - convertible to String using the Spring conversion services will be stored as a string. - (Spring Data Graph adds a custom conversion factory that comes with converters for Enums and Dates). + @GraphProperty: Optional annotation for property fields + + It is not necessary to annotate data fields, as they are persisted by default; all fields that + contain primitive values are persisted directly to the graph. All fields convertible to String + using the Spring conversion services will be stored as a string. Spring Data Graph includes a + custom conversion factory that comes with converters for Enums and Dates. Transient fields are not persisted. - This annotation is mainly used for cross-store persistence. + + + This annotation is typically used with cross-store persistence. When a node entity is configured + as partial, then all fields that should be persisted to the graph must be explicitly annotated + with @GraphProperty.
@Indexed: Making entities searchable by field value - The @Indexed annotation can be declared on fields that are intended to be indexed by the Neo4j - indexing facilities, triggered by value modification. - The resulting index can be used to later retrieve nodes or relationships that contain a certain property - value (for example a name). Often an index is used to establish the start node for a traversal. - Indexes are accessed by a Repository for a particular node or relationship entity, created via a - DirectGraphRepositoryFactory. - - GraphDatabaseContext exposes the indexes for Nodes and Relationships via the getIndex method. - Index names default to the domain class - name, but can also be named (indexName attribute)individually to reflect domain concepts. - be named, for instance to keep separate domain concepts in separate indexes. - - - Numerical values are indexed as such by default, allowing for range queries. - Fulltext indexing is also possible by setting the fulltext attribute to true. For details see - the indexing section . + The @Indexed annotation can be declared on fields that are intended to be indexed by the Neo4j + indexing facilities. The resulting index can be used to later retrieve nodes or relationships + that contain a certain property value, e.g. a name. Often an index is used to establish the start + node for a traversal. Indexes are accessed by a repository for a particular node or relationship + entity type. See and + for more information.
- @GraphTraversal: fields providing direct access to traversal results - The @GraphTraversal annotation leverages the delegation infrastructure used by the Spring Data Graph - aspects. It provides dynamic fields which, when accessed, return an Iterable of NodeEntities that are - the result of a traversal starting at the current NodeEntity. The TraversalDescription used for this - is created by a TraversalDescriptionBuilder whose class is referred to by the traversalBuilder - attribute of the annotation. The class of the expected NodeEntities is provided with the + @GraphTraversal: fields as traversal result views + + The @GraphTraversal annotation leverages the delegation infrastructure used by the + Spring Data Graph aspects. It provides dynamic fields which, when accessed, return an Iterable + of node entities that are the result of a traversal starting at the entity containing the field. + The TraversalDescription used for this is created by the + FieldTraversalDescriptionBuilder class defined by the traversalBuilder + attribute. The class of the resulting node entities must be provided with the elementClass attribute. - - @GraphTraversal in a Node Entity - + + @GraphTraversal from a node entity + people; + @GraphTraversal(traversalBuilder = PeopleTraversalBuilder.class, + elementClass = Person.class, params = "persons") + private Iterable people; -private static class PeopleTraversalBuilder implements FieldTraversalDescriptionBuilder { - @Override - public TraversalDescription build(NodeBacked start, Field field, String...params) { - return new TraversalDescriptionImpl() - .relationships(DynamicRelationshipType.withName(params[0])) - .filter(Traversal.returnAllButStartNode()); + private static class PeopleTraversalBuilder implements FieldTraversalDescriptionBuilder { + @Override + public TraversalDescription build(NodeBacked start, Field field, String... params) { + return new TraversalDescriptionImpl() + .relationships(DynamicRelationshipType.withName(params[0])) + .filter(Traversal.returnAllButStartNode()); + } } } -} ]]> - - +
\ No newline at end of file diff --git a/src/docbkx/reference/programming-model/relationships.xml b/src/docbkx/reference/programming-model/relationships.xml index 626821545..083fe52de 100644 --- a/src/docbkx/reference/programming-model/relationships.xml +++ b/src/docbkx/reference/programming-model/relationships.xml @@ -9,7 +9,7 @@ Spring Data Graph has special support to represent Neo4j relationships as entities too, but it is often not needed.
-
+
@RelatedTo: Connecting node entities Every field of a node entity that references one or more other node entities is backed by relationships @@ -45,7 +45,8 @@ public class Movie { mostPaidIn; @RelatedTo(type = "ACTS_IN", elementClass = Movie.class) diff --git a/src/docbkx/reference/programming-model/repositories.xml b/src/docbkx/reference/programming-model/repositories.xml index 3fbec2230..1d80a9e07 100644 --- a/src/docbkx/reference/programming-model/repositories.xml +++ b/src/docbkx/reference/programming-model/repositories.xml @@ -1,6 +1,6 @@ -
+
CRUD with repositories The repositories provided by Spring Data Graph build on the composable repository infrastructure @@ -105,7 +105,8 @@ Using GraphRepositories graphRepository = graphRepositoryFactory.createGraphRepository(Person.class); +GraphRepository graphRepository = graphRepositoryFactory + .createGraphRepository(Person.class); Person michael = graphRepository.save(new Person("Michael", 36)); @@ -140,8 +141,9 @@ Iterable davesFriends = graphRepository.findAllByTraversal(dave, public interface PersonRepository extends GraphRepository, PersonRepositoryExtension {} // alternatively select some of the required repositories individually -public interface PersonRepository extends CRUDGraphRepository, IndexQueryExecutor, - TraversalQueryExecutor, PersonRepositoryExtension {} +public interface PersonRepository extends CRUDGraphRepository, + IndexQueryExecutor, TraversalQueryExecutor, + PersonRepositoryExtension {} // provide a custom extension if needed public interface PersonRepositoryExtension { @@ -156,8 +158,10 @@ public class PersonRepositoryImpl implements PersonRepositoryExtension { } } -// configure the repositories, preferably via the datagraph:repositories namespace (graphDatabaseContext reference is optional) - +// configure the repositories, preferably via the datagraph:repositories namespace +// (graphDatabaseContext reference is optional) + // have it injected @Autowired diff --git a/src/docbkx/reference/programming-model/typerepresentationstrategy.xml b/src/docbkx/reference/programming-model/typerepresentationstrategy.xml index ce28c4304..83f38ce1e 100644 --- a/src/docbkx/reference/programming-model/typerepresentationstrategy.xml +++ b/src/docbkx/reference/programming-model/typerepresentationstrategy.xml @@ -1,7 +1,7 @@
- Entity types stored + Entity type representation There are several ways to represent the Java type hierarchy of the data model in the graph. In general, for all node and relationship entities, type information is needed to perform certain repository operations. Some of From 227d498b39b2704a697b44ef12dde0361d529f22 Mon Sep 17 00:00:00 2001 From: David Montag Date: Mon, 11 Apr 2011 16:12:13 -0700 Subject: [PATCH 4/5] Clarified repositories in reference. --- .../programming-model/repositories.xml | 232 ++++++++++-------- 1 file changed, 131 insertions(+), 101 deletions(-) diff --git a/src/docbkx/reference/programming-model/repositories.xml b/src/docbkx/reference/programming-model/repositories.xml index 1d80a9e07..71cf38ecb 100644 --- a/src/docbkx/reference/programming-model/repositories.xml +++ b/src/docbkx/reference/programming-model/repositories.xml @@ -8,17 +8,17 @@ They allow for interface based composition of repositories consisting of provided default implementations for certain interfaces and additional custom implementations for other methods. - - - Spring Data Graph provides only the infrastructure and some default repository implementations - so far. Future releases will support finders derived from method names, named queries, and - annotated query methods. - (e.g. - findByName(name), - @Query(name="find-by-name-query") findByName(name), and - @Query(query="{name:%s}") findByName(name)) - - + + + + + + + + + + + Spring Data Graph comes with typed repository implementations that provide methods for locating node and relationship entities. There are 3 types of basic repository interfaces @@ -27,106 +27,136 @@ indexing subsystem for queries, and TraversalRepository handles Neo4j traversals. - CRUDRepository delegates to the configured TypeRepresentationStrategy - (see ) - for type based queries. - - - Load an instance via a Neo4j node id - T findOne(id) - - - Check for existence of a Neo4j node id - boolean exists(id) - - - Iterate over all nodes of a node entity type - Iterable<T> findAll() - (supported in future versions: - Iterable<T> findAll(Sort) and - Page<T> findAll(Pageable)) - - - Count the instances of a node entity type - Long count() - - - Save a graph entity - T save(T) and Iterable<T> save(Iterable<T>) - - - Delete a graph entity - void delete(T), void; delete(Iterable<T>), - and deleteAll() - - - - - - IndexRepository works with the indexing subsystem and provides methods to find - entities by indexed properties, ranged queries, and combinations thereof. The index key is - the name of the indexed entity field, unless overridden in the @Indexed annotation. - - - Iterate over all indexed entity instances with a certain field value - Iterable<T> findAllByPropertyValue(key, value) - - - Get a single entity instance with a certain field value - T findByPropertyValue(key, value) - - - Iterate over all indexed entity instances with field values in a certain numerical range (inclusive) - Iterable<T> findAllByRange(key, from, to) - - - Iterate over all indexed entity instances with field values matching the given fulltext string or QueryContext query - Iterable<T> findAllByQuery(key, queryOrQueryContext) - - - There is also a NamedIndexRepository with the same methods, but with an additional index - name parameter, making it possible to query any index. + GraphRepository is a convenience repository interface, extending CRUDRepository, + IndexRepository, and TraversalRepository. Generally, it has all the + desired repository methods. If named index operations are required, then NamedIndexRepository + may also be included. - - TraversalRepository delegates to the Neo4j traversal framework. - - - Iterate over a traversal result - Iterable<T> findAllByTraversal(startEntity, traversalDescription) - - - - - The Repository instances are either created manually via a - DirectGraphRepositoryFactory, bound to a concrete node or relationship entity class. - The DirectGraphRepositoryFactory is configured in the Spring context and can be injected. - - - Using GraphRepositories - graphRepository = graphRepositoryFactory - .createGraphRepository(Person.class); +
+ CRUDRepository + + CRUDRepository delegates to the configured TypeRepresentationStrategy + (see ) + for type based queries. + + + Load an instance via a Neo4j node id + T findOne(id) + + + Check for existence of a Neo4j node id + boolean exists(id) + + + Iterate over all nodes of a node entity type + Iterable<T> findAll() + (supported in future versions: + Iterable<T> findAll(Sort) and + Page<T> findAll(Pageable)) + + + Count the instances of a node entity type + Long count() + + + Save a graph entity + T save(T) and Iterable<T> save(Iterable<T>) + + + Delete a graph entity + void delete(T), void; delete(Iterable<T>), + and deleteAll() + + + + + Important to note here is that the save, delete, and deleteAll + methods are only there to conform to the org.springframework.data.repository.Repository + interface. The recommended way of saving and deleting entities is by using entity.persist() + and entity.remove(). + +
-Person michael = graphRepository.save(new Person("Michael", 36)); +
+ IndexRepository and NamedIndexRepository + + IndexRepository works with the indexing subsystem and provides methods to find + entities by indexed properties, ranged queries, and combinations thereof. The index key is + the name of the indexed entity field, unless overridden in the @Indexed annotation. + + + Iterate over all indexed entity instances with a certain field value + Iterable<T> findAllByPropertyValue(key, value) + + + Get a single entity instance with a certain field value + T findByPropertyValue(key, value) + + + Iterate over all indexed entity instances with field values in a certain numerical range (inclusive) + Iterable<T> findAllByRange(key, from, to) + + + Iterate over all indexed entity instances with field values matching the given fulltext string or QueryContext query + Iterable<T> findAllByQuery(key, queryOrQueryContext) + + + + + There is also a NamedIndexRepository with the same methods, but with an additional index + name parameter, making it possible to query any index. + -Person dave = graphRepository.findOne(123); +
-Long numberOfPeople = graphRepository.count(); +
+ TraversalRepository + + TraversalRepository delegates to the Neo4j traversal framework. + + + Iterate over a traversal result + Iterable<T> findAllByTraversal(startEntity, traversalDescription) + + + +
-Person mark = graphRepository.findByPropertyValue("name", "mark"); +
+ Creating repositories + + The Repository instances are either created manually via a + DirectGraphRepositoryFactory, bound to a concrete node or relationship entity class. + The DirectGraphRepositoryFactory is configured in the Spring context and can be injected. + + + Using GraphRepositories + graphRepository = graphRepositoryFactory + .createGraphRepository(Person.class); -Iterable devs = graphRepository.findAllByProperyValue("occupation", "developer"); + Person michael = graphRepository.save(new Person("Michael", 36)); -Iterable middleAgedPeople = graphRepository.findAllByRange("age", 20, 40); + Person dave = graphRepository.findOne(123); -Iterable aTeam = graphRepository.findAllByQuery("name", "A*"); + Long numberOfPeople = graphRepository.count(); + + Person mark = graphRepository.findByPropertyValue("name", "mark"); + + Iterable devs = graphRepository.findAllByProperyValue("occupation", "developer"); + + Iterable middleAgedPeople = graphRepository.findAllByRange("age", 20, 40); + + Iterable aTeam = graphRepository.findAllByQuery("name", "A*"); + + Iterable davesFriends = graphRepository.findAllByTraversal(dave, + Traversal.description().pruneAfterDepth(1) + .relationships(KNOWS).filter(returnAllButStartNode())); + ]]> + +
-Iterable davesFriends = graphRepository.findAllByTraversal(dave, - Traversal.description().pruneAfterDepth(1) - .relationships(KNOWS).filter(returnAllButStartNode())); -]]>
-
Composing repositories From fcb833176110e93357061cbfce524c69fde656fc Mon Sep 17 00:00:00 2001 From: David Montag Date: Mon, 11 Apr 2011 16:57:57 -0700 Subject: [PATCH 5/5] Edited samples chapter in reference. --- src/docbkx/reference/samples.xml | 167 +++++++++++++++++-------------- 1 file changed, 91 insertions(+), 76 deletions(-) diff --git a/src/docbkx/reference/samples.xml b/src/docbkx/reference/samples.xml index 702ad69bf..ee5d86eaa 100644 --- a/src/docbkx/reference/samples.xml +++ b/src/docbkx/reference/samples.xml @@ -2,86 +2,101 @@ - Sample code + Sample code -
- Introduction - - Spring Data Graph comes with a number of samples. - The source code of the samples is found on - GitHub. - The different sample projects are introduced below. - -
+
+ Introduction + + Spring Data Graph comes with a number of sample applications. The source code of the samples can be found on + Github. The different sample + projects are introduced below. + +
-
- Hello Worlds sample - The Hello Worlds sample application is a simple console application with unit tests, that - creates some Worlds (entities / nodes) and Rocket Routes (relationships) in a Galaxy (graph) - and then reads them back and prints them out. - The unit tests demonstrate some other features of Spring Data Graph. The sample comes with a - minimal configuration for Maven and Spring to get up and running quickly. - Executing the application creates the following graph in the Graph Database: - - - - - -
+
+ Hello Worlds sample application + + The Hello Worlds sample application is a simple console application. It creates some worlds + (node entities) and rocket routes (relationships) between worlds, all in a galaxy (the graph), + and then prints them. + + + The unit tests demonstrate some other features of Spring Data Graph as well. The sample comes + with a minimal configuration for Maven and Spring to get up and running quickly. + + + Executing the application creates the following graph in the graph database: + + + + + + +
-
- IMDB sample - A web application that imports datasets from the Internet Movie Database (IMDB) into - the - graph database. It allows listings of movies with their actors and actors with their roles in - different movies. It also uses graph traversal operations to calculate the Kevin Bacon number - (distance to an actor that has acted with Kevin Bacon). This sample application shows the - basic usage of Spring Data Graph in a more complex setting with several annotated entities and - relationships as well as usage of indices and graph traversal. - See the readme file for instruction on how to compile and run the application. - An excerpt of the data stored in the Graph Database after executing the application: - - - - - - - - - - -
+
+ IMDB sample application + + The IMDB sample is a web application that imports datasets from the Internet Movie Database (IMDB) + into the graph database. It allows the listing of movies with their actors, and of actors and their + roles in different movies. It also uses graph traversal operations to calculate the + Bacon number of any given actor. + This sample application shows the usage of Spring Data Graph in a more complex setting, using several + annotated entities and relationships as well as indexes and graph traversals. + + + See the readme file for instructions on how to compile and run the application. + + + An excerpt of the data stored in the graph database after executing the application: + + + + + + + + + + +
-
- MyRestaurant sample - Simple, JPA based web application for managing users and restaurants, with the ability to add - restaurants as favorites to a user. - - - - - -
+
+ MyRestaurants sample application + + Simple, JPA-based web application for managing users and restaurants, with the ability to add + restaurants as favorites to a user. It is basically the foundation for the MyRestaurants-Social + application (see), and does therefore not use + Spring Data Graph. + + + + + + +
-
- MyRestaurant-Social sample - An extended version of the MyRestaurant sample application that adds social networking - functionality to it. It is possible to have friends and to add rated relationships to - restaurants. The relationships and some of the properties of the entities are transparently - stored in the graph database. There is also a graph traversal that provides a recommendation - based on your friends' (and their friends') rating of restaurants. - An excerpt of the data stored in the Graph Database after executing the application: - - - - - - - - - - - -
+
+ MyRestaurant-Social sample application + + This application extends the MyRestaurants sample application, adding social networking + functionality to it with cross-store persistence. The web application allows for users to add + friends and rate restaurants. A graph traversal provides recommendations based on your friends' + (and their friends') rating of restaurants. + + + Here's an excerpt of the data stored in the graph database after executing the application: + + + + + + + + + + + +