Added Entity Persistence, Fetching, Lazy Loading

* fixed documenting test
* destructured GraphDatabaseService
* fixed tests
This commit is contained in:
Michael Hunger
2011-10-13 11:04:07 +02:00
parent 09ab48ebfa
commit 86b80a5a3f
26 changed files with 1527 additions and 821 deletions

View File

@@ -39,7 +39,8 @@ import org.springframework.data.neo4j.annotation.GraphTraversal;
import org.springframework.data.neo4j.aspects.core.NodeBacked;
import org.springframework.data.neo4j.aspects.core.RelationshipBacked;
import org.springframework.data.neo4j.fieldaccess.GraphBackedEntityIterableWrapper;
import org.springframework.data.neo4j.support.EntityStateHandler;
import org.springframework.data.neo4j.support.RelationshipResult;
import org.springframework.data.neo4j.support.node.NodeEntityStateFactory;
import org.springframework.data.neo4j.support.DoReturn;
import org.springframework.data.neo4j.core.EntityPath;
@@ -158,32 +159,19 @@ public privileged aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMix
}
public <T extends NodeBacked> T NodeBacked.projectTo(Class<T> targetType) {
return (T)Neo4jNodeBacking.aspectOf().graphDatabaseContext.projectTo( this, targetType);
return (T)graphDatabaseContext().projectTo( this, targetType);
}
public Relationship NodeBacked.relateTo(NodeBacked target, String type) {
return this.relateTo(target,type,false);
return this.relateTo(target, type, false);
}
public Relationship NodeBacked.relateTo(NodeBacked target, String type, boolean allowDuplicates) {
if (target==null) throw new IllegalArgumentException("Target entity is null");
if (type==null) throw new IllegalArgumentException("Relationshiptype is null");
if (!allowDuplicates) {
Relationship relationship=getRelationshipTo(target,type);
if (relationship!=null) return relationship;
}
return this.getPersistentState().createRelationshipTo(target.getPersistentState(), DynamicRelationshipType.withName(type));
final RelationshipResult result = entityStateHandler().relateTo(this, target, type, allowDuplicates);
return result.relationship;
}
public Relationship NodeBacked.getRelationshipTo(NodeBacked target, String type) {
Node node = this.getPersistentState();
Node targetNode = target.getPersistentState();
if (node==null || targetNode==null) return null;
Iterable<Relationship> relationships = node.getRelationships(DynamicRelationshipType.withName(type), org.neo4j.graphdb.Direction.OUTGOING);
for (Relationship relationship : relationships) {
if (relationship.getOtherNode(node).equals(targetNode)) return relationship;
}
return null;
return graphDatabaseContext().getRelationshipTo(this,target,null,type);
}
public Long NodeBacked.getNodeId() {
@@ -194,113 +182,70 @@ public privileged aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMix
public <T> Iterable<T> NodeBacked.findAllByTraversal(final Class<T> targetType, TraversalDescription traversalDescription) {
if (!hasPersistentState()) throw new IllegalStateException("No node attached to " + this);
final Traverser traverser = traversalDescription.traverse(this.getPersistentState());
if (Node.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser.nodes();
if (Relationship.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser.relationships();
if (Path.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser;
return (Iterable<T>)Neo4jNodeBacking.aspectOf().convertToGraphEntity(traverser,targetType);
}
private Iterable<?> convertToGraphEntity(Traverser traverser, final Class<?> targetType) {
final GraphDatabaseContext ctx = Neo4jNodeBacking.aspectOf().graphDatabaseContext;
if (NodeBacked.class.isAssignableFrom(targetType)) {
return GraphBackedEntityIterableWrapper.create(traverser.nodes(), (Class<? extends NodeBacked>) targetType, ctx);
}
if (RelationshipBacked.class.isAssignableFrom(targetType)) {
return GraphBackedEntityIterableWrapper.create(traverser.relationships(), (Class<? extends RelationshipBacked>) targetType, ctx);
}
throw new IllegalStateException("Can't determine valid type for traversal target "+targetType);
return graphDatabaseContext().convertResultsTo(traverser, targetType);
}
public <T> Iterable<T> NodeBacked.findAllByQuery(final String query, final Class<T> targetType, Map<String,Object> params) {
final CypherQueryExecutor executor = new CypherQueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext);
final CypherQueryExecutor executor = new CypherQueryExecutor(graphDatabaseContext());
return executor.query(query, targetType,params);
}
public Iterable<Map<String,Object>> NodeBacked.findAllByQuery(final String query,Map<String,Object> params) {
final CypherQueryExecutor executor = new CypherQueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext);
final CypherQueryExecutor executor = new CypherQueryExecutor(graphDatabaseContext());
return executor.queryForList(query,params);
}
public <T> T NodeBacked.findByQuery(final String query, final Class<T> targetType,Map<String,Object> params) {
final CypherQueryExecutor executor = new CypherQueryExecutor(Neo4jNodeBacking.aspectOf().graphDatabaseContext);
final CypherQueryExecutor executor = new CypherQueryExecutor(graphDatabaseContext());
return executor.queryForObject(query, targetType,params);
}
public <S extends NodeBacked, E extends NodeBacked> Iterable<EntityPath<S,E>> NodeBacked.findAllPathsByTraversal(TraversalDescription traversalDescription) {
if (!hasPersistentState()) throw new IllegalStateException("No node attached to " + this);
final Traverser traverser = traversalDescription.traverse(this.getPersistentState());
return new EntityPathPathIterableWrapper<S, E>(traverser, Neo4jNodeBacking.aspectOf().graphDatabaseContext);
return new EntityPathPathIterableWrapper<S, E>(traverser, graphDatabaseContext());
}
public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N target, Class<R> relationshipClass, String relationshipType) {
return this.relateTo(target,relationshipClass,relationshipType,false);
return graphDatabaseContext().relateTo(this, target, relationshipClass, relationshipType, false);
}
public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N target, Class<R> relationshipClass, String relationshipType, boolean allowDuplicates) {
if (target==null) throw new IllegalArgumentException("Target entity is null");
if (relationshipClass==null) throw new IllegalArgumentException("Relationship class is null");
if (relationshipType==null) throw new IllegalArgumentException("Relationshiptype is null");
Relationship rel = this.relateTo(target,relationshipType,allowDuplicates);
GraphDatabaseContext gdc = Neo4jNodeBacking.aspectOf().graphDatabaseContext;
gdc.postEntityCreation(rel, relationshipClass);
return (R) gdc.createEntityFromState(rel, relationshipClass);
return graphDatabaseContext().relateTo(this,target,relationshipClass, relationshipType,allowDuplicates);
}
public void NodeBacked.remove() {
Neo4jNodeBacking.aspectOf().graphDatabaseContext.removeNodeEntity(this);
graphDatabaseContext().removeNodeEntity(this);
}
public void NodeBacked.removeRelationshipTo(NodeBacked target, String relationshipType) {
if (target==null) throw new IllegalArgumentException("Target entity is null");
if (relationshipType==null) throw new IllegalArgumentException("Relationshiptype is null");
Node node=this.getPersistentState();
Node targetNode=target.getPersistentState();
if (node==null || targetNode==null) return;
for (Relationship rel : this.getPersistentState().getRelationships(DynamicRelationshipType.withName(relationshipType))) {
if (rel.getOtherNode(node).equals(targetNode)) {
rel.delete();
return;
}
}
graphDatabaseContext().removeRelationshipTo(this,target,relationshipType);
}
public <R extends RelationshipBacked> R NodeBacked.getRelationshipTo( NodeBacked target, Class<R> relationshipClass, String type) {
if (target ==null) throw new IllegalArgumentException("Target entity is null");
if (relationshipClass==null) throw new IllegalArgumentException("Relationship class is null");
if (type==null) throw new IllegalArgumentException("Relationshiptype is null");
Node node=this.getPersistentState();
Node targetNode= target.getPersistentState();
if (node==null || targetNode==null) return null;
for (Relationship rel : node.getRelationships(DynamicRelationshipType.withName(type))) {
if (rel.getOtherNode(node).equals(targetNode)) {
return (R)Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipClass);
}
}
return null;
return (R)graphDatabaseContext().getRelationshipTo(this,target,relationshipClass,type);
}
public static GraphDatabaseContext graphDatabaseContext() {
return Neo4jNodeBacking.aspectOf().graphDatabaseContext;
}
/**
* @param obj
* @return result of equals operation fo the underlying node, false if there is none
*/
public final boolean NodeBacked.equals(Object obj) {
if (obj == this) return true;
if (!hasPersistentState()) return false;
if (obj instanceof NodeBacked) {
return this.getPersistentState().equals(((NodeBacked) obj).getPersistentState());
}
return false;
public boolean NodeBacked.equals(Object obj) {
return entityStateHandler().equals(this, obj);
}
public static EntityStateHandler entityStateHandler() {
return graphDatabaseContext().getEntityStateHandler();
}
/**
* @return result of the hashCode of the underlying node (if any, otherwise identityHashCode)
*/
public final int NodeBacked.hashCode() {
if (!hasPersistentState()) return System.identityHashCode(this);
return getPersistentState().hashCode();
public int NodeBacked.hashCode() {
return entityStateHandler().hashCode(this);
}
/**

View File

@@ -73,7 +73,7 @@ public class IndexingRelationshipTypeRepresentationStrategyTest {
@Test
@Transactional
public void testPostEntityCreationOfRelationshipBacked() throws Exception {
Index<Relationship> typesIndex = graphDatabaseService.index().forRelationships(IndexingNodeTypeRepresentationStrategy.INDEX_NAME);
Index<Relationship> typesIndex = graphDatabaseService.index().forRelationships(IndexingRelationshipTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Relationship> linkHits = typesIndex.get(IndexingNodeTypeRepresentationStrategy.INDEX_KEY, link.getClass().getName());
Relationship rel = linkHits.getSingle();
assertEquals(rel(link), rel);

View File

@@ -93,7 +93,8 @@
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
</property>
<property name="mappingContext" ref="mappingContext"/>
<property name="nodeEntityConverter" ref="nodeEntityConverter"/>
<property name="relationshipEntityStateFactory" ref="relationshipEntityStateFactory"/>
<property name="nodeEntityStateFactory" ref="nodeEntityStateFactory"/>
</bean>

View File

@@ -1,325 +1,325 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.neo4j</groupId>
<artifactId>movies</artifactId>
<version>2.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Movies</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>1.5-SNAPSHOT</neo4j.version>
<spring-data-graph.version>2.0.0.BUILD-SNAPSHOT</spring-data-graph.version>
<aspectj.version>1.6.12.M1</aspectj.version>
<org.slf4j-version>1.6.1</org.slf4j-version>
</properties>
<repositories>
<repository>
<id>spring-maven-release</id>
<name>Spring Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>spring-maven-snapshot</id>
<name>Spring Maven Snapshot Repository</name>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>http://maven.springframework.org/snapshot</url>
</repository>
<repository>
<id>spring-maven-milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
<repository>
<id>neo4j-public-repository</id>
<url>http://m2.neo4j.org/</url>
<name>Publicly available Maven 2 repository for Neo4j</name>
</repository>
<repository>
<id>jboss-public-repo</id>
<url>http://repository.jboss.org/maven2/</url>
<name>JBoss public available repo</name>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-maven-release</id>
<name>Spring Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
<pluginRepository>
<id>spring-maven-milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
<version>${spring-data-graph.version}</version>
<exclusions>
<exclusion>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>opensymphony</groupId>
<artifactId>sitemesh</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
<!-- Neo4j Libraries -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-kernel</artifactId>
<version>${neo4j.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.1.2.v20100523</version>
<configuration>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
<!--scanIntervalSeconds>1</scanIntervalSeconds -->
</configuration>
</plugin>
<plugin>
<!-- Must use java 1.5 or higher for annotations -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useFile>true</useFile>
<includes>
<include>**/*Tests.java</include>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
<junitArtifactName>junit:junit</junitArtifactName>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
<aspectLibrary>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.neo4j</groupId>
<artifactId>movies</artifactId>
<version>2.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Movies</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.0.6.RELEASE</spring.version>
<slf4j.version>1.6.1</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<neo4j.version>1.5-SNAPSHOT</neo4j.version>
<spring-data-graph.version>2.0.0.BUILD-SNAPSHOT</spring-data-graph.version>
<aspectj.version>1.6.12.M1</aspectj.version>
<org.slf4j-version>1.6.1</org.slf4j-version>
</properties>
<repositories>
<repository>
<id>spring-maven-release</id>
<name>Spring Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>spring-maven-snapshot</id>
<name>Spring Maven Snapshot Repository</name>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>http://maven.springframework.org/snapshot</url>
</repository>
<repository>
<id>spring-maven-milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
<repository>
<id>neo4j-public-repository</id>
<url>http://m2.neo4j.org/</url>
<name>Publicly available Maven 2 repository for Neo4j</name>
</repository>
<repository>
<id>jboss-public-repo</id>
<url>http://repository.jboss.org/maven2/</url>
<name>JBoss public available repo</name>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-maven-release</id>
<name>Spring Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</pluginRepository>
<pluginRepository>
<id>spring-maven-milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-rest</artifactId>
<version>${spring-data-graph.version}</version>
<exclusions>
<exclusion>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>opensymphony</groupId>
<artifactId>sitemesh</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.2.GA</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
<!-- Neo4j Libraries -->
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-kernel</artifactId>
<version>${neo4j.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.1.2.v20100523</version>
<configuration>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
<!--scanIntervalSeconds>1</scanIntervalSeconds -->
</configuration>
</plugin>
<plugin>
<!-- Must use java 1.5 or higher for annotations -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useFile>true</useFile>
<includes>
<include>**/*Tests.java</include>
<include>**/*Test.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
<junitArtifactName>junit:junit</junitArtifactName>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
<aspectLibrary>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -17,6 +17,11 @@
<neo4j:config storeDirectory="data/graph.db"/>
<neo4j:repositories base-package="org.neo4j.cineasts.repository"/>
<!--neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
<constructor-arg value="http://localhost:7474"/>
</bean-->
<bean class="org.neo4j.cineasts.movieimport.MovieDbApiClient">
<constructor-arg value="926d2a79e82920b62f03b1cb57e532e6"/>
</bean>

View File

@@ -68,6 +68,7 @@ public class UserAccountRelationshipsTest extends AbstractTestWithUserAccount {
@Transactional
@Test
@Ignore
public void testAddRecommendation() {
UserAccount user = userAccountRepo.findUserAccount(userId);
Restaurant rest = restaurantRepository.findRestaurant(22L);

View File

@@ -123,6 +123,11 @@
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
@@ -145,6 +150,12 @@
<dependency>
<groupId>com.tinkerpop.blueprints</groupId>
<artifactId>blueprints-neo4j-graph</artifactId>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>

View File

@@ -87,7 +87,8 @@ public abstract class Neo4jConfiguration {
gdc.setGraphDatabaseService(getGraphDatabaseService());
gdc.setConversionService(conversionService());
gdc.setMappingContext(mappingContext());
gdc.setNodeEntityConverter(nodeEntityConverter());
gdc.setNodeEntityStateFactory(nodeEntityStateFactory());
gdc.setRelationshipEntityStateFactory(relationshipEntityStateFactory());
gdc.setEntityStateHandler(entityStateHandler());
gdc.setNodeTypeRepresentationStrategy(nodeTypeRepresentationStrategy());
gdc.setRelationshipTypeRepresentationStrategy(relationshipTypeRepresentationStrategy());

View File

@@ -93,7 +93,7 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory {
}
private Object executeQuery(Object entity, String queryString, Map<String, Object> params) {
return graphDatabaseContext.executeQuery(entity,queryString,params,property);
return graphDatabaseContext.executeQuery(queryString,params,property);
}
private Map<String, Object> createPlaceholderParams(Object entity) {

View File

@@ -70,7 +70,7 @@ public class Neo4jEntityConverterImpl<T,S extends PropertyContainer> implements
// 1) source -> type alias
// 2) type alias -> type
// 3) check for subtype matching / enforcement
final TypeInformation<R> requestedTypeInformation = ClassTypeInformation.from(requestedType);
final TypeInformation<R> requestedTypeInformation = requestedType == null ? null : ClassTypeInformation.from(requestedType);
final TypeInformation<? extends R> targetType = typeMapper.readType(source, requestedTypeInformation);
// retrieve meta-information about the type

View File

@@ -0,0 +1,260 @@
/**
* 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.mapping;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
import org.springframework.data.neo4j.support.EntityInstantiator;
import org.springframework.data.neo4j.support.EntityStateHandler;
import org.springframework.data.neo4j.support.ManagedEntity;
import org.springframework.data.neo4j.support.node.EntityStateFactory;
import org.springframework.data.neo4j.support.node.NodeEntityInstantiator;
import org.springframework.data.neo4j.support.relationship.RelationshipEntityInstantiator;
import java.util.HashMap;
import java.util.Map;
import static java.util.Arrays.asList;
/**
* @author mh
* @since 11.10.11
*/
public class Neo4jEntityPersister implements Neo4jEntityConverter<Object,Node> {
Neo4jEntityConverter<Object,Node> nodeConverter;
Neo4jEntityConverter<Object,Relationship> relationshipConverter;
private EntityStateHandler entityStateHandler;
private final Neo4jMappingContext mappingContext;
public Neo4jEntityPersister(GraphDatabaseService graphDatabaseService, Neo4jMappingContext mappingContext, ConversionService conversionService,
EntityStateFactory<Node> nodeEntityStateFactory, EntityStateFactory<Relationship> relationshipEntityStateFactory,
TypeRepresentationStrategy<Node> nodeTypeRepresentationStrategy, TypeRepresentationStrategy<Relationship> relationshipTypeRepresentationStrategy) {
this.mappingContext = mappingContext;
this.entityStateHandler = new EntityStateHandler(mappingContext, graphDatabaseService);
final EntityInstantiator<Node> nodeEntityInstantiator = new CachedInstantiator<Node>(new NodeEntityInstantiator(entityStateHandler));
TypeMapper<Node> nodeTypeMapper = new DefaultTypeMapper<Node>(new TRSTypeAliasAccessor<Node>(nodeTypeRepresentationStrategy), asList(new ClassValueTypeInformationMapper()));
SourceStateTransmitter<Node> nodeStateTransmitter = new SourceStateTransmitter<Node>(nodeEntityStateFactory);
final EntityInstantiator<Relationship> relationshipEntityInstantiator = new CachedInstantiator<Relationship>(new RelationshipEntityInstantiator(entityStateHandler));
TypeMapper<Relationship> relationShipTypeMapper = new DefaultTypeMapper<Relationship>(new TRSTypeAliasAccessor<Relationship>(relationshipTypeRepresentationStrategy), asList(new ClassValueTypeInformationMapper()));
SourceStateTransmitter<Relationship> relationshipStateTransmitter = new SourceStateTransmitter<Relationship>(relationshipEntityStateFactory);
Neo4jEntityFetchHandler fetchHandler=new Neo4jEntityFetchHandler(entityStateHandler, conversionService, relationshipStateTransmitter , nodeStateTransmitter);
this.nodeConverter = new CachedConverter<Node>(
new Neo4jEntityConverterImpl<Object,Node>(mappingContext, conversionService, nodeEntityInstantiator,
entityStateHandler, nodeTypeMapper, nodeStateTransmitter, fetchHandler));
this.relationshipConverter = new CachedConverter<Relationship>(
new Neo4jEntityConverterImpl<Object,Relationship>(mappingContext, conversionService,
relationshipEntityInstantiator, entityStateHandler, relationShipTypeMapper,
relationshipStateTransmitter, fetchHandler));
}
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state) {
return createEntityFromState(state,null);
}
static class StackedEntityCache {
private long depth;
private final Map<PropertyContainer,Object> objects =new HashMap<PropertyContainer, Object>();
private static ThreadLocal<StackedEntityCache> stackedEntityCache = new ThreadLocal<StackedEntityCache>() {
@Override
protected StackedEntityCache initialValue() {
return new StackedEntityCache();
}
};
public static void push() {
cache().depth++;
}
public static void pop() {
if (--cache().depth==0) {
stackedEntityCache.remove();
}
}
@SuppressWarnings("unchecked")
public static <T> T get(PropertyContainer state) {
return (T) cache().objects.get(state);
}
public static <T> T add(PropertyContainer state, T value) {
cache().objects.put(state, value);
return value;
}
private static StackedEntityCache cache() {
return stackedEntityCache.get();
}
public static boolean contains(PropertyContainer state) {
return cache().objects.containsKey(state);
}
}
static class CachedInstantiator<S extends PropertyContainer> implements EntityInstantiator<S> {
private final EntityInstantiator<S> delegate;
CachedInstantiator(EntityInstantiator<S> delegate) {
this.delegate = delegate;
}
@Override
public <T> T createEntityFromState(S state, Class<T> type) {
try {
if (state==null) throw new IllegalArgumentException("State must not be null");
StackedEntityCache.push();
if (StackedEntityCache.contains(state)) return StackedEntityCache.get(state);
return StackedEntityCache.add(state, delegate.createEntityFromState(state, type));
} finally {
StackedEntityCache.pop();
}
}
}
static class CachedConverter<S extends PropertyContainer> implements Neo4jEntityConverter<Object,S> {
private final Neo4jEntityConverter<Object,S> delegate;
CachedConverter(Neo4jEntityConverter<Object, S> delegate) {
this.delegate = delegate;
}
@Override
public MappingContext<? extends Neo4jPersistentEntity<?>, Neo4jPersistentProperty> getMappingContext() {
return delegate.getMappingContext();
}
@Override
public ConversionService getConversionService() {
return delegate.getConversionService();
}
@Override
public <R> R read(Class<R> type, S state) {
try {
if (state==null) throw new IllegalArgumentException("State must not be null");
StackedEntityCache.push();
if (StackedEntityCache.contains(state)) return StackedEntityCache.get(state);
return StackedEntityCache.add(state, delegate.read(type, state));
} finally {
StackedEntityCache.pop();
}
}
@Override
public void write(Object source, S sink) {
delegate.write(source,sink);
}
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type) {
if (state == null) throw new IllegalArgumentException("state has to be either a Node or Relationship");
if (isNode(state)) {
return nodeConverter.read(type, (Node) state);
}
if (isRelationship(state)) {
return relationshipConverter.read(type, (Relationship) state);
}
throw new IllegalArgumentException("state has to be either a Node or Relationship");
}
private boolean isRelationship(PropertyContainer state) {
return state instanceof Relationship;
}
private boolean isNode(PropertyContainer state) {
return state instanceof Node;
}
public <S extends PropertyContainer, T> T projectTo(Object entity, Class<T> targetType) {
S state = getPersistentState(entity);
return createEntityFromState(state, targetType);
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> S getPersistentState(Object entity) {
return entityStateHandler.getPersistentState(entity);
}
public Object persist(Object entity) {
final Class<?> type = entity.getClass();
if (isManaged(entity)) {
return ((ManagedEntity)entity).persist();
} else {
return persist(entity, type);
}
}
public boolean isManaged(Object entity) {
return entityStateHandler.isManaged(entity);
}
private Object persist(Object entity, Class<?> type) {
if (isNodeEntity(type)) {
final Node node = this.<Node>getPersistentState(entity);
this.nodeConverter.write(entity, node);
return entity; // TODO ?
}
if (isRelationshipEntity(type)) {
final Relationship relationship = this.<Relationship>getPersistentState(entity);
this.relationshipConverter.write(entity, relationship);
return entity; // TODO ?
}
throw new IllegalArgumentException("@NodeEntity or @RelationshipEntity annotation required on domain class"+type);
}
public boolean isNodeEntity(Class<?> targetType) {
return mappingContext.isNodeEntity(targetType);
}
public boolean isRelationshipEntity(Class targetType) {
return mappingContext.isRelationshipEntity(targetType);
}
public <S extends PropertyContainer> void setPersistentState(Object entity, S state) {
entityStateHandler.setPersistentState(entity,state);
}
@Override
public MappingContext<? extends Neo4jPersistentEntity<?>, Neo4jPersistentProperty> getMappingContext() {
return mappingContext;
}
@Override
public ConversionService getConversionService() {
return this.nodeConverter.getConversionService();
}
@Override
public <R> R read(Class<R> type, Node source) {
return createEntityFromState(source, type);
}
@Override
public void write(Object source, Node sink) {
nodeConverter.write(source,sink);
}
}

View File

@@ -24,7 +24,6 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.util.TypeInformation;
import scala.annotation.target.field;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;

View File

@@ -0,0 +1,82 @@
/**
* 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.support;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.IndexManager;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
/**
* @author mh
* @since 12.10.11
*/
public class EntityRemover {
private EntityStateHandler entityStateHandler;
private TypeRepresentationStrategy<Node> nodeTypeRepresentationStrategy;
private TypeRepresentationStrategy<Relationship> relationshipTypeRepresentationStrategy;
private IndexManager indexManager;
public EntityRemover(EntityStateHandler entityStateHandler, TypeRepresentationStrategy<Node> nodeTypeRepresentationStrategy, TypeRepresentationStrategy<Relationship> relationshipTypeRepresentationStrategy, IndexManager indexManager) {
this.entityStateHandler = entityStateHandler;
this.nodeTypeRepresentationStrategy = nodeTypeRepresentationStrategy;
this.relationshipTypeRepresentationStrategy = relationshipTypeRepresentationStrategy;
this.indexManager = indexManager;
}
public void removeNodeEntity(Object entity) {
Node node = entityStateHandler.getPersistentState(entity, Node.class);
if (node == null) return;
nodeTypeRepresentationStrategy.preEntityRemoval(node);
for (Relationship relationship : node.getRelationships()) {
removeRelationship(relationship);
}
removeFromIndexes(node);
node.delete();
}
public void removeRelationshipEntity(Object entity) {
Relationship relationship = entityStateHandler.getPersistentState(entity, Relationship.class);
if (relationship == null) return;
removeRelationship(relationship);
}
private void removeRelationship(Relationship relationship) {
relationshipTypeRepresentationStrategy.preEntityRemoval(relationship);
removeFromIndexes(relationship);
relationship.delete();
}
private void removeFromIndexes(Node node) {
for (String indexName : indexManager.nodeIndexNames()) {
indexManager.forNodes(indexName).remove(node);
}
}
private void removeFromIndexes(Relationship relationship) {
for (String indexName : indexManager.relationshipIndexNames()) {
indexManager.forRelationships(indexName).remove(relationship);
}
}
public void removeRelationshipTo(Object start, Object target, String type) {
final RelationshipResult result = entityStateHandler.removeRelationshipTo(start, target, type);
if (result!=null && result.type == RelationshipResult.Type.DELETED) {
relationshipTypeRepresentationStrategy.preEntityRemoval(result.relationship);
}
}
}

View File

@@ -56,6 +56,23 @@ public class EntityStateHandler {
return ManagedEntity.class.isAssignableFrom(type);
}
public boolean hasPersistentState(Object entity) {
if (entity instanceof PropertyContainer) return true;
if (isManaged(entity)) return ((ManagedEntity) entity).getPersistentState() != null;
return getId(entity) != null;
}
private Number getId(Object entity) {
final Class<?> type = entity.getClass();
final Neo4jPersistentEntityImpl<?> persistentEntity = mappingContext.getPersistentEntity(type);
final Object id = persistentEntity.getPersistentId(entity);
if (id == null) return null; // todo create new node?
if (id instanceof Number) {
return ((Number) id);
}
throw new IllegalArgumentException("The id of " + persistentEntity.getEntityName() + " " + persistentEntity.getIdProperty() + " is not a number");
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> S getPersistentState(Object entity) {
if (entity instanceof PropertyContainer) {
@@ -64,17 +81,14 @@ public class EntityStateHandler {
if (isManaged(entity)) {
return ((ManagedEntity<S, Object>) entity).getPersistentState();
}
final Class<?> type = entity.getClass();
final Neo4jPersistentEntityImpl<?> persistentEntity = mappingContext.getPersistentEntity(type);
final Object id = persistentEntity.getPersistentId(entity);
if (id == null) return null; // todo create new node?
if (!(id instanceof Number))
throw new IllegalArgumentException("The id of " + persistentEntity.getEntityName() + " " + persistentEntity.getIdProperty() + " is not a number");
long graphId = ((Number) id).longValue();
if (isNodeEntity(type)) {
final Number id = getId(entity);
if (id == null) return null;
long graphId = id.longValue();
final Neo4jPersistentEntityImpl<?> persistentEntity = mappingContext.getPersistentEntity(entity.getClass());
if (persistentEntity.isNodeEntity()) {
return (S) service.getNodeById(graphId);
}
if (isRelationshipEntity(type)) {
if (persistentEntity.isRelationshipEntity()) {
return (S) service.getRelationshipById(graphId);
}
throw new IllegalArgumentException("The entity " + persistentEntity.getEntityName() + " has to be either annotated with @NodeEntity or @RelationshipEntity");
@@ -115,4 +129,81 @@ public class EntityStateHandler {
}
return (S) startNode.createRelationshipTo(endNode, DynamicRelationshipType.withName(relType.toString()));
}
public RelationshipResult relateTo(Object source, Object target, String type) {
return this.relateTo(source, target, type, false);
}
// todo gdc.postEntityCreation(rel), return createEntityFromState(rel)
public RelationshipResult relateTo(Object source, Object target, String type, boolean allowDuplicates) {
if (source == null) throw new IllegalArgumentException("Source entity is null");
if (target == null) throw new IllegalArgumentException("Target entity is null");
if (type == null) throw new IllegalArgumentException("Relationshiptype is null");
if (!allowDuplicates) {
Relationship relationship = getRelationshipTo(source, target, type);
if (relationship != null) return new RelationshipResult(relationship, RelationshipResult.Type.EXISTING);
}
final Node sourceNode = getPersistentState(source, Node.class);
final Node targetNode = getPersistentState(target, Node.class);
if (sourceNode == null) throw new IllegalArgumentException("Source Node is null");
if (targetNode == null) throw new IllegalArgumentException("Target Node is null");
final Relationship relationship = sourceNode.createRelationshipTo(targetNode, DynamicRelationshipType.withName(type));
return new RelationshipResult(relationship, RelationshipResult.Type.NEW);
}
@SuppressWarnings("unchecked")
public <R extends PropertyContainer> R getPersistentState(Object entity, Class<R> type) {
final PropertyContainer state = getPersistentState(entity);
if (type==null || type.isInstance(state)) return (R)state;
throw new IllegalArgumentException("Target state is not the requested "+type+" but "+state);
}
public RelationshipResult removeRelationshipTo(Object source, Object target, String relationshipType) {
final Relationship relationship = getRelationshipTo(source, target, relationshipType);
if (relationship!=null) {
relationship.delete();
return new RelationshipResult(relationship, RelationshipResult.Type.DELETED);
}
return null;
}
public Relationship getRelationshipTo(Object source, Object target, String type) {
if (source == null) throw new IllegalArgumentException("Source entity is null");
if (target == null) throw new IllegalArgumentException("Target entity is null");
if (type == null) throw new IllegalArgumentException("Relationshiptype is null");
Node node = getPersistentState(source);
Node targetNode = getPersistentState(target);
if (node == null || targetNode == null) return null;
Iterable<Relationship> relationships = node.getRelationships(DynamicRelationshipType.withName(type),Direction.OUTGOING);
for (Relationship relationship : relationships) {
if (relationship.getOtherNode(node).equals(targetNode)) return relationship;
}
return null;
}
public final boolean equals(Object first, Object second) {
if (second == first) return true;
if (second == null) return false;
final PropertyContainer firstState = getPersistentState(first);
if (firstState == null) return false;
final PropertyContainer secondState = getPersistentState(second);
if (secondState == null) return false;
return firstState.equals(secondState);
}
/**
* @return result of the hashCode of the underlying node (if any, otherwise identityHashCode)
*/
public final int hashCode(Object entity) {
if (entity == null) throw new IllegalArgumentException("Entity is null");
final PropertyContainer state = getPersistentState(entity);
if (state == null) return System.identityHashCode(entity);
return state.hashCode();
}
}

View File

@@ -21,22 +21,22 @@ import org.apache.commons.logging.LogFactory;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.traversal.*;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.helpers.collection.ClosableIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
import org.neo4j.kernel.AbstractGraphDatabase;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.convert.EntityConverter;
import org.springframework.data.neo4j.annotation.Indexed;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.core.*;
import org.springframework.data.neo4j.mapping.Neo4jEntityConverter;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
import org.springframework.data.neo4j.fieldaccess.GraphBackedEntityIterableWrapper;
import org.springframework.data.neo4j.mapping.Neo4jEntityPersister;
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.mapping.Neo4jNodeConverter;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.node.EntityStateFactory;
import org.springframework.data.neo4j.support.query.CypherQueryExecutor;
import org.springframework.data.util.TypeInformation;
@@ -50,7 +50,7 @@ import java.util.Map;
/**
* Mediator class for the graph related services like the {@link GraphDatabaseService}, the used
* {@link org.springframework.data.neo4j.core.TypeRepresentationStrategy}, entity instantiators for nodes and relationships as well as a spring conversion service.
*
* <p/>
* It delegates the appropriate methods to those services. The services are not intended to be accessible from outside.
*
* @author Michael Hunger
@@ -64,7 +64,6 @@ public class GraphDatabaseContext {
private GraphDatabaseService graphDatabaseService;
private ConversionService conversionService;
private Neo4jEntityConverter<Object,Node> converter;
private Validator validator;
private TypeRepresentationStrategy<Node> nodeTypeRepresentationStrategy;
@@ -73,31 +72,63 @@ public class GraphDatabaseContext {
private Neo4jMappingContext mappingContext;
private CypherQueryExecutor cypherQueryExecutor;
private EntityStateHandler entityStateHandler;
private Neo4jEntityPersister entityPersister;
private EntityStateFactory<Node> nodeEntityStateFactory;
private EntityStateFactory<Relationship> relationshipEntityStateFactory;
private EntityRemover entityRemover;
private TypeRepresentationStrategies typeRepresentationStrategies;
static class IndexProvider {
private IndexManager indexManager;
private Neo4jMappingContext mappingContext;
IndexProvider(IndexManager indexManager, Neo4jMappingContext mappingContext) {
this.indexManager = indexManager;
this.mappingContext = mappingContext;
}
public <S extends PropertyContainer, T> Index<S> getIndex(Class<T> type) {
return getIndex(type, null);
}
public <S extends PropertyContainer, T> Index<S> getIndex(Class<T> type, String indexName) {
return getIndex(type, indexName, null);
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer, T> Index<S> getIndex(Class<T> type, String indexName, Boolean fullText) {
if (indexName == null) indexName = Indexed.Name.get(type);
if (fullText == null) {
if (mappingContext.isNodeEntity(type)) return (Index<S>) getIndexManager().forNodes(indexName);
if (mappingContext.isRelationshipEntity(type))
return (Index<S>) getIndexManager().forRelationships(indexName);
throw new IllegalArgumentException("Wrong index type supplied: " + type + " expected Node- or Relationship-Entity");
}
Map<String, String> config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG;
if (mappingContext.isNodeEntity(type)) return (Index<S>) getIndexManager().forNodes(indexName, config);
if (mappingContext.isRelationshipEntity(type))
return (Index<S>) getIndexManager().forRelationships(indexName, config);
throw new IllegalArgumentException("Wrong index type supplied: " + type + " expected Node- or Relationship-Entity");
}
public IndexManager getIndexManager() {
return indexManager;
}
}
IndexProvider indexProvider;
public <S extends PropertyContainer, T> Index<S> getIndex(Class<T> type) {
return getIndex(type, null);
return indexProvider.getIndex(type, null);
}
public <S extends PropertyContainer, T> Index<S> getIndex(Class<T> type, String indexName) {
return getIndex(type, indexName, null);
return indexProvider.getIndex(type, indexName, null);
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer, T> Index<S> getIndex(Class<T> type, String indexName, Boolean fullText) {
if (indexName==null) indexName = Indexed.Name.get(type);
if (fullText == null){
if (mappingContext.isNodeEntity(type)) return (Index<S>) getIndexManager().forNodes(indexName);
if (mappingContext.isRelationshipEntity(type)) return (Index<S>) getIndexManager().forRelationships(indexName);
throw new IllegalArgumentException("Wrong index type supplied: " + type+" expected Node- or Relationship-Entity");
}
Map<String, String> config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG;
if (mappingContext.isNodeEntity(type)) return (Index<S>) getIndexManager().forNodes(indexName, config);
if (mappingContext.isRelationshipEntity(type)) return (Index<S>) getIndexManager().forRelationships(indexName, config);
throw new IllegalArgumentException("Wrong index type supplied: " + type+" expected Node- or Relationship-Entity");
return indexProvider.getIndex(type,indexName,fullText);
}
/**
@@ -105,7 +136,7 @@ public class GraphDatabaseContext {
*/
public boolean transactionIsRunning() {
if (!(graphDatabaseService instanceof AbstractGraphDatabase)) {
return true; // assume always running tx (e.g. for REST or other remotes)
return true; // assume always running tx (e.g. for REST or other remotes)
}
try {
final TransactionManager txManager = ((AbstractGraphDatabase) graphDatabaseService).getConfig().getTxModule().getTxManager();
@@ -116,152 +147,83 @@ public class GraphDatabaseContext {
}
}
public <T> ClosableIterable<T> findAll(final Class<T> entityClass) {
return getTypeRepresentationStrategy(entityClass).findAll(entityClass);
}
public <T> long count(final Class<T> entityClass) {
return getTypeRepresentationStrategy(entityClass).count(entityClass);
}
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state) {
return getTypeRepresentationStrategy(state).createEntity(state);
}
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type) {
if (state==null) throw new IllegalArgumentException("state has to be either a Node or Relationship, not null");
return getTypeRepresentationStrategy(state, type).createEntity(state, type);
}
public <S extends PropertyContainer, T> T projectTo(Object entity, Class<T> targetType) {
S state = getPersistentState(entity);
return getTypeRepresentationStrategy(state, targetType).projectEntity(state, targetType);
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> S getPersistentState(Object entity) {
return entityStateHandler.getPersistentState(entity);
}
// todo depending on type of mapping
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> void setPersistentState(Object entity, S state) {
entityStateHandler.setPersistentState(entity, state);
}
public <S extends PropertyContainer, T> void postEntityCreation(S node, Class<T> entityClass) {
getTypeRepresentationStrategy(node, entityClass).postEntityCreation(node, entityClass);
}
@SuppressWarnings("unchecked")
public <T> Iterable<T> findAllByTraversal(Object entity, Class<?> targetType, TraversalDescription traversalDescription) {
final PropertyContainer state = entityStateHandler.getPersistentState(entity);
public <T> Iterable<T> findAllByTraversal(Object entity, Class<?> targetType, TraversalDescription traversalDescription) {
final PropertyContainer state = entityPersister.getPersistentState(entity);
if (state == null) throw new IllegalStateException("No node attached to " + this);
final Traverser traverser = traversalDescription.traverse((Node) state);
if (Node.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser.nodes();
if (Relationship.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser.relationships();
if (Path.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser;
return (Iterable<T>)convertToGraphEntity(traverser, targetType);
return (Iterable<T>) convertToGraphEntity(traverser, targetType);
}
private Iterable<?> convertToGraphEntity(Traverser traverser, final Class<?> targetType) {
if (isNodeEntity(targetType)) {
return new IterableWrapper<Object,Node>(traverser.nodes()) {
return new IterableWrapper<Object, Node>(traverser.nodes()) {
@Override
protected Object underlyingObjectToObject(Node node) {
return createEntityFromState(node,targetType);
return createEntityFromState(node, targetType);
}
};
}
if (isRelationshipEntity(targetType)) {
return new IterableWrapper<Object,Relationship>(traverser.relationships()) {
return new IterableWrapper<Object, Relationship>(traverser.relationships()) {
@Override
protected Object underlyingObjectToObject(Relationship relationship) {
return createEntityFromState(relationship, targetType);
}
};
}
throw new IllegalStateException("Can't determine valid type for traversal target "+targetType);
throw new IllegalStateException("Can't determine valid type for traversal target " + targetType);
}
public <T> ClosableIterable<T> findAll(final Class<T> entityClass) {
return typeRepresentationStrategies.findAll(entityClass);
}
public <T> long count(final Class<T> entityClass) {
return typeRepresentationStrategies.count(entityClass);
}
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state) {
return entityPersister.createEntityFromStoredType(state);
}
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type) {
return entityPersister.createEntityFromState(state, type);
}
public <S extends PropertyContainer, T> T projectTo(Object entity, Class<T> targetType) {
return entityPersister.projectTo(entity, targetType);
}
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> S getPersistentState(Object entity) {
return entityPersister.getPersistentState(entity);
}
// todo depending on type of mapping
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> void setPersistentState(Object entity, S state) {
entityPersister.setPersistentState(entity, state);
}
@Deprecated() // TODO remove
public <S extends PropertyContainer, T> void postEntityCreation(S node, Class<T> entityClass) {
typeRepresentationStrategies.postEntityCreation(node, entityClass);
}
public void removeNodeEntity(Object entity) {
Node node = getPersistentState(entity);
if (node == null) return;
nodeTypeRepresentationStrategy.preEntityRemoval(node);
for (Relationship relationship : node.getRelationships()) {
removeRelationship(relationship);
}
removeFromIndexes(node);
node.delete();
entityRemover.removeNodeEntity(entity);
}
public void removeRelationshipEntity(Object entity) {
Relationship relationship = getPersistentState(entity);
if (relationship == null) return;
removeRelationship(relationship);
entityRemover.removeRelationshipEntity(entity);
}
private void removeRelationship(Relationship relationship) {
relationshipTypeRepresentationStrategy.preEntityRemoval(relationship);
removeFromIndexes(relationship);
relationship.delete();
}
private void removeFromIndexes(Node node) {
IndexManager indexManager = getIndexManager();
for (String indexName : indexManager.nodeIndexNames()) {
indexManager.forNodes(indexName).remove(node);
}
}
private void removeFromIndexes(Relationship relationship) {
IndexManager indexManager = getIndexManager();
for (String indexName : indexManager.relationshipIndexNames()) {
indexManager.forRelationships(indexName).remove(relationship);
}
}
private IndexManager getIndexManager() {
return graphDatabaseService.index();
}
@SuppressWarnings("unchecked")
private <T> TypeRepresentationStrategy<?> getTypeRepresentationStrategy(Class<T> type) {
if (mappingContext.isNodeEntity(type)) {
return (TypeRepresentationStrategy<?>)nodeTypeRepresentationStrategy;
} else if (mappingContext.isRelationshipEntity(type)) {
return (TypeRepresentationStrategy<?>)relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
}
@SuppressWarnings("unchecked")
private <S extends PropertyContainer, T> TypeRepresentationStrategy<S> getTypeRepresentationStrategy(S state, Class<T> type) {
if (state instanceof Node && mappingContext.isNodeEntity(type)) {
return (TypeRepresentationStrategy<S>) nodeTypeRepresentationStrategy;
} else if (state instanceof Relationship && mappingContext.isRelationshipEntity(type)) {
return (TypeRepresentationStrategy<S>) relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type "+type+" is not NodeBacked nor RelationshipBacked.");
}
@SuppressWarnings("unchecked")
private <S extends PropertyContainer, T> TypeRepresentationStrategy<S> getTypeRepresentationStrategy(S state) {
if (state instanceof Node) {
return (TypeRepresentationStrategy<S>) nodeTypeRepresentationStrategy;
} else if (state instanceof Relationship) {
return (TypeRepresentationStrategy<S>) relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
}
/**
* Delegates to {@link GraphDatabaseService}
*/
@@ -279,7 +241,7 @@ public class GraphDatabaseContext {
/**
* Delegates to {@link GraphDatabaseService}
*/
public Node getReferenceNode() {
public Node getReferenceNode() {
return graphDatabaseService.getReferenceNode();
}
@@ -304,17 +266,97 @@ public class GraphDatabaseContext {
return graphDatabaseService.getRelationshipById(id);
}
public GraphDatabaseService getGraphDatabaseService() {
return graphDatabaseService;
}
public void setGraphDatabaseService(GraphDatabaseService graphDatabaseService) {
this.graphDatabaseService = graphDatabaseService;
}
@PostConstruct
public void createCypherExecutor() {
public void postConstruct() {
this.typeRepresentationStrategies = new TypeRepresentationStrategies(mappingContext, nodeTypeRepresentationStrategy, relationshipTypeRepresentationStrategy);
this.cypherQueryExecutor = new CypherQueryExecutor(this);
this.entityPersister = new Neo4jEntityPersister(graphDatabaseService, mappingContext, conversionService,
nodeEntityStateFactory, relationshipEntityStateFactory, nodeTypeRepresentationStrategy, relationshipTypeRepresentationStrategy);
this.entityRemover = new EntityRemover(entityStateHandler, nodeTypeRepresentationStrategy, relationshipTypeRepresentationStrategy, graphDatabaseService.index());
this.indexProvider = new IndexProvider(graphDatabaseService.index(), mappingContext);
}
public boolean isNodeEntity(Class<?> targetType) {
return targetType.isAnnotationPresent(NodeEntity.class);
//return mappingContext.isNodeEntity(targetType);
}
public boolean isRelationshipEntity(Class targetType) {
return targetType.isAnnotationPresent(RelationshipEntity.class);
// return mappingContext.isRelationshipEntity(targetType);
}
public Object save(Object entity) {
return entityPersister.persist(entity);
}
public boolean isManaged(Object entity) {
return entityStateHandler.isManaged(entity);
}
public Object executeQuery(String queryString, Map<String, Object> params, Neo4jPersistentProperty property) {
final TypeInformation<?> typeInformation = property.getTypeInformation();
final TypeInformation<?> actualType = typeInformation.getActualType();
final Class<?> targetType = actualType.getType();
if (actualType.isMap()) {
return cypherQueryExecutor.queryForList(queryString, params);
}
if (typeInformation.isCollectionLike()) {
return cypherQueryExecutor.query(queryString, targetType, params);
}
return cypherQueryExecutor.queryForObject(queryString, targetType, params);
}
@SuppressWarnings("unchecked")
public <T> Iterable<T> convertResultsTo(Traverser traverser, Class<T> targetType) {
if (Node.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser.nodes();
if (Relationship.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser.relationships();
if (Path.class.isAssignableFrom(targetType)) return (Iterable<T>) traverser;
if (isNodeEntity(targetType)) {
return GraphBackedEntityIterableWrapper.create(traverser.nodes(), targetType, this);
}
if (isRelationshipEntity(targetType)) {
return GraphBackedEntityIterableWrapper.create(traverser.relationships(), targetType, this);
}
throw new IllegalStateException("Can't determine valid type for traversal target " + targetType);
}
public <R> R getRelationshipTo(Object source, Object target, Class<R> relationshipClass, String type) {
final Relationship relationship = entityStateHandler.getRelationshipTo(source, target, type);
if (relationship == null) return null;
return entityPersister.createEntityFromState(relationship, relationshipClass);
}
public void removeRelationshipTo(Object start, Object target, String type) {
entityRemover.removeRelationshipTo(start, target, type);
}
public <R> R relateTo(Object source, Object target, Class<R> relationshipClass, String relationshipType, boolean allowDuplicates) {
final RelationshipResult result = entityStateHandler.relateTo(source, target, relationshipType, allowDuplicates);
if (result.type == RelationshipResult.Type.NEW) {
// TODO
postEntityCreation(result.relationship, relationshipClass);
}
return createEntityFromState(result.relationship, relationshipClass);
}
public void setEntityStateHandler(EntityStateHandler entityStateHandler) {
this.entityStateHandler = entityStateHandler;
}
public void setNodeEntityStateFactory(EntityStateFactory<Node> nodeEntityStateFactory) {
this.nodeEntityStateFactory = nodeEntityStateFactory;
}
public void setRelationshipEntityStateFactory(EntityStateFactory<Relationship> relationshipEntityStateFactory) {
this.relationshipEntityStateFactory = relationshipEntityStateFactory;
}
public EntityStateHandler getEntityStateHandler() {
return entityStateHandler;
}
public TypeRepresentationStrategy<Node> getNodeTypeRepresentationStrategy() {
@@ -334,12 +376,12 @@ public class GraphDatabaseContext {
}
public ConversionService getConversionService() {
return conversionService;
}
return conversionService;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Validator getValidator() {
return validator;
@@ -353,49 +395,13 @@ public class GraphDatabaseContext {
this.mappingContext = mappingContext;
}
public boolean isNodeEntity(Class<?> targetType) {
return targetType.isAnnotationPresent(NodeEntity.class);
//return mappingContext.isNodeEntity(targetType);
public GraphDatabaseService getGraphDatabaseService() {
return graphDatabaseService;
}
public boolean isRelationshipEntity(Class targetType) {
return targetType.isAnnotationPresent(RelationshipEntity.class);
// return mappingContext.isRelationshipEntity(targetType);
}
public Object save(Object entity) {
if (isManaged(entity)) {
return ((ManagedEntity)entity).persist();
} else {
final Node node = this.<Node>getPersistentState(entity);
this.converter.write(entity, node);
return entity; // TODO ?
}
}
public boolean isManaged(Object entity) {
return entityStateHandler.isManaged(entity);
}
public void setNodeEntityConverter(Neo4jEntityConverter<Object, Node> converter) {
this.converter = converter;
}
public Object executeQuery(Object entity, String queryString, Map<String, Object> params, Neo4jPersistentProperty property) {
final TypeInformation<?> typeInformation = property.getTypeInformation();
final TypeInformation<?> actualType = typeInformation.getActualType();
final Class<?> targetType = actualType.getType();
if (actualType.isMap()) {
return cypherQueryExecutor.queryForList(queryString, params);
}
if (typeInformation.isCollectionLike()) {
return cypherQueryExecutor.query(queryString, targetType, params);
}
return cypherQueryExecutor.queryForObject(queryString, targetType, params);
}
public void setEntityStateHandler(EntityStateHandler entityStateHandler) {
this.entityStateHandler = entityStateHandler;
public void setGraphDatabaseService(GraphDatabaseService graphDatabaseService) {
this.graphDatabaseService = graphDatabaseService;
}
}

View File

@@ -0,0 +1,34 @@
/**
* 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.support;
import org.neo4j.graphdb.Relationship;
/**
* @author mh
* @since 12.10.11
*/
public class RelationshipResult {
public final Relationship relationship;
public final Type type;
RelationshipResult(Relationship relationship, Type type) {
this.relationship = relationship;
this.type = type;
}
public enum Type { NEW, EXISTING, DELETED }
}

View File

@@ -0,0 +1,111 @@
/**
* 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.support;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.helpers.collection.ClosableIterable;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
import org.springframework.data.neo4j.mapping.Neo4jMappingContext;
/**
* @author mh
* @since 12.10.11
*/
public class TypeRepresentationStrategies implements TypeRepresentationStrategy<PropertyContainer> {
private final Neo4jMappingContext mappingContext;
private final TypeRepresentationStrategy<Node> nodeTypeRepresentationStrategy;
private final TypeRepresentationStrategy<Relationship> relationshipTypeRepresentationStrategy;
TypeRepresentationStrategies(Neo4jMappingContext mappingContext,
TypeRepresentationStrategy<Node> nodeTypeRepresentationStrategy,
TypeRepresentationStrategy<Relationship> relationshipTypeRepresentationStrategy) {
this.mappingContext = mappingContext;
this.nodeTypeRepresentationStrategy = nodeTypeRepresentationStrategy;
this.relationshipTypeRepresentationStrategy = relationshipTypeRepresentationStrategy;
}
@SuppressWarnings("unchecked")
private <T> TypeRepresentationStrategy<?> getTypeRepresentationStrategy(Class<T> type) {
if (mappingContext.isNodeEntity(type)) {
return (TypeRepresentationStrategy<?>) nodeTypeRepresentationStrategy;
} else if (mappingContext.isRelationshipEntity(type)) {
return (TypeRepresentationStrategy<?>) relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
}
@SuppressWarnings("unchecked")
private <S extends PropertyContainer, T> TypeRepresentationStrategy<S> getTypeRepresentationStrategy(S state, Class<T> type) {
if (state instanceof Node && mappingContext.isNodeEntity(type)) {
return (TypeRepresentationStrategy<S>) nodeTypeRepresentationStrategy;
} else if (state instanceof Relationship && mappingContext.isRelationshipEntity(type)) {
return (TypeRepresentationStrategy<S>) relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type " + type + " is not NodeBacked nor RelationshipBacked.");
}
@SuppressWarnings("unchecked")
private <S extends PropertyContainer, T> TypeRepresentationStrategy<S> getTypeRepresentationStrategy(S state) {
if (state instanceof Node) {
return (TypeRepresentationStrategy<S>) nodeTypeRepresentationStrategy;
} else if (state instanceof Relationship) {
return (TypeRepresentationStrategy<S>) relationshipTypeRepresentationStrategy;
}
throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked.");
}
@Override
public void postEntityCreation(PropertyContainer state, Class<?> type) {
getTypeRepresentationStrategy(state, type).postEntityCreation(state, type);
}
@Override
public <U> ClosableIterable<U> findAll(Class<U> type) {
return getTypeRepresentationStrategy(type).findAll(type);
}
@Override
public long count(Class<?> type) {
return getTypeRepresentationStrategy(type).count(type);
}
@Override
public <U> Class<U> getJavaType(PropertyContainer state) {
return getTypeRepresentationStrategy(state).getJavaType(state);
}
@Override
public void preEntityRemoval(PropertyContainer state) {
getTypeRepresentationStrategy(state).preEntityRemoval(state);
}
@Override
public <U> U createEntity(PropertyContainer state) throws IllegalStateException {
return getTypeRepresentationStrategy(state).createEntity(state);
}
@Override
public <U> U createEntity(PropertyContainer state, Class<U> type) throws IllegalStateException, IllegalArgumentException {
return getTypeRepresentationStrategy(state, type).createEntity(state, type);
}
@Override
public <U> U projectEntity(PropertyContainer state, Class<U> type) {
return getTypeRepresentationStrategy(state).projectEntity(state, type);
}
}

View File

@@ -21,7 +21,6 @@ import org.springframework.data.neo4j.support.GraphDatabaseContext;
import org.springframework.data.neo4j.support.conversion.EntityResultConverter;
import org.springframework.util.ClassUtils;
import java.util.Collections;
import java.util.Map;
/**
@@ -47,18 +46,14 @@ public class CypherQueryExecutor implements QueryOperations<Map<String,Object>>
}
public Iterable<Map<String, Object>> queryForList(String statement, Map<String,Object> params) {
return queryEngine.query(statement,mergeParams(params));
return queryEngine.query(statement, params);
}
public <T> Iterable<T> query(String statement, Class<T> type, Map<String,Object> params) {
return queryEngine.query(statement,mergeParams(params)).to(type);
return queryEngine.query(statement, params).to(type);
}
public <T> T queryForObject(String statement, Class<T> type, Map<String,Object> params) {
return (T) queryEngine.query(statement,mergeParams(params)).to(type).single();
}
private Map<String,Object> mergeParams(Map<String,Object> params) {
if (params==null) return Collections.emptyMap();
return params;
return (T) queryEngine.query(statement, params).to(type).single();
}
}

View File

@@ -32,7 +32,7 @@ import org.springframework.data.neo4j.support.EntityInstantiator;
public class IndexingRelationshipTypeRepresentationStrategy implements RelationshipTypeRepresentationStrategy {
public static final String INDEX_NAME = "__types__";
public static final String INDEX_NAME = "__rel_types__";
public static final String TYPE_PROPERTY_NAME = "__type__";
public static final String INDEX_KEY = "className";
private EntityInstantiator<Relationship> relationshipEntityInstantiator;

View File

@@ -15,32 +15,14 @@
*/
package org.springframework.data.neo4j.mapping;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
import org.springframework.data.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean;
import org.springframework.data.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory;
import org.springframework.data.neo4j.fieldaccess.RelationshipDelegatingFieldAccessorFactory;
import org.springframework.data.neo4j.model.Friendship;
import org.springframework.data.neo4j.model.Group;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.model.Personality;
import org.springframework.data.neo4j.support.EntityStateHandler;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import org.springframework.data.neo4j.support.node.NodeEntityInstantiator;
import org.springframework.data.neo4j.support.node.NodeEntityStateFactory;
import org.springframework.data.neo4j.support.relationship.RelationshipEntityInstantiator;
import org.springframework.data.neo4j.support.relationship.RelationshipEntityStateFactory;
import org.springframework.data.neo4j.support.typerepresentation.NoopNodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.NoopRelationshipTypeRepresentationStrategy;
import java.util.*;
@@ -52,17 +34,7 @@ import static org.junit.Assert.assertEquals;
* @author mh
* @since 19.09.11
*/
public class Neo4jEntityConverterTest {
public static final DynamicRelationshipType PERSONS = DynamicRelationshipType.withName("persons");
private static final RelationshipType KNOWS = DynamicRelationshipType.withName("knows");
private Neo4jEntityConverterImpl<Object,Node> converter;
private Transaction tx;
private GraphDatabaseContext gdc;
private Group group;
private Person michael;
private Person emil;
private Person andres;
public class Neo4jEntityConverterTest extends Neo4jPersistentTestBase {
/* OUCH
@@ -92,76 +64,6 @@ public class Neo4jEntityConverterTest {
*/
@Before
public void setUp() throws Exception {
final Neo4jMappingContext mappingContext = new Neo4jMappingContext();
gdc = createContext(mappingContext);
tx = gdc.beginTx();
final NodeEntityStateFactory nodeEntityStateFactory = createNodeEntityStateFactory(mappingContext);
final RelationshipEntityStateFactory relationshipEntityStateFactory = createRelationshipEntityStateFactory(mappingContext);
final EntityStateHandler entityStateHandler = new EntityStateHandler(mappingContext, gdc.getGraphDatabaseService());
final NodeEntityInstantiator entityInstantiator = new NodeEntityInstantiator(entityStateHandler);
final TypeRepresentationStrategy<Node> typeRepresentationStrategy = gdc.getNodeTypeRepresentationStrategy();
TypeMapper<Node> typeMapper = new DefaultTypeMapper<Node>(new TRSTypeAliasAccessor<Node>(typeRepresentationStrategy),asList(new ClassValueTypeInformationMapper()));
SourceStateTransmitter<Node> nodeStateTransmitter = new SourceStateTransmitter<Node>(nodeEntityStateFactory);
SourceStateTransmitter<Relationship> relationshipStateTransmitter = new SourceStateTransmitter<Relationship>(relationshipEntityStateFactory);
final ConversionService conversionService = gdc.getConversionService();
Neo4jEntityFetchHandler fetchHandler=new Neo4jEntityFetchHandler(entityStateHandler, conversionService, relationshipStateTransmitter , nodeStateTransmitter);
converter = new Neo4jEntityConverterImpl<Object,Node>(mappingContext, conversionService, entityInstantiator, entityStateHandler, typeMapper, nodeStateTransmitter, fetchHandler);
gdc.setNodeEntityConverter(converter);
group = new Group();
michael = new Person("Michael", 37);
emil = new Person("Emil", 30);
andres = new Person("Andrés", 36);
}
private NodeEntityStateFactory createNodeEntityStateFactory(Neo4jMappingContext mappingContext) {
final NodeEntityStateFactory nodeEntityStateFactory = new NodeEntityStateFactory();
nodeEntityStateFactory.setMappingContext(mappingContext);
nodeEntityStateFactory.setGraphDatabaseContext(gdc);
nodeEntityStateFactory.setNodeDelegatingFieldAccessorFactory(new NodeDelegatingFieldAccessorFactory(gdc));
return nodeEntityStateFactory;
}
private RelationshipEntityStateFactory createRelationshipEntityStateFactory(Neo4jMappingContext mappingContext) {
final RelationshipEntityStateFactory relationshipEntityStateFactory = new RelationshipEntityStateFactory();
relationshipEntityStateFactory.setMappingContext(mappingContext);
relationshipEntityStateFactory.setGraphDatabaseContext(gdc);
relationshipEntityStateFactory.setRelationshipDelegatingFieldAccessorFactory(new RelationshipDelegatingFieldAccessorFactory(gdc));
return relationshipEntityStateFactory;
}
private GraphDatabaseContext createContext(Neo4jMappingContext mappingContext) throws Exception {
GraphDatabaseContext gdc = new GraphDatabaseContext();
final ImpermanentGraphDatabase gdb = new ImpermanentGraphDatabase();
gdc.setGraphDatabaseService(gdb);
gdc.setMappingContext(mappingContext);
final EntityStateHandler entityStateHandler = new EntityStateHandler(mappingContext, gdb);
gdc.setNodeTypeRepresentationStrategy(new NoopNodeTypeRepresentationStrategy(new NodeEntityInstantiator(entityStateHandler)));
gdc.setRelationshipTypeRepresentationStrategy(new NoopRelationshipTypeRepresentationStrategy(new RelationshipEntityInstantiator(entityStateHandler)));
gdc.setConversionService(new Neo4jConversionServiceFactoryBean().getObject());
gdc.setEntityStateHandler(entityStateHandler);
gdc.createCypherExecutor();
return gdc;
}
private List<Node> groupMemberNodes() {
return groupMemberNodes(groupNode());
}
private List<Node> groupMemberNodes(Node node) {
return getRelatedNodes(node, "persons", Direction.OUTGOING);
}
@After
public void tearDown() throws Exception {
tx.failure();
tx.finish();
gdc.getGraphDatabaseService().shutdown();
}
@Test
public void testWriteEntityToNewNode() {
@@ -178,31 +80,23 @@ public class Neo4jEntityConverterTest {
assertEquals("node found in index", createdNode, found);
}
private Node michaelNode() {
return gdc.getNodeById(michael.getId());
}
@Test
public void testWriteEntityToExistingNode() {
final Node existingNode = createNewNode();
converter.write(michael, existingNode);
write(michael, existingNode);
assertEquals("Entity uses provided node", (Long) existingNode.getId(), michael.getId());
assertEquals("Michael", existingNode.getProperty("name"));
}
private Node createNewNode() {
return gdc.createNode();
}
@Test
public void testUpdateExistingNode() {
final Node existingNode = createNewNode();
existingNode.setProperty("name", "Test");
assertEquals("Test", existingNode.getProperty("name"));
converter.write(michael, existingNode);
write(michael, existingNode);
assertEquals("Michael", existingNode.getProperty("name"));
michael.setName("Emil");
converter.write(michael, existingNode);
write(michael, existingNode);
assertEquals("Emil", existingNode.getProperty("name"));
}
@@ -211,7 +105,7 @@ public class Neo4jEntityConverterTest {
final Node existingNode = createNewNode();
michael.setBirthdate(new Date(100));
michael.setPersonality(Personality.EXTROVERT);
converter.write(michael, existingNode);
write(michael, existingNode);
assertEquals("100", existingNode.getProperty("birthdate"));
assertEquals("EXTROVERT", existingNode.getProperty("personality"));
}
@@ -223,7 +117,7 @@ public class Neo4jEntityConverterTest {
existingNode.setProperty("age", 36);
existingNode.setProperty("personality", "EXTROVERT");
existingNode.setProperty("birthdate", "100");
final Person p = converter.read(Person.class, existingNode);
final Person p = readPerson(existingNode);
assertEquals("Michael", p.getName());
assertEquals(36, p.getAge());
assertEquals(new Date(100), p.getBirthdate());
@@ -233,9 +127,9 @@ public class Neo4jEntityConverterTest {
@Test
public void testDeleteProperty() {
final Node existingNode = createNewNode();
converter.write(michael, existingNode);
write(michael, existingNode);
michael.setName(null);
converter.write(michael, existingNode);
write(michael, existingNode);
assertEquals(false, existingNode.hasProperty("name"));
}
@@ -243,7 +137,7 @@ public class Neo4jEntityConverterTest {
public void testReadEntityFromExistingNode() {
final Node node = createNewNode();
node.setProperty("name", "Emil");
final Person p = converter.read(Person.class, node);
final Person p = readPerson(node);
assertEquals("Emil", p.getName());
}
@@ -257,31 +151,6 @@ public class Neo4jEntityConverterTest {
assertEquals("added additional relationship end node", emil.getId(), (Long) boss.getId());
}
private Group storeInGraph(Group g) {
final Long id = g.getId();
if (id != null) {
converter.write(g, gdc.getNodeById(id));
} else {
converter.write(g, null);
}
return g;
}
private Person storeInGraph(Person p) {
final Long id = p.getId();
if (id != null) {
converter.write(p, gdc.getNodeById(id));
} else {
converter.write(p, null);
}
return p;
}
private <T> T storeInGraph(T obj) {
converter.write(obj, null);
return obj;
}
@Test
public void testAddRelationshipWithPreExistingNode() {
storeInGraph(michael);
@@ -292,10 +161,6 @@ public class Neo4jEntityConverterTest {
assertEquals("added michaelNode to group", michael.getId(), (Long) michaelNode.getId());
}
private Node groupNode() {
return gdc.getNodeById(group.getId());
}
@Test
public void testAddRelationshipWithTwoExistingNodes() {
storeInGraph(michael);
@@ -318,14 +183,6 @@ public class Neo4jEntityConverterTest {
}
private <T> Set<T> set(T... objs) {
return new HashSet<T>(asList(objs));
}
private <T> Set<T> set(Iterable<T> objs) {
return IteratorUtil.addToCollection(objs, new HashSet<T>());
}
@Test
public void testAddRelationshipCascadeOverTwoSteps() {
andres.setBoss(emil);
@@ -337,14 +194,6 @@ public class Neo4jEntityConverterTest {
assertEquals(emil.getId(), (Long) getRelatedNodes(andresNode(), "boss", Direction.INCOMING).get(0).getId());
}
private Node andresNode() {
return gdc.getNodeById(andres.getId());
}
private Node emilNode() {
return gdc.getNodeById(emil.getId());
}
@Test
public void testDeleteSingleRelationship() {
emil.setBoss(andres);
@@ -375,7 +224,7 @@ public class Neo4jEntityConverterTest {
groupNode.createRelationshipTo(p1, PERSONS);
groupNode.createRelationshipTo(p2, PERSONS);
Group g = converter.read(Group.class, groupNode);
Group g = readGroup(groupNode);
assertEquals(set(readPerson(p1), readPerson(p2)), set(g.getPersons()));
}
@@ -387,7 +236,7 @@ public class Neo4jEntityConverterTest {
groupNode.createRelationshipTo(p1, PERSONS);
groupNode.createRelationshipTo(p2, PERSONS);
Group g = converter.read(Group.class, groupNode);
Group g = readGroup(groupNode);
assertEquals(set(readPerson(p1), readPerson(p2)), set(g.getReadOnlyPersons()));
}
@@ -413,18 +262,6 @@ public class Neo4jEntityConverterTest {
assertEquals(set(emilNode()), set(groupMemberNodes()));
}
public Person readPerson(Node node) {
return converter.read(Person.class, node);
}
private List<Node> getRelatedNodes(Node startNode, String type, Direction direction) {
List<Node> result = new ArrayList<Node>();
for (Relationship relationship : startNode.getRelationships(DynamicRelationshipType.withName(type), direction)) {
result.add(relationship.getOtherNode(startNode));
}
return result;
}
@Test
public void testSetRelationshipWithNonExistingNode() {
michael.setBoss(emil);
@@ -451,7 +288,7 @@ public class Neo4jEntityConverterTest {
julianNode.setProperty("name", "Julian");
groupNode.createRelationshipTo(julianNode, PERSONS);
Group g = converter.read(Group.class, groupNode);
Group g = readGroup(groupNode);
Person julian = IteratorUtil.first(g.getPersons());
assertEquals("Julian", julian.getName());
}
@@ -464,7 +301,7 @@ public class Neo4jEntityConverterTest {
Relationship friendshipRelationship = michaelNode().createRelationshipTo(andresNode(), KNOWS);
friendshipRelationship.setProperty("Friendship.years", 19);
Person m = converter.read(Person.class, michaelNode());
Person m = readPerson(michaelNode());
Friendship friendship = IteratorUtil.first(m.getFriendships());
assertEquals((Long) friendshipRelationship.getId(), friendship.getId());

View File

@@ -0,0 +1,90 @@
/**
* 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.mapping;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.neo4j.graphdb.Node;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.model.Friendship;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.support.ManagedEntity;
import static org.junit.Assert.assertEquals;
/**
* @author mh
* @since 12.10.11
*/
public class Neo4jEntityPersisterTest extends Neo4jPersistentTestBase {
@Test
public void testCreateEntityFromStoredType() throws Exception {
final Node personNode = gdc.createNode();
personNode.setProperty("name","Michael");
final Person person = entityPersister.createEntityFromState(personNode, Person.class);
assertEquals("Michael",person.getName());
}
@Test
public void testCreateEntityFromState() throws Exception {
}
@Test
public void testProjectTo() throws Exception {
storeInGraph(michael);
final Developer developer = entityPersister.projectTo(michael, Developer.class);
assertEquals(michael.getId(), developer.id);
assertEquals(michael.getName(), developer.name);
}
@Test
public void testGetPersistentState() throws Exception {
storeInGraph(michael);
assertEquals(michaelNode(), entityPersister.getPersistentState(michael));
}
@Test
public void testPersist() throws Exception {
entityPersister.persist(michael);
assertEquals((Long) michaelNode().getId(), michael.getId());
assertEquals(michaelNode(), entityPersister.getPersistentState(michael));
assertEquals(michaelNode().getProperty("name"), michael.getName());
}
@Test
public void testIsManaged() throws Exception {
assertEquals(false,entityPersister.isManaged(michael));
assertEquals(true,entityPersister.isManaged(Mockito.mock(ManagedEntity.class)));
}
@Test
public void testIsNodeEntity() throws Exception {
assertEquals(true,entityPersister.isNodeEntity(Person.class));
assertEquals(false, entityPersister.isNodeEntity(Friendship.class));
}
@Test
public void testIsRelationshipEntity() throws Exception {
assertEquals(false,entityPersister.isRelationshipEntity(Person.class));
assertEquals(true, entityPersister.isRelationshipEntity(Friendship.class));
}
}

View File

@@ -0,0 +1,234 @@
/**
* 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.mapping;
import org.junit.After;
import org.junit.Before;
import org.neo4j.graphdb.*;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.convert.DefaultTypeMapper;
import org.springframework.data.convert.TypeMapper;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean;
import org.springframework.data.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory;
import org.springframework.data.neo4j.fieldaccess.RelationshipDelegatingFieldAccessorFactory;
import org.springframework.data.neo4j.model.Group;
import org.springframework.data.neo4j.model.Person;
import org.springframework.data.neo4j.support.EntityStateHandler;
import org.springframework.data.neo4j.support.GraphDatabaseContext;
import org.springframework.data.neo4j.support.node.NodeEntityInstantiator;
import org.springframework.data.neo4j.support.node.NodeEntityStateFactory;
import org.springframework.data.neo4j.support.relationship.RelationshipEntityInstantiator;
import org.springframework.data.neo4j.support.relationship.RelationshipEntityStateFactory;
import org.springframework.data.neo4j.support.typerepresentation.NoopNodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.NoopRelationshipTypeRepresentationStrategy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.Arrays.asList;
/**
* @author mh
* @since 12.10.11
*/
public class Neo4jPersistentTestBase {
private Transaction tx;
private Neo4jEntityConverterImpl<Object,Node> nodeConverter;
protected GraphDatabaseContext gdc;
protected NodeEntityStateFactory nodeEntityStateFactory;
protected RelationshipEntityStateFactory relationshipEntityStateFactory;
protected EntityStateHandler entityStateHandler;
protected NodeEntityInstantiator nodeEntityInstantiator;
protected TypeMapper<Node> nodeTypeMapper;
protected SourceStateTransmitter<Node> nodeStateTransmitter;
protected SourceStateTransmitter<Relationship> relationshipStateTransmitter;
protected ConversionService conversionService;
protected Neo4jEntityFetchHandler fetchHandler;
protected Neo4jMappingContext mappingContext;
protected Neo4jEntityPersister entityPersister;
protected Group group;
protected Person michael;
protected Person emil;
protected Person andres;
public static final RelationshipType PERSONS = DynamicRelationshipType.withName("persons");
protected static final RelationshipType KNOWS = DynamicRelationshipType.withName("knows");
@NodeEntity
public static class Developer {
@GraphId
Long id;
String name;
}
@Before
public void setUp() throws Exception {
mappingContext = new Neo4jMappingContext();
gdc = createContext(mappingContext);
tx = gdc.beginTx();
nodeEntityStateFactory = createNodeEntityStateFactory(mappingContext);
relationshipEntityStateFactory = createRelationshipEntityStateFactory(mappingContext);
gdc.setNodeEntityStateFactory(nodeEntityStateFactory);
gdc.setRelationshipEntityStateFactory(relationshipEntityStateFactory);
gdc.postConstruct();
entityStateHandler = gdc.getEntityStateHandler();
nodeEntityInstantiator = new NodeEntityInstantiator(entityStateHandler);
nodeTypeMapper = new DefaultTypeMapper<Node>(new TRSTypeAliasAccessor<Node>(gdc.getNodeTypeRepresentationStrategy()),asList(new ClassValueTypeInformationMapper()));
nodeStateTransmitter = new SourceStateTransmitter<Node>(nodeEntityStateFactory);
relationshipStateTransmitter = new SourceStateTransmitter<Relationship>(relationshipEntityStateFactory);
conversionService = gdc.getConversionService();
fetchHandler = new Neo4jEntityFetchHandler(entityStateHandler, conversionService, relationshipStateTransmitter, nodeStateTransmitter);
nodeConverter = new Neo4jEntityConverterImpl<Object,Node>(mappingContext, conversionService, nodeEntityInstantiator, entityStateHandler, nodeTypeMapper, nodeStateTransmitter, fetchHandler);
entityPersister = new Neo4jEntityPersister(gdc.getGraphDatabaseService(), mappingContext, conversionService,
nodeEntityStateFactory, relationshipEntityStateFactory,
gdc.getNodeTypeRepresentationStrategy(), gdc.getRelationshipTypeRepresentationStrategy());
group = new Group();
michael = new Person("Michael", 37);
emil = new Person("Emil", 30);
andres = new Person("Andrés", 36);
}
private NodeEntityStateFactory createNodeEntityStateFactory(Neo4jMappingContext mappingContext) {
final NodeEntityStateFactory nodeEntityStateFactory = new NodeEntityStateFactory();
nodeEntityStateFactory.setMappingContext(mappingContext);
nodeEntityStateFactory.setGraphDatabaseContext(gdc);
nodeEntityStateFactory.setNodeDelegatingFieldAccessorFactory(new NodeDelegatingFieldAccessorFactory(gdc));
return nodeEntityStateFactory;
}
private RelationshipEntityStateFactory createRelationshipEntityStateFactory(Neo4jMappingContext mappingContext) {
final RelationshipEntityStateFactory relationshipEntityStateFactory = new RelationshipEntityStateFactory();
relationshipEntityStateFactory.setMappingContext(mappingContext);
relationshipEntityStateFactory.setGraphDatabaseContext(gdc);
relationshipEntityStateFactory.setRelationshipDelegatingFieldAccessorFactory(new RelationshipDelegatingFieldAccessorFactory(gdc));
return relationshipEntityStateFactory;
}
private GraphDatabaseContext createContext(Neo4jMappingContext mappingContext) throws Exception {
GraphDatabaseContext gdc = new GraphDatabaseContext();
final ImpermanentGraphDatabase gdb = new ImpermanentGraphDatabase();
gdc.setGraphDatabaseService(gdb);
gdc.setMappingContext(mappingContext);
final EntityStateHandler entityStateHandler = new EntityStateHandler(mappingContext, gdb);
gdc.setNodeTypeRepresentationStrategy(new NoopNodeTypeRepresentationStrategy(new NodeEntityInstantiator(entityStateHandler)));
gdc.setRelationshipTypeRepresentationStrategy(new NoopRelationshipTypeRepresentationStrategy(new RelationshipEntityInstantiator(entityStateHandler)));
gdc.setConversionService(new Neo4jConversionServiceFactoryBean().getObject());
gdc.setEntityStateHandler(entityStateHandler);
return gdc;
}
protected List<Node> groupMemberNodes() {
return groupMemberNodes(groupNode());
}
private List<Node> groupMemberNodes(Node node) {
return getRelatedNodes(node, "persons", Direction.OUTGOING);
}
@After
public void tearDown() throws Exception {
tx.failure();
tx.finish();
gdc.getGraphDatabaseService().shutdown();
}
protected Node michaelNode() {
return gdc.getNodeById(michael.getId());
}
protected Node createNewNode() {
return gdc.createNode();
}
protected Group storeInGraph(Group g) {
final Long id = g.getId();
if (id != null) {
write(g, gdc.getNodeById(id));
} else {
write(g, null);
}
return g;
}
protected Person storeInGraph(Person p) {
final Long id = p.getId();
if (id != null) {
write(p, gdc.getNodeById(id));
} else {
write(p,null);
}
return p;
}
protected Object write(Object entity, Node node) {
entityPersister.write(entity, node);
return entity;
}
@SuppressWarnings("unchecked")
private <T> T storeInGraph(T obj) {
return (T) write(obj,null);
}
protected Node groupNode() {
return gdc.getNodeById(group.getId());
}
protected <T> Set<T> set(T... objs) {
return new HashSet<T>(asList(objs));
}
protected <T> Set<T> set(Iterable<T> objs) {
return IteratorUtil.addToCollection(objs, new HashSet<T>());
}
protected Node andresNode() {
return gdc.getNodeById(andres.getId());
}
protected Node emilNode() {
return gdc.getNodeById(emil.getId());
}
public Person readPerson(Node node) {
return entityPersister.read(Person.class, node);
}
public Group readGroup(Node node) {
return entityPersister.read(Group.class, node);
}
protected List<Node> getRelatedNodes(Node startNode, String type, Direction direction) {
List<Node> result = new ArrayList<Node>();
for (Relationship relationship : startNode.getRelationships(DynamicRelationshipType.withName(type), direction)) {
result.add(relationship.getOtherNode(startNode));
}
return result;
}
}

View File

@@ -162,7 +162,7 @@ public class Person {
@Override
public String toString() {
return name;
return "["+graphId+"] " + name;
}
@Override

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test;
package org.springframework.data.test;
import org.junit.After;
@@ -35,7 +35,7 @@ public abstract class DocumentingTestBase {
public void outputDocs() throws IOException {
final File directory = new File(docbookDirectory(),"snippets");
if (directory.isFile() || !directory.exists() && !directory.mkdirs()) throw new RuntimeException("Could not create directory "+directory);
final String name = getClass().getSimpleName();
final String name = getSnippetName();
final PrintWriter writer = new PrintWriter(new FileWriter(getSnippetFileName(directory,name)));
writer.write(createHeader());
writer.write(createTitle());
@@ -45,6 +45,11 @@ public abstract class DocumentingTestBase {
writer.close();
}
private String getSnippetName() {
final String className = getClass().getSimpleName();
return className.replace("Tests?$","");
}
private File docbookDirectory() {
return new File(determineRoot(),DOCBOOK_DIR);
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test;
package org.springframework.data.test;
import org.junit.Test;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.snippets;
package org.springframework.data.test.snippets;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,21 +26,20 @@ import org.springframework.data.neo4j.conversion.ResultConverter;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.template.Neo4jOperations;
import org.springframework.data.neo4j.template.Neo4jTemplate;
import org.springframework.test.DocumentingTestBase;
import org.springframework.data.test.DocumentingTestBase;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.neo4j.helpers.collection.MapUtil.map;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:DocumentingTest-context.xml"})
public class SnippetNeo4jTemplateMethods extends DocumentingTestBase {
public class SnippetNeo4jTemplateMethodsTest extends DocumentingTestBase {
@Autowired
private GraphDatabase graphDatabase;
private static final RelationshipType WORKS_WITH = DynamicRelationshipType.withName("WORKS_WITH");
@@ -66,10 +65,9 @@ public class SnippetNeo4jTemplateMethods extends DocumentingTestBase {
neo.createRelationship(mark, thomas, WORKS_WITH, map("project", "spring-data"));
neo.index("devs", thomas, "name", "Thomas");
// Cypher
assert "Mark".equals(neo.query("start p=({p_person}) match p<-[:WORKS_WITH]-other return other.name",
map("person", thomas)).to(String.class).single());
// Cypher TODO
assertEquals( "Mark", neo.query("start p=node({person}) match p<-[:WORKS_WITH]-other return other.name",
map("person", asList(thomas.getId()))).to(String.class).single());
// SNIPPET template
@@ -79,18 +77,18 @@ public class SnippetNeo4jTemplateMethods extends DocumentingTestBase {
// SNIPPET template
// Gremlin
assert thomas.equals(neo.execute("g.v(person).out('WORKS_WITH')",
map("person", mark)).to(Node.class).single());
assertEquals(thomas, neo.execute("g.v(person).out('WORKS_WITH')",
map("person", mark.getId())).to(Node.class).single());
// Index lookup
assert mark.equals(neo.lookup("devs", "name", "Mark").to(Node.class).single());
assertEquals(thomas, neo.lookup("devs", "name", "Thomas").to(Node.class).single());
// Index lookup with Result Converter
assert "Mark".equals(neo.lookup("devs", "name", "Mark").to(String.class, new ResultConverter<PropertyContainer, String>() {
assertEquals("Thomas", neo.lookup("devs", "name", "Thomas").to(String.class, new ResultConverter<PropertyContainer, String>() {
public String convert(PropertyContainer element, Class<String> type) {
return (String) element.getProperty("name");
}
}));
}).single());
// SNIPPET template
}