Edited reference docs. Mainly around detached entities, but also other parts.

This commit is contained in:
David Montag
2011-04-11 15:20:13 -07:00
parent b7077e89f6
commit 5e19489053
8 changed files with 207 additions and 106 deletions

View File

@@ -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>

View File

@@ -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 &lt;- 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)

View File

@@ -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
@@ -105,7 +105,8 @@
<example>
<title>Using GraphRepositories</title>
<programlisting language="java"><![CDATA[
GraphRepository<Person> graphRepository = graphRepositoryFactory.createGraphRepository(Person.class);
GraphRepository<Person> graphRepository = graphRepositoryFactory
.createGraphRepository(Person.class);
Person michael = graphRepository.save(new Person("Michael", 36));
@@ -140,8 +141,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 +158,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

View File

@@ -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