Merge branch 'master' into snapshot
This commit is contained in:
@@ -62,6 +62,4 @@ public interface EntityState<ENTITY extends GraphBacked<STATE>,STATE> {
|
||||
STATE getPersistentState();
|
||||
|
||||
ENTITY persist();
|
||||
|
||||
boolean refersTo(GraphBacked target);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,4 @@ public interface NodeBacked extends GraphBacked<Node> {
|
||||
* @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);
|
||||
}
|
||||
|
||||
@@ -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<NodeBacked> backrefs=new ArrayList<NodeBacked>();
|
||||
private EntityState<?, ?> entityState;
|
||||
|
||||
public BackReferences(EntityState<?,?> entityState) {
|
||||
|
||||
this.entityState = entityState;
|
||||
}
|
||||
|
||||
public void addBackReferences(Collection<NodeBacked> backReference) {
|
||||
this.backrefs.addAll(backReference);
|
||||
}
|
||||
|
||||
|
||||
private void pruneInvalidBackRefs() {
|
||||
GraphBacked entity = entityState.getEntity();
|
||||
for (Iterator<NodeBacked> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,6 @@ public abstract class DefaultEntityState<ENTITY extends GraphBacked<STATE>, STAT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected Object getIdFromEntity() {
|
||||
final Field idField = fieldAccessorFactoryProviders.getIdField();
|
||||
if (idField==null) return null;
|
||||
@@ -120,9 +119,4 @@ public abstract class DefaultEntityState<ENTITY extends GraphBacked<STATE>, STAT
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean refersTo(GraphBacked target) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ENTITY extends GraphBacked<STATE>, STATE> imple
|
||||
protected final EntityState<ENTITY,STATE> delegate;
|
||||
private final static Log log = LogFactory.getLog(DetachedEntityState.class);
|
||||
private GraphDatabaseContext graphDatabaseContext;
|
||||
private final BackReferences backReferences = null;
|
||||
public DetachedEntityState(final EntityState<ENTITY, STATE> delegate, GraphDatabaseContext graphDatabaseContext) {
|
||||
this.delegate = delegate;
|
||||
this.graphDatabaseContext = graphDatabaseContext;
|
||||
//this.backReferences = new BackReferences(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,14 +152,31 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, 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<Field, ExistingValue> 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<Field, ExistingValue> dirtyCopy = new HashMap<Field, ExistingValue>(dirty);
|
||||
clearDirty();
|
||||
for (final Map.Entry<Field, ExistingValue> 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<Object>)valueFromEntity) {
|
||||
if (o instanceof NodeBacked) {
|
||||
((NodeBacked) o).persist();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +234,6 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, STATE> imple
|
||||
Transaction tx = graphDatabaseContext.beginTx();
|
||||
try {
|
||||
ENTITY result = delegate.persist();
|
||||
//persistNeighbours();
|
||||
|
||||
flushDirty();
|
||||
tx.success();
|
||||
@@ -226,46 +242,4 @@ public class DetachedEntityState<ENTITY extends GraphBacked<STATE>, STATE> imple
|
||||
tx.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void persistNeighbours() {
|
||||
backReferences.persistNeighbours();
|
||||
for (NodeBacked nodeBacked : getOutboundDirtyNodeEntities()) {
|
||||
nodeBacked.persist();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<NodeBacked> getOutboundDirtyNodeEntities() {
|
||||
HashSet<NodeBacked> result = new HashSet<NodeBacked>();
|
||||
for (Field field : dirty.keySet()) {
|
||||
if (handleSingleField(result, field)) continue;
|
||||
handleOneToMany(result, field);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean handleOneToMany(HashSet<NodeBacked> 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<NodeBacked> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -119,9 +119,6 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields<No
|
||||
public <T extends NodeBacked> 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) {
|
||||
|
||||
@@ -67,6 +67,10 @@ class HasRelationshipMatcher extends TypeSafeMatcher<Node>
|
||||
{
|
||||
description.appendText( "Expected relationship named " + relationshipTypeName + " to " +(other==null ? "unspecified": other)+"\r\n got: " );
|
||||
|
||||
if (relationships == null) {
|
||||
description.appendValue("[]");
|
||||
return;
|
||||
}
|
||||
List<String> types = new ArrayList<String>();
|
||||
for ( Relationship rel : relationships )
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
indexes (via the IndexManager), database life cycle callbacks, transaction management, and more.
|
||||
</para>
|
||||
<para>
|
||||
The EmbeddedGraphDatabaseService is an implementation of GraphDatabaseService that is used to
|
||||
The <code>EmbeddedGraphDatabase</code> 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
|
||||
<ulink url="http://wiki.neo4j.org/content/Getting_Started_With_Neo4j_Server">Neo4j server</ulink>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<para>
|
||||
Behind the scenes, Spring Data Graph leverages <ulink url="http://www.eclipse.org/aspectj/">AspectJ</ulink>
|
||||
aspects to modify the behavior of simple annotated POJO entities
|
||||
(see <xref linkend="reference:aspectj-intro"/>). Each node entity is backed by a graph node that holds its
|
||||
(see <xref linkend="reference:aspectj-details"/>). 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.
|
||||
</para>
|
||||
|
||||
@@ -1,36 +1,129 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section>
|
||||
<title>Detached entities</title>
|
||||
<title>Detached node entities</title>
|
||||
<para>
|
||||
By default newly created node entities are in a detached state. When <code>persist()</code> 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 <code>persist()</code> is called on the entity, it becomes
|
||||
attached to the graph, and its properties and relationships are stores in the database. If
|
||||
<code>persist()</code> is not called within a transaction, it automatically creates an implicit
|
||||
transaction for the operation.
|
||||
</para>
|
||||
<para>
|
||||
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 <code>persist()</code> 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 <code>persist()</code>.
|
||||
</para>
|
||||
<para>
|
||||
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 <code>persist()</code> for the data to be saved.
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@NodeEntity
|
||||
<!--<para>-->
|
||||
<!--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.-->
|
||||
<!--</para>-->
|
||||
<example>
|
||||
<title>Persisting entities</title>
|
||||
<programlisting language="java"><![CDATA[@NodeEntity
|
||||
class Person {
|
||||
String name;
|
||||
Person(String name) { this.name = name; }
|
||||
}
|
||||
Person p = new Person().persist();
|
||||
|
||||
// Store Michael in the database.
|
||||
Person p = new Person("Michael").persist();
|
||||
]]></programlisting>
|
||||
</example>
|
||||
<section id="reference:programming-model:detached:relating">
|
||||
<title>Relating detached entities</title>
|
||||
<para>
|
||||
As mentioned above, an entity simply created with the <code>new</code> keyword starts out detached.
|
||||
It also has no state assigned to it. If you create a new entity with <code>new</code> and then throw
|
||||
it away, the database won't be touched at all.
|
||||
</para>
|
||||
<para>
|
||||
Now consider this scenario:
|
||||
<example>
|
||||
<title>Relationships outside of transactions</title>
|
||||
<programlisting><![CDATA[@NodeEntity
|
||||
class Movie {
|
||||
private Actor topActor;
|
||||
public void setTopActor(Actor actor) {
|
||||
topActor = actor;
|
||||
}
|
||||
}
|
||||
|
||||
@NodeEntity
|
||||
class Actor {
|
||||
}
|
||||
|
||||
Movie movie = new Movie();
|
||||
Actor actor = new Actor();
|
||||
|
||||
movie.setTopActor(actor);
|
||||
]]></programlisting>
|
||||
</example>
|
||||
</para>
|
||||
<para>
|
||||
Neither the actor nor the movie has been assigned a node in the graph. If we were to call
|
||||
<code>movie.persist()</code>, 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.
|
||||
</para>
|
||||
<para>
|
||||
Important to note here is that if <code>actor.persist()</code> 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.
|
||||
</para>
|
||||
<para>
|
||||
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 <code>persist()</code>
|
||||
is however only cascaded to related entity fields that have been modified.
|
||||
</para>
|
||||
<para>
|
||||
In the following example, the actor and the movie are both attached entites, having both been
|
||||
previously persisted to the graph:
|
||||
<example>
|
||||
<title>Cascade for modified fields</title>
|
||||
<programlisting><![CDATA[actor.setName("Billy Bob");
|
||||
movie.persist();
|
||||
]]></programlisting>
|
||||
</example>
|
||||
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 <code>movie.persist()</code>. The reason for this is, as
|
||||
mentioned above, that cascading will only be done for fields that have been modified. Since the
|
||||
<code>movie.topActor</code> field has not been modified, it will not cascade the persist operation
|
||||
to the actor.
|
||||
</para>
|
||||
</section>
|
||||
<!--<para>-->
|
||||
<!--Please keep in mind that the detached behavior is still being heavily developed. The defaults and-->
|
||||
<!--other aspects of the behavior are likely to change in subsequent releases. At the moment there-->
|
||||
<!--is no support for the creation of relationships outside of transactions. More complex operations-->
|
||||
<!--like creating whole subgraphs outside of transactions is not supported.-->
|
||||
<!--</para>-->
|
||||
<!--<note>-->
|
||||
<!--<para>-->
|
||||
|
||||
<!--</para>-->
|
||||
<!--<para>-->
|
||||
<!--In certain cases, it is actually possible to create relationships outside of a-->
|
||||
<!--transactional context. If the entities and their relationships form a <ulink-->
|
||||
<!--url="http://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG (Directed Acyclic Graph)</ulink>,-->
|
||||
<!--then they can be persisted <emphasis>if</emphasis> <code>persist()</code> is called on every entity-->
|
||||
<!--<emphasis>in reverse order from the leaf nodes</emphasis>.-->
|
||||
<!--</para>-->
|
||||
<!--<para>-->
|
||||
<!--For example, assume that instances of entity classes A, B, and C are linked like so:-->
|
||||
<!--<code>B2 <- A -> B1 -> C</code>. In order to persist this graph without transactions, one would-->
|
||||
<!--first have to persist <code>C</code>, then <code>B1</code> and <code>B2</code>, and finally-->
|
||||
<!--<code>A</code>. If one wants to use this-->
|
||||
<!--</para>-->
|
||||
<!--</note>-->
|
||||
</section>
|
||||
|
||||
@@ -3,22 +3,24 @@
|
||||
<section>
|
||||
<title>Bean validation (JSR-303)</title>
|
||||
<para>
|
||||
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. <code>@Min</code>, <code>@Max</code>,
|
||||
<code>@Size</code>, etc. Validation errors throw a <code>ValidationException</code>. 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 <code>GraphDatabaseContext</code>.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
<example>
|
||||
<title>Bean validation</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@NodeEntity
|
||||
class Person {
|
||||
@Size(min = 3, max = 20)
|
||||
String name;
|
||||
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
@Min(0) @Max(100)
|
||||
int age;
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
package.
|
||||
</para>
|
||||
<para>
|
||||
The indexes can be queried by using a repository (see <xref linkend="reference:repositories" />).
|
||||
The indexes can be queried by using a repository (see
|
||||
<xref linkend="reference:programming-model:repositories" />).
|
||||
Typically, the repository is an instance of
|
||||
<code>org.springframework.data.graph.neo4j.repository.DirectGraphRepositoryFactory</code>.
|
||||
The methods <code>findByPropertyValue()</code> and <code>findAllByPropertyValue()</code> work on
|
||||
@@ -45,7 +46,8 @@ class Person {
|
||||
@Indexed int age;
|
||||
}
|
||||
|
||||
GraphRepository<Person> graphRepository = graphRepositoryFactory.createGraphRepository(Person.class);
|
||||
GraphRepository<Person> 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<Person> graphRepository = graphRepositoryFactory.createGraphRepository(Person.class);
|
||||
GraphRepository<Person> graphRepository = graphRepositoryFactory
|
||||
.createGraphRepository(Person.class);
|
||||
|
||||
Person mark = graphRepository.findAllByQuery("people-search", "name", "ma*");
|
||||
]]></programlisting>
|
||||
|
||||
@@ -9,92 +9,90 @@
|
||||
<section>
|
||||
<title>@NodeEntity: The basic building block</title>
|
||||
<para>
|
||||
The <code>@NodeEntity</code> 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 <code>useShortNames</code> is set to false, the properties and relationship
|
||||
names used will be prepended with the class name of the entity.
|
||||
</para><para>
|
||||
If the <code>partial</code>
|
||||
parameter is set to true, this entity takes part in a cross-store setting /<xref linkend="cross-store"/>)
|
||||
where only the specifically annotated parts of the entity not handled by JPA will be mapped to the graph store.
|
||||
The <code>@NodeEntity</code> 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
|
||||
<code>useShortNames</code> attribute overridden to false, the property and relationship names will
|
||||
have the class name of the entity prepended.
|
||||
</para>
|
||||
<para>Entity fields can be annotated with @GraphProperty, @RelatedTo, @RelatedToVia, @Indexed, @GraphId and
|
||||
@GraphTraversal.
|
||||
<para>
|
||||
If the <code>partial</code> 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
|
||||
<xref linkend="cross-store"/> for more information.
|
||||
</para>
|
||||
<example>
|
||||
<title>Simple Node Entity</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
// simplest example
|
||||
@NodeEntity
|
||||
<para>
|
||||
Entity fields can be annotated with <code>@GraphProperty</code>, <code>@RelatedTo</code>,
|
||||
<code>@RelatedToVia</code>, <code>@Indexed</code>, <code>@GraphId</code> and
|
||||
<code>@GraphTraversal</code>.
|
||||
</para>
|
||||
<example>
|
||||
<title>Simple node entity</title>
|
||||
<programlisting language="java"><![CDATA[@NodeEntity
|
||||
public class Movie {
|
||||
String title;
|
||||
String title;
|
||||
}
|
||||
]]></programlisting>
|
||||
</example>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>@GraphProperty: Optional Annotation for Property Fields</title>
|
||||
<para>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).
|
||||
<title>@GraphProperty: Optional annotation for property fields</title>
|
||||
<para>
|
||||
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 <code>Enum</code>s and <code>Date</code>s.
|
||||
Transient fields are not persisted.
|
||||
This annotation is mainly used for cross-store persistence.
|
||||
</para>
|
||||
<para>
|
||||
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 <code>@GraphProperty</code>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>@Indexed: Making entities searchable by field value</title>
|
||||
<para>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 <code>Repository</code> for a particular node or relationship entity, created via a
|
||||
<code>DirectGraphRepositoryFactory</code>.
|
||||
</para>
|
||||
<para>
|
||||
GraphDatabaseContext exposes the indexes for Nodes and Relationships via the <code>getIndex</code> method.
|
||||
Index names default to the domain class
|
||||
name, but can also be named (<code>indexName</code> attribute)individually to reflect domain concepts.
|
||||
be named, for instance to keep separate domain concepts in separate indexes.
|
||||
</para>
|
||||
<para>
|
||||
Numerical values are indexed as such by default, allowing for range queries.
|
||||
Fulltext indexing is also possible by setting the <code>fulltext</code> attribute to true. For details see
|
||||
the indexing section <xref linkend="reference:programming-model:indexing"/>.
|
||||
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 <xref linkend="reference:programming-model:indexing"/> and
|
||||
<xref linkend="reference:programming-model:repositories"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>@GraphTraversal: fields providing direct access to traversal results</title>
|
||||
<para>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 <code>traversalBuilder</code>
|
||||
attribute of the annotation. The class of the expected NodeEntities is provided with the
|
||||
<title>@GraphTraversal: fields as traversal result views</title>
|
||||
<para>
|
||||
The <code>@GraphTraversal</code> 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 <code>TraversalDescription</code> used for this is created by the
|
||||
<code>FieldTraversalDescriptionBuilder</code> class defined by the <code>traversalBuilder</code>
|
||||
attribute. The class of the resulting node entities must be provided with the
|
||||
<code>elementClass</code> attribute.
|
||||
<example>
|
||||
<title>@GraphTraversal in a Node Entity</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@NodeEntity
|
||||
</para>
|
||||
<example>
|
||||
<title>@GraphTraversal from a node entity</title>
|
||||
<programlisting language="java"><![CDATA[@NodeEntity
|
||||
public class Group {
|
||||
@GraphTraversal(traversalBuilder = PeopleTraversalBuilder.class,
|
||||
elementClass = Person.class, params = "persons")
|
||||
private Iterable<Person> people;
|
||||
@GraphTraversal(traversalBuilder = PeopleTraversalBuilder.class,
|
||||
elementClass = Person.class, params = "persons")
|
||||
private Iterable<Person> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></programlisting>
|
||||
|
||||
</example>
|
||||
</para>
|
||||
</example>
|
||||
</section>
|
||||
</section>
|
||||
@@ -9,7 +9,7 @@
|
||||
Spring Data Graph has special support to represent Neo4j relationships as entities too, but it is often
|
||||
not needed.
|
||||
</para>
|
||||
<section>
|
||||
<section id="reference:programming_model:relationships:relatedto">
|
||||
<title>@RelatedTo: Connecting node entities</title>
|
||||
<para>
|
||||
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 {
|
||||
<programlisting language="java"><![CDATA[
|
||||
@NodeEntity
|
||||
public class Actor {
|
||||
@RelatedTo(type = "mostPaidActor", direction = Direction.INCOMING, elementClass = Movie.class)
|
||||
@RelatedTo(type = "mostPaidActor", direction = Direction.INCOMING,
|
||||
elementClass = Movie.class)
|
||||
private Set<Movie> mostPaidIn;
|
||||
|
||||
@RelatedTo(type = "ACTS_IN", elementClass = Movie.class)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section id="reference:repositories">
|
||||
<section id="reference:programming-model:repositories">
|
||||
<title>CRUD with repositories</title>
|
||||
<para>
|
||||
The repositories provided by Spring Data Graph build on the composable repository infrastructure
|
||||
@@ -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.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
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.
|
||||
<code>findByName(name)</code>,
|
||||
<code>@Query(name="find-by-name-query") findByName(name)</code>, and
|
||||
<code>@Query(query="{name:%s}") findByName(name)</code>)
|
||||
</para>
|
||||
</note>
|
||||
<!--<note>-->
|
||||
<!--<para>-->
|
||||
<!--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.-->
|
||||
<!--<code>findByName(name)</code>,-->
|
||||
<!--<code>@Query(name="find-by-name-query") findByName(name)</code>, and-->
|
||||
<!--<code>@Query(query="{name:%s}") findByName(name)</code>)-->
|
||||
<!--</para>-->
|
||||
<!--</note>-->
|
||||
<para>
|
||||
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,105 +27,136 @@
|
||||
indexing subsystem for queries, and <code>TraversalRepository</code> handles Neo4j traversals.
|
||||
</para>
|
||||
<para>
|
||||
<code>CRUDRepository</code> delegates to the configured <code>TypeRepresentationStrategy</code>
|
||||
(see <xref linkend="reference:programming-model:typerepresentationstrategy"/>)
|
||||
for type based queries.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Load an instance via a Neo4j node id</term>
|
||||
<listitem><para><code>T findOne(id)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Check for existence of a Neo4j node id</term>
|
||||
<listitem><para><code>boolean exists(id)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Iterate over all nodes of a node entity type</term>
|
||||
<listitem><para><code>Iterable<T> findAll()</code>
|
||||
(supported in future versions:
|
||||
<code>Iterable<T> findAll(Sort)</code> and
|
||||
<code>Page<T> findAll(Pageable)</code>)</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Count the instances of a node entity type</term>
|
||||
<listitem><para><code>Long count()</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Save a graph entity</term>
|
||||
<listitem><para><code>T save(T)</code> and <code>Iterable<T> save(Iterable<T>)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Delete a graph entity</term>
|
||||
<listitem><para><code>void delete(T)</code>, <code>void; delete(Iterable<T>)</code>,
|
||||
and <code>deleteAll()</code></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
<para>
|
||||
<code>IndexRepository</code> 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 <code>@Indexed</code> annotation.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Iterate over all indexed entity instances with a certain field value</term>
|
||||
<listitem><para><code>Iterable<T> findAllByPropertyValue(key, value)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Get a single entity instance with a certain field value</term>
|
||||
<listitem><para><code>T findByPropertyValue(key, value)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Iterate over all indexed entity instances with field values in a certain numerical range (inclusive)</term>
|
||||
<listitem><para><code>Iterable<T> findAllByRange(key, from, to)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Iterate over all indexed entity instances with field values matching the given fulltext string or QueryContext query</term>
|
||||
<listitem><para><code>Iterable<T> findAllByQuery(key, queryOrQueryContext)</code></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
There is also a <code>NamedIndexRepository</code> with the same methods, but with an additional index
|
||||
name parameter, making it possible to query any index.
|
||||
<code>GraphRepository</code> is a convenience repository interface, extending <code>CRUDRepository</code>,
|
||||
<code>IndexRepository</code>, and <code>TraversalRepository</code>. Generally, it has all the
|
||||
desired repository methods. If named index operations are required, then <code>NamedIndexRepository</code>
|
||||
may also be included.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<code>TraversalRepository</code> delegates to the Neo4j traversal framework.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Iterate over a traversal result</term>
|
||||
<listitem><para><code>Iterable<T> findAllByTraversal(startEntity, traversalDescription)</code></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
<para>
|
||||
The <code>Repository</code> instances are either created manually via a
|
||||
<code>DirectGraphRepositoryFactory</code>, bound to a concrete node or relationship entity class.
|
||||
The <code>DirectGraphRepositoryFactory</code> is configured in the Spring context and can be injected.
|
||||
</para>
|
||||
<example>
|
||||
<title>Using GraphRepositories</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
GraphRepository<Person> graphRepository = graphRepositoryFactory.createGraphRepository(Person.class);
|
||||
<section>
|
||||
<title>CRUDRepository</title>
|
||||
<para>
|
||||
<code>CRUDRepository</code> delegates to the configured <code>TypeRepresentationStrategy</code>
|
||||
(see <xref linkend="reference:programming-model:typerepresentationstrategy"/>)
|
||||
for type based queries.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Load an instance via a Neo4j node id</term>
|
||||
<listitem><para><code>T findOne(id)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Check for existence of a Neo4j node id</term>
|
||||
<listitem><para><code>boolean exists(id)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Iterate over all nodes of a node entity type</term>
|
||||
<listitem><para><code>Iterable<T> findAll()</code>
|
||||
(supported in future versions:
|
||||
<code>Iterable<T> findAll(Sort)</code> and
|
||||
<code>Page<T> findAll(Pageable)</code>)</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Count the instances of a node entity type</term>
|
||||
<listitem><para><code>Long count()</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Save a graph entity</term>
|
||||
<listitem><para><code>T save(T)</code> and <code>Iterable<T> save(Iterable<T>)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Delete a graph entity</term>
|
||||
<listitem><para><code>void delete(T)</code>, <code>void; delete(Iterable<T>)</code>,
|
||||
and <code>deleteAll()</code></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
<para>
|
||||
Important to note here is that the <code>save</code>, <code>delete</code>, and <code>deleteAll</code>
|
||||
methods are only there to conform to the <code>org.springframework.data.repository.Repository</code>
|
||||
interface. The recommended way of saving and deleting entities is by using <code>entity.persist()</code>
|
||||
and <code>entity.remove()</code>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
Person michael = graphRepository.save(new Person("Michael", 36));
|
||||
<section>
|
||||
<title>IndexRepository and NamedIndexRepository</title>
|
||||
<para>
|
||||
<code>IndexRepository</code> 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 <code>@Indexed</code> annotation.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Iterate over all indexed entity instances with a certain field value</term>
|
||||
<listitem><para><code>Iterable<T> findAllByPropertyValue(key, value)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Get a single entity instance with a certain field value</term>
|
||||
<listitem><para><code>T findByPropertyValue(key, value)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Iterate over all indexed entity instances with field values in a certain numerical range (inclusive)</term>
|
||||
<listitem><para><code>Iterable<T> findAllByRange(key, from, to)</code></para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>Iterate over all indexed entity instances with field values matching the given fulltext string or QueryContext query</term>
|
||||
<listitem><para><code>Iterable<T> findAllByQuery(key, queryOrQueryContext)</code></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
<para>
|
||||
There is also a <code>NamedIndexRepository</code> with the same methods, but with an additional index
|
||||
name parameter, making it possible to query any index.
|
||||
</para>
|
||||
|
||||
Person dave = graphRepository.findOne(123);
|
||||
</section>
|
||||
|
||||
Long numberOfPeople = graphRepository.count();
|
||||
<section>
|
||||
<title>TraversalRepository</title>
|
||||
<para>
|
||||
<code>TraversalRepository</code> delegates to the Neo4j traversal framework.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>Iterate over a traversal result</term>
|
||||
<listitem><para><code>Iterable<T> findAllByTraversal(startEntity, traversalDescription)</code></para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
Person mark = graphRepository.findByPropertyValue("name", "mark");
|
||||
<section>
|
||||
<title>Creating repositories</title>
|
||||
<para>
|
||||
The <code>Repository</code> instances are either created manually via a
|
||||
<code>DirectGraphRepositoryFactory</code>, bound to a concrete node or relationship entity class.
|
||||
The <code>DirectGraphRepositoryFactory</code> is configured in the Spring context and can be injected.
|
||||
</para>
|
||||
<example>
|
||||
<title>Using GraphRepositories</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
GraphRepository<Person> graphRepository = graphRepositoryFactory
|
||||
.createGraphRepository(Person.class);
|
||||
|
||||
Iterable<Person> devs = graphRepository.findAllByProperyValue("occupation", "developer");
|
||||
Person michael = graphRepository.save(new Person("Michael", 36));
|
||||
|
||||
Iterable<Person> middleAgedPeople = graphRepository.findAllByRange("age", 20, 40);
|
||||
Person dave = graphRepository.findOne(123);
|
||||
|
||||
Iterable<Person> aTeam = graphRepository.findAllByQuery("name", "A*");
|
||||
Long numberOfPeople = graphRepository.count();
|
||||
|
||||
Person mark = graphRepository.findByPropertyValue("name", "mark");
|
||||
|
||||
Iterable<Person> devs = graphRepository.findAllByProperyValue("occupation", "developer");
|
||||
|
||||
Iterable<Person> middleAgedPeople = graphRepository.findAllByRange("age", 20, 40);
|
||||
|
||||
Iterable<Person> aTeam = graphRepository.findAllByQuery("name", "A*");
|
||||
|
||||
Iterable<Person> davesFriends = graphRepository.findAllByTraversal(dave,
|
||||
Traversal.description().pruneAfterDepth(1)
|
||||
.relationships(KNOWS).filter(returnAllButStartNode()));
|
||||
]]></programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
Iterable<Person> davesFriends = graphRepository.findAllByTraversal(dave,
|
||||
Traversal.description().pruneAfterDepth(1)
|
||||
.relationships(KNOWS).filter(returnAllButStartNode()));
|
||||
]]></programlisting>
|
||||
</example>
|
||||
<section>
|
||||
<title>Composing repositories</title>
|
||||
<para>
|
||||
@@ -140,8 +171,9 @@ Iterable<Person> davesFriends = graphRepository.findAllByTraversal(dave,
|
||||
public interface PersonRepository extends GraphRepository<Person>, PersonRepositoryExtension {}
|
||||
|
||||
// alternatively select some of the required repositories individually
|
||||
public interface PersonRepository extends CRUDGraphRepository<Node,Person>, IndexQueryExecutor<Node,Person>,
|
||||
TraversalQueryExecutor<Node,Person>, PersonRepositoryExtension {}
|
||||
public interface PersonRepository extends CRUDGraphRepository<Node,Person>,
|
||||
IndexQueryExecutor<Node,Person>, TraversalQueryExecutor<Node,Person>,
|
||||
PersonRepositoryExtension {}
|
||||
|
||||
// provide a custom extension if needed
|
||||
public interface PersonRepositoryExtension {
|
||||
@@ -156,8 +188,10 @@ public class PersonRepositoryImpl implements PersonRepositoryExtension {
|
||||
}
|
||||
}
|
||||
|
||||
// configure the repositories, preferably via the datagraph:repositories namespace (graphDatabaseContext reference is optional)
|
||||
<datagraph:repositories base-package="org.springframework.data.graph.neo4j" graph-database-context-ref="graphDatabaseContext"/>
|
||||
// configure the repositories, preferably via the datagraph:repositories namespace
|
||||
// (graphDatabaseContext reference is optional)
|
||||
<datagraph:repositories base-package="org.springframework.data.graph.neo4j"
|
||||
graph-database-context-ref="graphDatabaseContext"/>
|
||||
|
||||
// have it injected
|
||||
@Autowired
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section id="reference:programming-model:typerepresentationstrategy">
|
||||
<title>Entity types stored</title>
|
||||
<title>Entity type representation</title>
|
||||
<para>
|
||||
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
|
||||
|
||||
@@ -2,86 +2,101 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="samples">
|
||||
<title>Sample code</title>
|
||||
<title>Sample code</title>
|
||||
|
||||
<section id="samples_introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
Spring Data Graph comes with a number of samples.
|
||||
The source code of the samples is found on
|
||||
<ulink url="http://github.com/SpringSource/spring-data-graph-examples">GitHub</ulink>.
|
||||
The different sample projects are introduced below.
|
||||
</para>
|
||||
</section>
|
||||
<section id="samples:introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
Spring Data Graph comes with a number of sample applications. The source code of the samples can be found on
|
||||
<ulink url="http://github.com/SpringSource/spring-data-graph-examples">Github</ulink>. The different sample
|
||||
projects are introduced below.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="samples_hello-worlds">
|
||||
<title>Hello Worlds sample</title>
|
||||
<para>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.</para>
|
||||
<para>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.</para>
|
||||
<para>Executing the application creates the following graph in the Graph Database:</para>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="helloworlds.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</section>
|
||||
<section id="samples:hello-worlds">
|
||||
<title>Hello Worlds sample application</title>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
Executing the application creates the following graph in the graph database:
|
||||
</para>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="helloworlds.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</section>
|
||||
|
||||
<section id="samples_imdb">
|
||||
<title>IMDB sample</title>
|
||||
<para>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.</para>
|
||||
<para>See the readme file for instruction on how to compile and run the application.</para>
|
||||
<para>An excerpt of the data stored in the Graph Database after executing the application:
|
||||
</para>
|
||||
<para>
|
||||
<screenshot>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="imdb.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</screenshot>
|
||||
</para>
|
||||
</section>
|
||||
<section id="samples:imdb">
|
||||
<title>IMDB sample application</title>
|
||||
<para>
|
||||
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
|
||||
<ulink url="http://en.wikipedia.org/wiki/Bacon_number">Bacon number</ulink> 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.
|
||||
</para>
|
||||
<para>
|
||||
See the readme file for instructions on how to compile and run the application.
|
||||
</para>
|
||||
<para>
|
||||
An excerpt of the data stored in the graph database after executing the application:
|
||||
</para>
|
||||
<para>
|
||||
<screenshot>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="imdb.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</screenshot>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="samples_myrestaurant-original">
|
||||
<title>MyRestaurant sample</title>
|
||||
<para>Simple, JPA based web application for managing users and restaurants, with the ability to add
|
||||
restaurants as favorites to a user.</para>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="restaurant.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</section>
|
||||
<section id="samples:myrestaurants-original">
|
||||
<title>MyRestaurants sample application</title>
|
||||
<para>
|
||||
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<xref linkend="samples:myrestaurants-social" />), and does therefore not use
|
||||
Spring Data Graph.
|
||||
</para>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="restaurant.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</section>
|
||||
|
||||
<section id="samples_myrestaurant-social">
|
||||
<title>MyRestaurant-Social sample</title>
|
||||
<para>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. </para>
|
||||
<para>An excerpt of the data stored in the Graph Database after executing the application:
|
||||
</para>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="restaurant-social.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="restaurant-social-graph.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</section>
|
||||
<section id="samples:myrestaurants-social">
|
||||
<title>MyRestaurant-Social sample application</title>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
Here's an excerpt of the data stored in the graph database after executing the application:
|
||||
</para>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="restaurant-social.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="restaurant-social-graph.png" contentwidth="15cm" scalefit="1"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user