DATAGRAPH-449 Updated setup and type representation strategy reference docs

This commit is contained in:
Nicki Watt
2014-03-16 03:19:08 +00:00
parent 7e676b2ac3
commit 91aee61d67
9 changed files with 779 additions and 56 deletions

View File

@@ -0,0 +1,96 @@
package org.springframework.data.neo4j.aspects.support.typerepresentation;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.neo4j.aspects.core.NodeBacked;
import org.springframework.data.neo4j.aspects.support.domain.*;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import java.util.Collection;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;
/**
* @author Nicki Watt
* @since 15.04.2014
*/
public class AliasOverrideTypeRepresentationStrategyTest {
protected GraphDatabaseService graphDatabaseService;
protected Neo4jTemplate neo4jTemplate;
protected ClassPathXmlApplicationContext applicationContext;
private void setupDefaultAppCtx() {
applicationContext = new ClassPathXmlApplicationContext(
"org/springframework/data/neo4j/aspects/support/LabelBasedIndexedPropertyEntityTests-context-basic.xml");
applicationContext.registerShutdownHook();
applicationContext.refresh();
graphDatabaseService = applicationContext.getBean(GraphDatabaseService.class);
neo4jTemplate = applicationContext.getBean(Neo4jTemplate.class);
}
private void setupOverrideAppCtx() {
applicationContext = new ClassPathXmlApplicationContext(
"org/springframework/data/neo4j/aspects/support/classNameAliasOverride-context.xml");
applicationContext.registerShutdownHook();
applicationContext.refresh();
graphDatabaseService = applicationContext.getBean(GraphDatabaseService.class);
neo4jTemplate = applicationContext.getBean(Neo4jTemplate.class);
}
@After
public void tearDown() {
if (applicationContext != null) applicationContext.stop();
}
@Test
public void testSimpleNameUsedForDefaultTRS() {
setupDefaultAppCtx();
String simpleLabelName = Thing.class.getSimpleName();
testExpectedNameAppliedForLabelTRS(simpleLabelName);
}
@Test
public void testFQDNNameUsedForOverridenTRS() {
setupOverrideAppCtx();
String fqdnLabelName = Thing.class.getName();
testExpectedNameAppliedForLabelTRS(fqdnLabelName);
}
public void testExpectedNameAppliedForLabelTRS(String expectedLabelName) {
// Given I create a Node Entity and it is saved into the graph
// And I verify this by ensuring the nodeId is present
Thing thing = createThing(graphDatabaseService);
Long savedNodeId = ((NodeBacked) thing).getNodeId();
assertNotNull("Node Id Should have been assigned as part of persist call ",savedNodeId);
// When I get all the labels from this Node
// Then I expect to find the two appropriate TRS based labels
try (Transaction tx = graphDatabaseService.beginTx()) {
Node retrievedNode = neo4jTemplate.getNode(savedNodeId);
Collection<Label> labels = IteratorUtil.asCollection(retrievedNode.getLabels());
assertThat( labels, hasItems(
DynamicLabel.label(expectedLabelName),
DynamicLabel.label("_" + expectedLabelName)));
}
}
private Thing createThing(GraphDatabaseService graphDB) {
Thing thing = null;
try (Transaction tx = graphDB.beginTx()) {
thing = new Thing();
thing.setName("theThing");
((NodeBacked)thing).persist();
tx.success();
}
return thing;
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config/>
<neo4j:config graphDatabaseService="graphDatabaseService" base-package="org.springframework.data.neo4j.aspects.support.domain"/>
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase" destroy-method="shutdown"/>
<!-- Use fully qualified name as type alias instead -->
<bean id="entityAlias" class="org.springframework.data.neo4j.support.mapping.ClassNameAlias" />
</beans>

View File

@@ -0,0 +1,51 @@
package org.springframework.data.neo4j.config;
import org.junit.After;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.neo4j.config.spring.BasicJavaConfig;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.repositories.PersonRepository;
import static org.junit.Assert.assertNotNull;
/**
* Create SDN using (non test based) Java Config - No XML in site!
*/
public class BasicJavaConfigTest {
AnnotationConfigApplicationContext appCtx;
@After
public void tearDown() {
if (appCtx != null) appCtx.stop();
}
@Test
public void verifyAppCtxStartsCorrectlyWithJavaConfig() {
startJavaBasedAppCtx();
}
private void startJavaBasedAppCtx() {
appCtx = new AnnotationConfigApplicationContext();
appCtx.register(BasicJavaConfig.class);
appCtx.registerShutdownHook();
appCtx.refresh();
}
@Test
public void testBasicRepositoryFunctionality() {
startJavaBasedAppCtx();
GraphDatabaseService graphDatabaseService = appCtx.getBean(GraphDatabaseService.class);
PersonRepository personRepository = appCtx.getBean(PersonRepository.class);
try (Transaction tx = graphDatabaseService.beginTx()) {
Person person = new Person("Howdy",50);
personRepository.save(person);
assertNotNull(person.getId());
tx.success();
}
}
}

View File

@@ -0,0 +1,23 @@
package org.springframework.data.neo4j.config.spring;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.test.TestGraphDatabaseFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
@Configuration
@EnableNeo4jRepositories(basePackages = "org.springframework.data.neo4j.repositories")
public class BasicJavaConfig extends Neo4jConfiguration {
public BasicJavaConfig() {
setBasePackage("org.springframework.data.neo4j.model");
}
@Bean
public GraphDatabaseService graphDatabaseService() {
return new TestGraphDatabaseFactory().newImpermanentDatabase();
}
}

View File

@@ -0,0 +1,417 @@
/**
* 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.neo4j.repository.query;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.metatest.TestImpermanentGraphDatabase;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.neo4j.test.TestGraphDatabaseFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.IndexBasedNodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
/**
* Tests for the various finder method based scenarios
* , specifically where the Index Type Representation Strategy is being used.
*
* @author Oliver Gierke & Nicki Watt
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class DerivedFinderMethodForIndexedBasedTRSViaJavaConfigTests extends AbstractDerivedFinderMethodTestBase {
private static final String DEFAULT_START_CLAUSE = "START `thing`=node:__types__(className=\"Thing\")";
@Configuration
@EnableNeo4jRepositories(basePackages = "org.springframework.data.neo4j.repository.query")
static class Config extends Neo4jConfiguration {
Config() {
// Equivalent of setting basePackage for XML based <neo4j:config base-package=".."/>
// (This will probably move into an/the @EnableNeo4jRepositories in the future)
setBasePackage("org.springframework.data.neo4j.model,org.springframework.data.neo4j.repository.query");
}
@Override
public TypeRepresentationStrategyFactory typeRepresentationStrategyFactory() {
return new TypeRepresentationStrategyFactory(
graphDatabase(),
TypeRepresentationStrategyFactory.Strategy.Indexed);
}
@Bean(name="graphDatabaseService", destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {
return new TestGraphDatabaseFactory().newImpermanentDatabase();
}
}
@Autowired
NodeTypeRepresentationStrategy strategy;
@Before
public void setup() {
super.setup();
assertThat("The tests in this class should be configured to use the Label " +
"based Type Representation Strategy, however it is not ... ",
strategy, instanceOf(IndexBasedNodeTypeRepresentationStrategy.class));
}
@Test
@Override
public void testQueryWithEntityGraphId() throws Exception {
// findByOwnerId
this.trsSpecificExpectedQuery =
"START `thing_owner`=node({0}) " +
"MATCH (`thing`)-[:`owner`]->(`thing_owner`) " +
"WHERE `thing`.__type__ IN ['Thing'] ";
super.testQueryWithEntityGraphId();
}
@Test
@Override
public void testIndexQueryWithTwoParams() throws Exception {
// "findByFirstNameAndLastName",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "firstName:foo AND lastName:bar" };
super.testIndexQueryWithTwoParams();
}
@Test
@Override
public void testIndexQueryWithOneParam() throws Exception {
// "findByFirstName",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`(`firstName`={0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "foo" };
super.testIndexQueryWithOneParam();
}
@Test
@Override
public void testSchemaIndexQueryWithOneParam() throws Exception {
/*
TODO - Determine exactly what correct query should be when
using a schema based index and a Legacy based TRS
*/
// "findByAlias",
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`alias` = {0} RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "foo" };
super.testSchemaIndexQueryWithOneParam();
}
@Test
@Override
public void testIndexQueryWithOneParamFullText() throws Exception {
// "findByDescription",
this.trsSpecificExpectedQuery =
"START `thing`=node:`search`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "description:foo" };
super.testIndexQueryWithOneParamFullText();
}
@Test
@Override
public void testIndexQueryWithOneParamFullTextAndOneParam() throws Exception {
// "findByDescriptionAndFirstName",
this.trsSpecificExpectedQuery =
"START `thing`=node:`search`({0}) WHERE `thing`.`firstName` = {1} RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "description:foo","bar" };
super.testIndexQueryWithOneParamFullTextAndOneParam();
}
@Test
@Override
public void testIndexQueryWithOneParamAndOneParamFullText() throws Exception {
// "findByFirstNameAndDescription",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`(`firstName`={0}) WHERE `thing`.`description` = {1} RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "foo","bar" };
super.testIndexQueryWithOneParamAndOneParamFullText();
}
@Test
@Override
public void testIndexQueryWithOneNonIndexedParam() throws Exception {
// "findByAge",
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`age` = {0} RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { 100 };
super.testIndexQueryWithOneNonIndexedParam();
}
@Test
@Override
public void testIndexQueryWithOneNonIndexedParamAndOneIndexedParam() throws Exception {
// "findByAgeAndFirstName",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`(`firstName`={1}) WHERE `thing`.`age` = {0} RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { 100, "foo" };
super.testIndexQueryWithOneNonIndexedParamAndOneIndexedParam();
}
@Test
@Override
public void testIndexQueryWithLikeIndexedParam() throws Exception {
// "findByFirstNameLike",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "firstName:foo" };
super.testIndexQueryWithLikeIndexedParam();
}
@Test
@Override
public void testIndexQueryWithLikeIndexedParamWithSpaces() throws Exception {
// "findByFirstNameLike",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "firstName:\"foo bar\"" };
super.testIndexQueryWithLikeIndexedParamWithSpaces();
}
@Test
@Override
public void testIndexQueryWithContainsIndexedParam() throws Exception {
// "findByFirstNameContains",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "firstName:*foo*" };
super.testIndexQueryWithContainsIndexedParam();
}
@Test
@Override
public void testIndexQueryWithStartsWithIndexedParam() throws Exception {
// "findByFirstNameStartsWith",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "firstName:foo*" };
super.testIndexQueryWithStartsWithIndexedParam();
}
@Test
@Override
public void testIndexQueryWithEndsWithIndexedParam() throws Exception {
// "findByFirstNameEndsWith",
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0}) RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "firstName:*foo" };
super.testIndexQueryWithEndsWithIndexedParam();
}
@Test(expected = RepositoryQueryException.class)
@Override
public void testFailIndexQueryWithStartsWithIndexedParamWithSpaces() throws Exception {
// findByFirstNameStartsWith
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`({0})" +
" RETURN `thing`";
super.testFailIndexQueryWithStartsWithIndexedParamWithSpaces();
}
@Test
@Override
public void testFindBySimpleStringParam() throws Exception {
// findByName
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` = {0}" +
" RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "foo" };
super.testFindBySimpleStringParam();
}
@Test
@Override
public void testFindBySimpleStringParamStartsWith() throws Exception {
// findByNameStartsWith
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` =~ {0}" +
" RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { "^foo.*" };
super.testFindBySimpleStringParamStartsWith();
}
@Test
@Override
public void testFindBySimpleStringParamEndsWith() throws Exception {
// findByNameEndsWith
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` =~ {0}" +
" RETURN `thing`";
this.trsSpecificExpectedParams = new Object[] { ".*foo$" };
super.testFindBySimpleStringParamEndsWith();
}
@Test
@Override
public void testFindBySimpleStringParamContains() throws Exception {
// findByNameContains
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` =~ {0}" +
" RETURN `thing`";
super.testFindBySimpleStringParamContains();
}
@Test
@Override
public void testFindBySimpleStringParamLike() throws Exception {
// findByNameLike
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` =~ {0}" +
" RETURN `thing`";
super.testFindBySimpleStringParamLike();
}
@Test
@Override
public void testFindBySimpleStringParamNotLike() throws Exception {
// findByNameNotLike
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE not( `thing`.`name` =~ {0} )" +
" RETURN `thing`";
super.testFindBySimpleStringParamNotLike();
}
@Test
@Override
public void testFindBySimpleStringParamRegexp() throws Exception {
// findByNameMatches
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` =~ {0}" +
" RETURN `thing`";
super.testFindBySimpleStringParamRegexp();
}
@Test
@Override
public void testFindBySimpleBooleanIsTrue() throws Exception {
// findByTaggedIsTrue
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`tagged` = true" +
" RETURN `thing`";
super.testFindBySimpleBooleanIsTrue();
}
@Test
@Override
public void testFindBySimpleBooleanIsFalse() throws Exception {
// findByTaggedIsFalse
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`tagged` = false" +
" RETURN `thing`";
super.testFindBySimpleBooleanIsFalse();
}
@Test
@Override
public void testFindBySimpleStringExists() throws Exception {
// findByNameExists
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE has(`thing`.`name` )" +
" RETURN `thing`";
super.testFindBySimpleStringExists();
}
@Test
@Override
public void testFindBySimpleStringInCollection() throws Exception {
// findByNameIn
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` in {0}" +
" RETURN `thing`";
super.testFindBySimpleStringInCollection();
}
@Test
@Override
public void testFindBySimpleStringInCollectionOfEnums() throws Exception {
// findByNameIn
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`name` in {0}" +
" RETURN `thing`";
super.testFindBySimpleStringInCollectionOfEnums();
}
@Test
@Override
public void testFindBySimpleStringNotInCollection() throws Exception {
// findByNameNotIn
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE not( `thing`.`name` in {0} )" +
" RETURN `thing`";
super.testFindBySimpleStringNotInCollection();
}
@Test
@Override
public void testFindBySimpleDateBefore() throws Exception {
// findByBornBefore
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`born` < {0} RETURN `thing`";
super.testFindBySimpleDateBefore();
}
@Test
@Override
public void testFindBySimpleDateAfter() throws Exception {
// findByBornAfter
this.trsSpecificExpectedQuery = DEFAULT_START_CLAUSE +
" WHERE `thing`.`born` > {0} RETURN `thing`";
super.testFindBySimpleDateAfter();
}
@Test
@Override
public void testFindByNumericIndexedField() throws Exception {
// findByNumber
this.trsSpecificExpectedQuery =
"START `thing`=node:`Thing`(`number`={0}) RETURN `thing`";
super.testFindByNumericIndexedField();
}
@Test
@Transactional
public void testMultipleIndexedFields() throws Exception {
super.testMultipleIndexedFields();
}
}

View File

@@ -9,7 +9,11 @@
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config/>
<neo4j:config storeDirectory="target/config-test"/>
<bean id="graphDbFactory" class="org.neo4j.graphdb.factory.GraphDatabaseFactory"/>
<bean id="graphDatabaseService" scope="singleton" destroy-method="shutdown" factory-bean="graphDbFactory" factory-method="newEmbeddedDatabase">
<constructor-arg value="target/config-test"/>
</bean>
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="config" class="org.springframework.data.neo4j.config.DataGraphNamespaceHandlerTests$Config"/>
<neo4j:repositories base-package="org.springframework.data.neo4j.model"/>

View File

@@ -9,8 +9,8 @@
<context:annotation-config/>
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory">
<constructor-arg index="0" ref="graphDatabase"/>
<constructor-arg index="1" value="Indexed"/>
<constructor-arg ref="graphDatabase"/>
<constructor-arg value="Indexed"/>
</bean>
<neo4j:config graphDatabaseService="graphDatabaseService" base-package="org.springframework.data.neo4j.model,org.springframework.data.neo4j.repository.query"/>

View File

@@ -4,21 +4,13 @@
<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
this type information hierarchy is saved in the graph database.
</para>
<para>
As the type information is also stored in labels, node/relationship-properties and/or indexes it might amount to a substantial
amount of data in the graph. It is possible to use an <code>@TypeAlias("name")</code> annotation on nodes and relationships to have a
short constant name for each type which is (unlike the default approach) renaming-refactoring-safe.
Other than in previous versions, Spring Data Neo4j uses the simple class name as default.
For using the fully qualified class name by default, register a <code>Neo4jMappingContext</code> bean configured with an
instance of <code>org.springframework.data.neo4j.support.mapping.ClassNameAlias</code>.
It is also possible to opt out of storing type information using the <code>NoopTypeRepresentationStrategies</code>.
node and relationship entities, type information is needed to perform certain repository operations (such as findAll()).
This means that some of the type information hierarchy needs to be saved in the graph database, and SDN
makes use of "Type Representation Strategies" in order to achieve this.
</para>
<para>
Implementations of <code>TypeRepresentationStrategy</code> take care of persisting this information during entity instance
creation. They also provide the repository methods that use this type information to perform their operations,
creation. They are also used by certain repository methods to perform their operations,
like <code>findAll</code> and <code>count</code>. The derived finderMethods also use the type information for graph global queries.
</para>
<para>
@@ -88,9 +80,71 @@
</listitem>
</itemizedlist>
</para>
<para>
In order to use a different Type Representation Strategy, simply register an alternative "typeRepresentationStrategyFactory"
spring bean specifying the strategy required. For example to use the legacy indexing strategy for nodes you could define
the following override bean.
<example>
<title>XML-based configuration</title>
<programlisting language="xml"><![CDATA[
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory">
<constructor-arg ref="graphDatabase"/>
<constructor-arg value="Indexed"/>
</bean>
]]></programlisting>
</example>
Whilst in Java Config this may look as follows:
<example>
<title>Java-based configuration</title>
<programlisting language="java"><![CDATA[
@Configuration
@EnableNeo4jRepositories(basePackages = "org.springframework.data.neo4j.ref.whatever.repositories")
static class Config extends Neo4jConfiguration {
Config() {
// Equivalent of setting basePackage for XML based <neo4j:config base-package=".."/>
// (This will probably move into an/the @EnableNeo4jRepositories in the future)
setBasePackage("org.springframework.data.neo4j.model,org.springframework.data.neo4j.repository.query");
}
@Override
public TypeRepresentationStrategyFactory typeRepresentationStrategyFactory() {
return new TypeRepresentationStrategyFactory(
graphDatabase(),
TypeRepresentationStrategyFactory.Strategy.Indexed);
}
...
}
]]></programlisting>
</example>
</para>
<para>
As some type information is also stored in labels, node/relationship-properties and/or indexes it might amount to a substantial
amount of data in the graph. It is possible to use an <code>@TypeAlias("name")</code> annotation on nodes and relationships to have a
short constant name for each type which is (unlike the default approach) renaming-refactoring-safe.
From 3.0 onwards, Spring Data Neo4j uses the simple class name as the default whilst previous versions used to default to the fully qualified name.
If you would like to use the fully qualified class name by default, you can
<itemizedlist>
<listitem>
<para>
Register a <code>Neo4jMappingContext</code> bean configured with an instance of <code>org.springframework.data.neo4j.support.mapping.ClassNameAlias</code>
</para>
</listitem>
<listitem>
<para>
Override the spring "entityAlias" bean with an instance of <code>org.springframework.data.neo4j.support.mapping.ClassNameAlias</code>. For example, using XML config this would look as follows:
<programlisting language="xml"><![CDATA[<bean id="entityAlias" class="org.springframework.data.neo4j.support.mapping.ClassNameAlias" />]]></programlisting>
</para>
</listitem>
</itemizedlist>
It is also possible to opt out of storing type information completely by using the <code>NoopTypeRepresentationStrategies</code>.
</para>
<para>
Spring Data Neo4j will by default autodetect which are the most suitable strategies for node and relationship
entities. For new data stores, it will always opt for the indexing strategies. If a data store was created
entities. For new data stores, it will always opt for the indexing strategies (Label based for nodes, and legacy
index based for relationships). If a data store was created
with the older<code>SubReferenceNodeTypeRepresentationStrategy</code>, then it will continue to use that
strategy for node entities. It will however in that case use the no-op strategy for relationship entities,
which means that the old data stores have no support for searching for relationship entities. The indexing

View File

@@ -17,7 +17,7 @@
<section>
<title>Dependencies for Spring Data Neo4j Simple Mapping</title>
<para>
For the simple POJO mapping it is enough to add the <code>org.springframework.data:spring-data-neo4j:2.1.0.RELEASE</code> dependency
For the simple POJO mapping it is enough to add the <code>org.springframework.data:spring-data-neo4j:3.0.0.RELEASE</code> dependency
to your project.
</para>
<example>
@@ -25,7 +25,7 @@
<programlisting language="xml"><![CDATA[<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>2.1.0.RELEASE</version>
<version>3.0.0.RELEASE</version>
</dependency>
]]></programlisting>
</example>
@@ -43,9 +43,9 @@
<programlisting language="java"><![CDATA[sourceCompatibility = 1.6
targetCompatibility = 1.6
springVersion = "3.1.0.RELEASE"
springDataNeo4jVersion = "2.1.0.RELEASE"
aspectjVersion = "1.6.12"
springVersion = "3.2.8.RELEASE"
springDataNeo4jVersion = "3.0.0.RELEASE"
aspectjVersion = "1.7.4"
apply from:'https://github.com/SpringSource/spring-data-neo4j/raw/master/build/
gradle/springdataneo4j.gradle'
@@ -126,13 +126,13 @@ repositories {
<programlisting language="xml"><![CDATA[<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
<version>2.1.0.RELEASE</version>
<version>3.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
<version>1.7.4</version>
</dependency>
]]></programlisting>
</example>
@@ -150,18 +150,18 @@ repositories {
<programlisting language="xml"><![CDATA[<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<version>1.4</version>
<dependencies>
<!-- NB: You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.6.12</version>
<version>1.7.4</version>
</dependency>
</dependencies>
<executions>
@@ -203,12 +203,42 @@ repositories {
<title>XML namespace</title>
<para>
The XML namespace can be used to configure Spring Data Neo4j. The <code>config</code> element
provides an XML-based configuration of Spring Data Neo4j in one line. It has three attributes.
<code>graphDatabaseService</code> points out the Neo4j instance to use. For convenience,
<code>storeDirectory</code> can be set instead of <code>graphDatabaseService</code> to
point to a directory where a new <code>EmbeddedGraphDatabase</code> will be created. For
cross-store configuration, the <code>entityManagerFactory</code> attribute needs to be
configured.
provides an XML-based configuration of Spring Data Neo4j in one line. It has four attributes.
<itemizedlist>
<listitem>
<para>
<code>base-package</code> points to a set of packages (provided as a comma separated String of names)
which SDN will scan for locate all of your domain entity classes (<code>@NodeEntity</code> and <code>@RelationshipEntity</code>).
<note>
<para>
Neo4j 2.0 introduced the requirement to separately manage schema and data transactions which altered some
options for SDN with regards be being able to automatically detect and register <code>@NodeEntity</code> and <code>@RelationshipEntity</code>s on the fly.
Several approaches were attempted to try and handle this automatically with SDN 3.0.X, none of which worked in a satisfactory manner.
This has resulted in the base-package becoming a mandatory field now with entity metadata handling becoming an
explicit step in the lifecycle.
</para>
</note>
</para>
</listitem>
<listitem>
<para>
<code>graphDatabaseService</code> points out the Neo4j instance to use.
</para>
</listitem>
<listitem>
<para>
<code>storeDirectory</code> is a convenient alternative (instead of <code>graphDatabaseService</code>)
to point to a directory where a new <code>EmbeddedGraphDatabase</code> will be created.
</para>
</listitem>
<listitem>
<para>
<code>entityManagerFactory</code> is only required for cross-store configuration.
</para>
</listitem>
</itemizedlist>
</para>
<example>
<title>XML configuration with store directory</title>
@@ -226,28 +256,24 @@ repositories {
http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
<context:annotation-config/>
<neo4j:config storeDirectory="target/config-test"/>
<neo4j:config
storeDirectory="target/config-test"
base-package="org.example.domain"/>
</beans>
]]></programlisting>
</example>
<example>
<title>XML configuration with GraphDatabaseService bean</title>
<title>XML configuration with basic GraphDatabaseService bean</title>
<programlisting language="xml"><![CDATA[<context:annotation-config/>
<bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
destroy-method="shutdown">
<constructor-arg index="0" value="target/config-test"/>
<!-- optionally pass in neo4j-config parameters to the graph database
<constructor-arg index="1">
<map>
<entry key="allow_store_upgrade" value="true"/>
</map>
</constructor-arg>
-->
<bean id="graphDbFactory" class="org.neo4j.graphdb.factory.GraphDatabaseFactory"/>
<bean id="graphDatabaseService" scope="singleton" destroy-method="shutdown"
factory-bean="graphDbFactory" factory-method="newEmbeddedDatabase">
<constructor-arg value="target/config-test"/>
</bean>
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<neo4j:config graphDatabaseService="graphDatabaseService" base-package="org.example.domain"/>
]]></programlisting>
</example>
<example>
@@ -289,7 +315,8 @@ repositories {
</bean>
<neo4j:config storeDirectory="target/config-test"
entityManagerFactory="entityManagerFactory"/>
entityManagerFactory="entityManagerFactory"
base-package="org.example.domain"/>
]]></programlisting>
</example>
</section>
@@ -336,22 +363,54 @@ repositories {
<code>Neo4jConfiguration</code> is registered with the context. This is either done
explicitly in the context configuration, or via classpath scanning for classes that
have the @Configuration annotation. The only thing that must be provided is the
<code>GraphDatabaseService</code>. The example below shows how to register the
<code>@Configuration Neo4jConfiguration</code> class, as well as Spring's
<code>ConfigurationClassPostProcessor</code> that transforms the
<code>@Configuration</code> class to bean definitions.
<code>GraphDatabaseService</code> and the <code>basePackage</code> must also be set.
The examples below show how this can be done.
<example>
<title>Java-based bean configuration</title>
<programlisting language="xml"><![CDATA[<![CDATA[<beans ...>
<title>Pure Java based bean configuration</title>
<programlisting language="java">
@Configuration
@EnableNeo4jRepositories(basePackages = "org.example.repositories")
public class BasicJavaConfig extends Neo4jConfiguration {
public BasicJavaConfig() {
setBasePackage("org.example.domain");
}
@Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabase("path/to/mydb");
}
// You can add your own beans here, and/or override some of the
// default config (such as Type Representation Strategies etc)
}
</programlisting>
</example>
<example>
<title>Java-based bean config initialiation via XML </title>
<para>
To register the default
<code>@Configuration Neo4jConfiguration</code> class, as well as Spring's
<code>ConfigurationClassPostProcessor</code> that transforms the
<code>@Configuration</code> class to bean definitions via XML.
</para>
<programlisting language="xml"><![CDATA[
<beans ...>
...
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.data.neo4j.config.Neo4jConfiguration"/>
<bean class="org.springframework.data.neo4j.config.Neo4jConfiguration">
<property name="basePackage" value="org.example.domain" />
</bean>
<bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/>
<bean id="graphDatabaseService" class="org.neo4j.kernel.EmbeddedGraphDatabase"
destroy-method="shutdown" scope="singleton">
<constructor-arg index="0" value="target/config-test"/>
<bean id="graphDbFactory" class="org.neo4j.graphdb.factory.GraphDatabaseFactory"/>
<bean id="graphDatabaseService" scope="singleton" destroy-method="shutdown"
factory-bean="graphDbFactory" factory-method="newEmbeddedDatabase">
<constructor-arg value="target/config-test"/>
</bean>
...
</beans>