diff --git a/.gitignore b/.gitignore index 6bf41057d..fe0f2e4f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.log .DS_Store target .springBeans diff --git a/pom.xml b/pom.xml index fbd377900..9e7ee8123 100644 --- a/pom.xml +++ b/pom.xml @@ -7,12 +7,14 @@ Spring Data Neo4j Distribution - 1.2.0.BUILD-SNAPSHOT + 2.0.0.BUILD-SNAPSHOT pom spring-data-neo4j-parent spring-data-neo4j spring-data-neo4j-rest + spring-data-neo4j-aspects + spring-data-neo4j-cross-store http://www.springsource.org/spring-data/neo4j @@ -461,7 +463,7 @@ org.springframework.build org.springframework.build.aws.ant - 3.0.5.RELEASE + 3.0.6.RELEASE net.java.dev.jets3t diff --git a/spring-data-neo4j-aspects/pom.xml b/spring-data-neo4j-aspects/pom.xml new file mode 100644 index 000000000..9cb651304 --- /dev/null +++ b/spring-data-neo4j-aspects/pom.xml @@ -0,0 +1,236 @@ + + 4.0.0 + + org.springframework.data + spring-data-neo4j-parent + 2.0.0.BUILD-SNAPSHOT + ../spring-data-neo4j-parent/pom.xml + + spring-data-neo4j-aspects + jar + Spring Data Neo4J Aspects + + http://www.springsource.org/spring-data/neo4j + + + + org.springframework + spring-beans + + + org.springframework + spring-tx + + + org.springframework + spring-aspects + + + org.springframework + spring-orm + + + + + org.springframework.data + spring-data-commons-core + + + org.springframework.data + spring-data-neo4j + + + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + compile + + + org.slf4j + slf4j-log4j12 + runtime + + + log4j + log4j + + + javax.mail + mail + + + javax.jms + jms + + + com.sun.jdmk + jmxtools + + + com.sun.jmx + jmxri + + + runtime + + + + javax.annotation + jsr250-api + true + + + + org.mockito + mockito-all + test + + + + junit + junit + + + + org.aspectj + aspectjrt + ${aspectj.version} + + + + cglib + cglib + 2.2 + + + + org.neo4j + neo4j-kernel + test-jar + test + + + + commons-configuration + commons-configuration + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + 1.0.0.Final + + + + + org.hibernate + hibernate-entitymanager + 3.5.5-Final + test + + + hsqldb + hsqldb + 1.8.0.10 + test + + + javax.validation + validation-api + 1.0.0.GA + + + org.hibernate + hibernate-validator + 4.0.2.GA + test + + + + + + neo4j-public-repository + Neo4J Public Repository + http://m2.neo4j.org + + true + + + + spring-maven-snapshot + + true + + Springframework Maven SNAPSHOT Repository + http://maven.springframework.org/snapshot + + + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.2 + + + org.aspectj + aspectjrt + ${aspectj.version} + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + compile + test-compile + + + + + true + + + org.springframework + spring-aspects + + + org.springframework + spring-data-neo4j-aspects + + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + com.springsource.bundlor + com.springsource.bundlor.maven + + + + + diff --git a/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/config/Neo4jAspectConfiguration.java b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/config/Neo4jAspectConfiguration.java new file mode 100644 index 000000000..776773abe --- /dev/null +++ b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/config/Neo4jAspectConfiguration.java @@ -0,0 +1,29 @@ +package org.springframework.data.neo4j.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.data.neo4j.support.node.Neo4jNodeBacking; +import org.springframework.data.neo4j.support.node.NodeEntityStateFactory; +import org.springframework.data.neo4j.support.relationship.Neo4jRelationshipBacking; + +/** + * @author mh + * @since 30.09.11 + */ +public class Neo4jAspectConfiguration extends Neo4jConfiguration { + @Bean + public Neo4jRelationshipBacking neo4jRelationshipBacking() throws Exception { + Neo4jRelationshipBacking aspect = Neo4jRelationshipBacking.aspectOf(); + aspect.setGraphDatabaseContext(graphDatabaseContext()); +aspect.setRelationshipEntityStateFactory(relationshipEntityStateFactory()); + return aspect; + } + + @Bean + public Neo4jNodeBacking neo4jNodeBacking() throws Exception { + Neo4jNodeBacking aspect = Neo4jNodeBacking.aspectOf(); + aspect.setGraphDatabaseContext(graphDatabaseContext()); + NodeEntityStateFactory entityStateFactory = nodeEntityStateFactory(); + aspect.setNodeEntityStateFactory(entityStateFactory); + return aspect; + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphBacked.java b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/GraphBacked.java similarity index 92% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphBacked.java rename to spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/GraphBacked.java index d548c7b7d..1ff76f623 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/GraphBacked.java +++ b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/GraphBacked.java @@ -16,6 +16,8 @@ package org.springframework.data.neo4j.core; +import org.springframework.data.neo4j.support.ManagedEntity; + /** * super interface denoting entities that are graph backed, the backing STATE can be a {@link org.neo4j.graphdb.Node} * or a {@link org.neo4j.graphdb.Relationship}. @@ -24,7 +26,7 @@ package org.springframework.data.neo4j.core; * @author Michael Hunger * @since 21.09.2010 */ -public interface GraphBacked { +public interface GraphBacked extends ManagedEntity { /** * internal setter used for initializing the graph-db state on existing or newly created entities * diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java similarity index 100% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java rename to spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/RelationshipBacked.java b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/RelationshipBacked.java similarity index 100% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/RelationshipBacked.java rename to spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/core/RelationshipBacked.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java similarity index 53% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java rename to spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java index 1645c8635..e2e4d8f0f 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java +++ b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/fieldaccess/JpaIdFieldAccessListenerFactory.java @@ -18,7 +18,8 @@ package org.springframework.data.neo4j.fieldaccess; import org.springframework.data.neo4j.core.EntityState; import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import javax.persistence.Id; @@ -26,29 +27,39 @@ import javax.persistence.Id; * @author Michael Hunger * @since 12.09.2010 */ -public class JpaIdFieldAccessListenerFactory implements FieldAccessorListenerFactory { +public class JpaIdFieldAccessListenerFactory implements FieldAccessorListenerFactory { + private final GraphDatabaseContext graphDatabaseContext; + + public JpaIdFieldAccessListenerFactory(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; + } + @Override - public boolean accept(final Neo4JPersistentProperty property) { + public boolean accept(final Neo4jPersistentProperty property) { return property.isAnnotationPresent(Id.class); } @Override - public FieldAccessListener forField(final Neo4JPersistentProperty property) { - return new JpaIdFieldListener(property); + public FieldAccessListener forField(final Neo4jPersistentProperty property) { + return new JpaIdFieldListener(property,graphDatabaseContext); } - public static class JpaIdFieldListener implements FieldAccessListener { - protected final Neo4JPersistentProperty property; + public static class JpaIdFieldListener implements FieldAccessListener { + protected final Neo4jPersistentProperty property; + private final GraphDatabaseContext graphDatabaseContext; - public JpaIdFieldListener(final Neo4JPersistentProperty property) { + public JpaIdFieldListener(final Neo4jPersistentProperty property, GraphDatabaseContext graphDatabaseContext) { this.property = property; + this.graphDatabaseContext = graphDatabaseContext; } @Override - public void valueChanged(NodeBacked nodeBacked, Object oldVal, Object newVal) { + public void valueChanged(Object entity, Object oldVal, Object newVal) { if (newVal != null) { - EntityState entityState =nodeBacked.getEntityState(); + graphDatabaseContext.save(entity); +/* TODO EntityState entityState = entity.getEntityState(); entityState.persist(); +*/ } } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj similarity index 98% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj rename to spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj index 9e31a1997..98df47166 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj +++ b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj @@ -62,13 +62,24 @@ import static org.springframework.data.neo4j.support.DoReturn.unwrap; * * Handles constructor invocation and partial entities as well. */ -public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields { +public privileged aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields { protected final Log log = LogFactory.getLog(getClass()); declare parents : (@NodeEntity *) implements NodeBacked; + //declare @type: NodeBacked+: @Configurable; + private GraphDatabaseContext graphDatabaseContext; + private NodeEntityStateFactory entityStateFactory; + + public void setGraphDatabaseContext(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; + } + public void setNodeEntityStateFactory(NodeEntityStateFactory entityStateFactory) { + this.entityStateFactory = entityStateFactory; + } + declare @field: @GraphProperty * (@Entity @NodeEntity(partial=true) *).*:@Transient; declare @field: @RelatedTo * (@Entity @NodeEntity(partial=true) *).*:@Transient; declare @field: @RelatedToVia * (@Entity @NodeEntity(partial=true) *).*:@Transient; @@ -91,15 +102,6 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields NodeBacked.entityState; + private transient EntityState NodeBacked.entityState; public T NodeBacked.persist() { return (T)this.entityState.persist(); @@ -145,7 +147,7 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields NodeBacked.getEntityState() { + public EntityState NodeBacked.getEntityState() { return entityState; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/Neo4jRelationshipBacking.aj b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/support/relationship/Neo4jRelationshipBacking.aj similarity index 96% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/Neo4jRelationshipBacking.aj rename to spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/support/relationship/Neo4jRelationshipBacking.aj index 496a414cf..b93e3ee45 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/Neo4jRelationshipBacking.aj +++ b/spring-data-neo4j-aspects/src/main/java/org/springframework/data/neo4j/support/relationship/Neo4jRelationshipBacking.aj @@ -69,7 +69,7 @@ public aspect Neo4jRelationshipBacking { /** * field for {@link org.springframework.data.neo4j.core.EntityState} that takes care of all entity operations */ - private EntityState RelationshipBacked.entityState; + private EntityState RelationshipBacked.entityState; public void RelationshipBacked.setPersistentState(Relationship r) { if (this.entityState == null) { @@ -92,7 +92,7 @@ public aspect Neo4jRelationshipBacking { } - public EntityState RelationshipBacked.getEntityState() { + public EntityState RelationshipBacked.getEntityState() { return this.entityState; } diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Attribute.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Attribute.java new file mode 100644 index 000000000..e90f699d9 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Attribute.java @@ -0,0 +1,38 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.Indexed; +import org.springframework.data.neo4j.annotation.NodeEntity; + +/** + * @author mh + * @since 27.04.11 + */ +@NodeEntity +public class Attribute { + @Indexed + T value; + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Car.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Car.java new file mode 100644 index 000000000..505742632 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Car.java @@ -0,0 +1,30 @@ +/** + * 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; + +import org.neo4j.graphdb.Node; +import org.springframework.data.neo4j.annotation.NodeEntity; + +@NodeEntity +public abstract class Car { + public Car() { + } + + public Car(Node n) { + setPersistentState(n); + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Developer.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Developer.java new file mode 100644 index 000000000..3ceb00edf --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Developer.java @@ -0,0 +1,46 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.NodeEntity; + +/** + * @author mh + * @since 18.02.11 + */ +@NodeEntity +public class Developer { + String name; + + Person spouse; + + public Developer(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public Person getSpouse() { + return spouse; + } + + public void setSpouse(Person spouse) { + this.spouse = spouse; + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Friendship.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Friendship.java new file mode 100644 index 000000000..c77f3bd12 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Friendship.java @@ -0,0 +1,107 @@ +/** + * 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; + + +import java.util.Date; + +import org.springframework.data.neo4j.annotation.EndNode; +import org.springframework.data.neo4j.annotation.Indexed; +import org.springframework.data.neo4j.annotation.RelationshipEntity; +import org.springframework.data.neo4j.annotation.StartNode; +import org.springframework.data.neo4j.fieldaccess.DynamicProperties; + +@RelationshipEntity(useShortNames = false) +public class Friendship { + + public Friendship() { + } + + public Friendship(Person p1, Person p2) { + this.p1 = p1; + this.p2 = p2; + } + + public Friendship(Person p1, Person p2, int years) { + this.p1 = p1; + this.p2 = p2; + this.years = years; + } + + @StartNode + private Person p1; + + @EndNode + private Person p2; + + @Indexed + private int years; + + private Date firstMeetingDate; + + private DynamicProperties personalProperties; + + private transient String latestLocation; + + public void setPerson1(Person p) { + p1 = p; + } + + public Person getPerson1() { + return p1; + } + + public void setPerson2(Person p) { + p2 = p; + } + + public Person getPerson2() { + return p2; + } + + public void setYears(int years) { + this.years = years; + } + + public int getYears() { + return years; + } + + public void setFirstMeetingDate(Date firstMeetingDate) { + this.firstMeetingDate = firstMeetingDate; + } + + public Date getFirstMeetingDate() { + return firstMeetingDate; + } + + public void setLatestLocation(String latestLocation) { + this.latestLocation = latestLocation; + } + + public String getLatestLocation() { + return latestLocation; + } + + public void setPersonalProperties(DynamicProperties personalProperties) { + this.personalProperties = personalProperties; + } + + public DynamicProperties getPersonalProperties() { + return personalProperties; + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/FriendshipRepository.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/FriendshipRepository.java new file mode 100644 index 000000000..a13ce8d8a --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/FriendshipRepository.java @@ -0,0 +1,22 @@ +/** + * 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; + +import org.springframework.data.neo4j.repository.GraphRepository; + +public interface FriendshipRepository extends GraphRepository { +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Group.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Group.java new file mode 100644 index 000000000..5fb46b4a9 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Group.java @@ -0,0 +1,164 @@ +/** + * 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; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.DynamicRelationshipType; +import org.neo4j.graphdb.traversal.TraversalDescription; +import org.neo4j.kernel.Traversal; +import org.neo4j.kernel.impl.traversal.TraversalDescriptionImpl; +import org.springframework.data.neo4j.annotation.*; +import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; + +import java.util.Collection; + +@NodeEntity +public class Group { + + public final static String OTHER_NAME_INDEX="other_name"; + public static final String SEARCH_GROUPS_INDEX = "search-groups"; + + @RelatedTo(direction = Direction.OUTGOING) + private Collection persons; + + @RelatedTo(type = "persons", elementClass = Person.class) + private Iterable readOnlyPersons; + + @GraphTraversal(traversalBuilder = PeopleTraversalBuilder.class, elementClass = Person.class, params = "persons") + private Iterable people; + + @GraphProperty + @Indexed + private String name; + + @GraphProperty + @Indexed + private Boolean admin; + + @GraphProperty + private String unindexedName; + + private String unindexedName2; + + @GraphProperty + @Indexed(indexName = SEARCH_GROUPS_INDEX, fulltext = true) + private String fullTextName; + + @Indexed(fieldName = OTHER_NAME_INDEX) + private String otherName; + + @Indexed(level=Indexed.Level.GLOBAL) + private String globalName; + + @Indexed(level=Indexed.Level.CLASS) + private String classLevelName; + + @Indexed(level=Indexed.Level.INSTANCE) + private String indexLevelName; + + public String getFullTextName() { + return fullTextName; + } + + public void setFullTextName(String fullTextName) { + this.fullTextName = fullTextName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void setPersons(Collection persons) { + this.persons = persons; + } + + public void addPerson(Person person) { + persons.add(person); + } + + public Collection getPersons() { + return persons; + } + + public Iterable getReadOnlyPersons() { + return readOnlyPersons; + } + + public void setReadOnlyPersons(Iterable p) { + readOnlyPersons = p; + } + + public Iterable getPeople() { + return people; + } + + public void setOtherName(String otherName) { + this.otherName = otherName; + } + + private static class PeopleTraversalBuilder implements FieldTraversalDescriptionBuilder { + @Override + public TraversalDescription build(Object start, Neo4jPersistentProperty property, String...params) { + return new TraversalDescriptionImpl() + .relationships(DynamicRelationshipType.withName(params[0])) + .filter(Traversal.returnAllButStartNode()); + + } + } + + public String getUnindexedName() { + return unindexedName; + } + + public void setUnindexedName(String unindexedName) { + this.unindexedName = unindexedName; + } + + public String getUnindexedName2() { + return unindexedName2; + } + + public void setUnindexedName2(String unindexedName2) { + this.unindexedName2 = unindexedName2; + } + + public void setGlobalName(String globalName) { + this.globalName = globalName; + } + + public void setClassLevelName(String classLevelName) { + this.classLevelName = classLevelName; + } + + public void setIndexLevelName(String indexLevelName) { + this.indexLevelName = indexLevelName; + } + + public Boolean isAdmin() { + return admin; + } + + public void setAdmin(Boolean admin) { + this.admin = admin; + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/GroupRepository.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/GroupRepository.java new file mode 100644 index 000000000..ca58ecde9 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/GroupRepository.java @@ -0,0 +1,27 @@ +/** + * 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; + +import org.springframework.data.neo4j.repository.NamedIndexRepository; +import org.springframework.data.neo4j.repository.GraphRepository; + +/** + * @author mh + * @since 29.03.11 + */ +public interface GroupRepository extends GraphRepository, NamedIndexRepository { +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/InvalidOneToNEntity.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/InvalidOneToNEntity.java new file mode 100644 index 000000000..1fbe45eeb --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/InvalidOneToNEntity.java @@ -0,0 +1,28 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.annotation.RelatedTo; + +import java.util.Collection; + +@NodeEntity +public class InvalidOneToNEntity { + @RelatedTo + private Collection others; +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/InvalidReadOnlyOneToNEntity.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/InvalidReadOnlyOneToNEntity.java new file mode 100644 index 000000000..cd257c05f --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/InvalidReadOnlyOneToNEntity.java @@ -0,0 +1,26 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.annotation.RelatedTo; + +@NodeEntity +public class InvalidReadOnlyOneToNEntity { + @RelatedTo + private Iterable others; +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Named.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Named.java new file mode 100644 index 000000000..247f2bfae --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Named.java @@ -0,0 +1,32 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.NodeEntity; + +/** + * @author mh + * @since 20.01.11 + */ +@NodeEntity +public class Named { + public String name; + + public String getName() { + return name; + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Person.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Person.java new file mode 100644 index 000000000..876892545 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Person.java @@ -0,0 +1,244 @@ +/** + * 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; + +import java.util.Date; +import java.util.Map; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.Size; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.springframework.data.neo4j.annotation.GraphId; +import org.springframework.data.neo4j.annotation.Indexed; +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.annotation.Query; +import org.springframework.data.neo4j.annotation.RelatedTo; +import org.springframework.data.neo4j.annotation.RelatedToVia; +import org.springframework.data.neo4j.fieldaccess.DynamicProperties; + + +@NodeEntity +public class Person { + + public static final String NAME_INDEX = "name_index"; + @GraphId + private Long graphId; + + @Indexed(indexName = NAME_INDEX) + @Size(min = 3, max = 20) + private String name; + + @Indexed + private String nickname; + + @Max(100) + @Min(0) + @Indexed + private int age; + + private Short height; + + private transient String thought; + + private Personality personality; + + private Date birthdate; + + private Person spouse; + + private Car car; + + private DynamicProperties personalProperties; + + @RelatedTo + private Person mother; + + @RelatedTo(type = "boss", direction = Direction.INCOMING) + private Person boss; + + @RelatedToVia(type = "knows", elementClass = Friendship.class) + private Iterable friendships; + + @Query(value = "start person=(%start) match (person)<-[:boss]-(boss) return boss") + private Person bossByQuery; + + @Query(value = "start person=(%start) match (person)<-[:boss]-(boss) return boss.%property",params = {"property","name"}) + private String bossName; + + @Query(value = "start person=(%start) match (person)<-[:persons]-(team)-[:persons]->(member) return member",elementClass = Person.class) + private Iterable otherTeamMembers; + + @Query(value = "start person=(%start) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name, member.age") + private Iterable> otherTeamMemberData; + + public Person(Node n) { + setPersistentState(n); + } + + public String getBossName() { + return bossName; + } + + public Iterable getOtherTeamMembers() { + return otherTeamMembers; + } + + public Iterable> getOtherTeamMemberData() { + return otherTeamMemberData; + } + + public Person getBossByQuery() { + return bossByQuery; + } + + public Person() { + } + + public Person(String name, int age) { + this.name = name; + this.age = age; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Short getHeight() { + return height; + } + + public void setHeight(Short height) { + this.height = height; + } + + + public Person getSpouse() { + return spouse; + } + + public void setSpouse(Person spouse) { + this.spouse = spouse; + } + + public Person getMother() { + return mother; + } + + public void setMother(Person mother) { + this.mother = mother; + } + + public Person getBoss() { + return boss; + } + + public void setBoss(Person boss) { + this.boss = boss; + } + + @Override + public String toString() { + return name; + } + + public Iterable getFriendships() { + return friendships; + } + + public void setFriendships(Iterable f) { + friendships = f; + } + + public Friendship knows(Person p) { + return (Friendship)relateTo(p, Friendship.class,"knows"); + } + + public void setPersonality(Personality personality) { + this.personality = personality; + } + + public Personality getPersonality() { + return personality; + } + + public void setThought(String thought) { + this.thought = thought; + } + + public String getThought() { + return thought; + } + + public Date getBirthdate() { + return birthdate; + } + + public void setBirthdate(Date birthdate) { + this.birthdate = birthdate; + } + + public long getId() { + return graphId; + } + + public void setCar(Car car) { + this.car = car; + } + + public Car getCar() { + return car; + } + + public void setProperty(String key, Object value) { + personalProperties.setProperty(key, value); + } + + public Object getProperty(String key) { + return personalProperties.getProperty(key); + } + + public DynamicProperties getPersonalProperties() { + return personalProperties; + } + + public void setPersonalProperties(DynamicProperties personalProperties) { + this.personalProperties = personalProperties; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public static Person persistedPerson(String name, int age) { + return new Person(name,age).persist(); + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/PersonCreator.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/PersonCreator.java new file mode 100644 index 000000000..9e3a214b8 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/PersonCreator.java @@ -0,0 +1,31 @@ +/** + * 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; + +import org.neo4j.graphdb.Node; +import org.springframework.data.persistence.StateBackedCreator; + +/** + * @author mh + * @since 25.03.11 + */ +public class PersonCreator implements StateBackedCreator { + @Override + public Person create(Node n, Class c) throws Exception { + return new Person(n); + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/PersonRepository.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/PersonRepository.java new file mode 100644 index 000000000..8f8cc7e1b --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/PersonRepository.java @@ -0,0 +1,54 @@ +/** + * 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; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.neo4j.annotation.Query; +import org.springframework.data.neo4j.annotation.QueryType; +import org.springframework.data.neo4j.repository.GraphRepository; +import org.springframework.data.neo4j.repository.NamedIndexRepository; +import org.springframework.data.repository.query.Param; + +import java.util.Map; + +/** + * @author mh + * @since 29.03.11 + */ +public interface PersonRepository extends GraphRepository, NamedIndexRepository { + + @Query("start team=(%team) match (team)-[:persons]->(member) return member") + Iterable findAllTeamMembers(@Param("team") Group team); + + @Query(value = "g.v(team).out('persons')", type = QueryType.Gremlin) + Iterable findAllTeamMembersGremlin(@Param("team") Group team); + + @Query("start team=(%team) match (team)-[:persons]->(member) return member.name,member.age") + Iterable> findAllTeamMemberData(@Param("team") Group team); + + @Query("start person=(%person) match (boss)-[:boss]->(person) return boss") + Person findBoss(@Param("person") Person person); + + Group findTeam(@Param("person") Person person); + + @Query("start team=(%team) match (team)-[:persons]->(member) return member") + Page findAllTeamMembersPaged(@Param("team") Group team, Pageable page); + @Query("start team=(%team) match (team)-[:persons]->(member) return member") + Iterable findAllTeamMembersSorted(@Param("team") Group team, Sort sort); +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Personality.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Personality.java new file mode 100644 index 000000000..74893c299 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Personality.java @@ -0,0 +1,21 @@ +/** + * 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; + +public enum Personality { + EXTROVERT, INTROVERT +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/SubGroup.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/SubGroup.java new file mode 100644 index 000000000..2675382d9 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/SubGroup.java @@ -0,0 +1,23 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.*; + +@NodeEntity +public class SubGroup extends Group { +} \ No newline at end of file diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Toyota.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Toyota.java new file mode 100644 index 000000000..49a1983a7 --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Toyota.java @@ -0,0 +1,22 @@ +/** + * 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; + +public class Toyota extends Car { + public Toyota() { + } +} diff --git a/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Volvo.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Volvo.java new file mode 100644 index 000000000..81ed1912f --- /dev/null +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/Volvo.java @@ -0,0 +1,24 @@ +/** + * 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; + +import org.springframework.data.neo4j.annotation.NodeEntity; + +public class Volvo extends Car { + public Volvo() { + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/AttachEntityTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/AttachEntityTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/AttachEntityTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/AttachEntityTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/DynamicPropertiesTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/DynamicPropertiesTest.java similarity index 96% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/DynamicPropertiesTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/DynamicPropertiesTest.java index 5534f2a39..1e519328e 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/DynamicPropertiesTest.java +++ b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/DynamicPropertiesTest.java @@ -74,6 +74,12 @@ public class DynamicPropertiesTest assertEquals("String two", nodeFor(p).getProperty("personalProperties-s")); } + @Test + public void testSetNullValue() { + final Person james = Person.persistedPerson("James", 35); + james.setPersonalProperties(null); + assertEquals("empty properties after setting to null",true,james.getPersonalProperties().asMap().isEmpty()); + } Person createTestPerson() { Person p = persistedPerson("James", 36); p.setProperty("s", "String"); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/EntityPropertyValidationTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/EntityPropertyValidationTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/EntityPropertyValidationTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/EntityPropertyValidationTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/FinderTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/FinderTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/FinderTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/FinderTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/GraphDatabaseFactoryTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/GraphDatabaseFactoryTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/GraphDatabaseFactoryTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/GraphDatabaseFactoryTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/GraphRepositoryTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/GraphRepositoryTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/GraphRepositoryTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/GraphRepositoryTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/HasRelationshipMatcher.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/HasRelationshipMatcher.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/HasRelationshipMatcher.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/HasRelationshipMatcher.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/IndexTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/IndexTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/IndexTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/IndexTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/IndexingNodeTypeRepresentationStrategyTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/IndexingNodeTypeRepresentationStrategyTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/IndexingNodeTypeRepresentationStrategyTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/IndexingNodeTypeRepresentationStrategyTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/IndexingRelationshipTypeRepresentationStrategyTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/IndexingRelationshipTypeRepresentationStrategyTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/IndexingRelationshipTypeRepresentationStrategyTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/IndexingRelationshipTypeRepresentationStrategyTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/ModificationOutsideOfTransactionTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/ModificationOutsideOfTransactionTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/ModificationOutsideOfTransactionTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/ModificationOutsideOfTransactionTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityInstantiationTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityInstantiationTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityInstantiationTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityInstantiationTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityQueryTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityQueryTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityQueryTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityQueryTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityRelationshipTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityRelationshipTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityRelationshipTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityRelationshipTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NodeEntityTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NodeEntityTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/ProjectionTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/ProjectionTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/ProjectionTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/ProjectionTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/PropertyTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/PropertyTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/PropertyTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/PropertyTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/RelationshipEntityTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/RelationshipEntityTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/RelationshipEntityTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/RelationshipEntityTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/SubReferenceNodeTypeRepresentationStrategyTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/TestTeam.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/TestTeam.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/TestTeam.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/TestTeam.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/TraversalTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/TraversalTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/TraversalTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/TraversalTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/path/EntityMapperTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/path/EntityMapperTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/path/EntityMapperTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/path/EntityMapperTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/path/EntityPathTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/path/EntityPathTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/path/EntityPathTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/path/EntityPathTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/path/PathMappingIteratorTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/path/PathMappingIteratorTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/path/PathMappingIteratorTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/path/PathMappingIteratorTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/GremlinQueryEngineTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/query/GremlinQueryEngineTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/GremlinQueryEngineTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/query/GremlinQueryEngineTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java b/spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java rename to spring-data-neo4j-aspects/src/test/java/org/springframework/data/neo4j/support/query/QueryEngineTest.java diff --git a/spring-data-neo4j/src/test/resources/META-INF/persistence.xml b/spring-data-neo4j-aspects/src/test/resources/META-INF/persistence.xml similarity index 100% rename from spring-data-neo4j/src/test/resources/META-INF/persistence.xml rename to spring-data-neo4j-aspects/src/test/resources/META-INF/persistence.xml diff --git a/spring-data-neo4j/src/test/resources/META-INF/spring/database.properties b/spring-data-neo4j-aspects/src/test/resources/META-INF/spring/database.properties similarity index 100% rename from spring-data-neo4j/src/test/resources/META-INF/spring/database.properties rename to spring-data-neo4j-aspects/src/test/resources/META-INF/spring/database.properties diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/partial/Neo4jGraphRecommendationTest-context.xml b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/partial/Neo4jGraphRecommendationTest-context.xml similarity index 99% rename from spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/partial/Neo4jGraphRecommendationTest-context.xml rename to spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/partial/Neo4jGraphRecommendationTest-context.xml index 35664c0d2..73e48d1f1 100644 --- a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/partial/Neo4jGraphRecommendationTest-context.xml +++ b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/partial/Neo4jGraphRecommendationTest-context.xml @@ -123,7 +123,7 @@ - + diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/IndexingTypeRepresentationStrategyOverride-context.xml b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/IndexingTypeRepresentationStrategyOverride-context.xml similarity index 100% rename from spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/IndexingTypeRepresentationStrategyOverride-context.xml rename to spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/IndexingTypeRepresentationStrategyOverride-context.xml diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/Neo4jGraphPersistenceTest-context.xml b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/Neo4jGraphPersistenceTest-context.xml similarity index 99% rename from spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/Neo4jGraphPersistenceTest-context.xml rename to spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/Neo4jGraphPersistenceTest-context.xml index 30cb58500..12b25682c 100644 --- a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/Neo4jGraphPersistenceTest-context.xml +++ b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/Neo4jGraphPersistenceTest-context.xml @@ -118,7 +118,7 @@ - + diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyOverride-context.xml b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyOverride-context.xml similarity index 100% rename from spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyOverride-context.xml rename to spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/NoopTypeRepresentationStrategyOverride-context.xml diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/PersonDirectCreator-context.xml b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/PersonDirectCreator-context.xml similarity index 100% rename from spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/PersonDirectCreator-context.xml rename to spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/PersonDirectCreator-context.xml diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/SubReferenceTypeRepresentationStrategyOverride-context.xml b/spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/SubReferenceTypeRepresentationStrategyOverride-context.xml similarity index 100% rename from spring-data-neo4j/src/test/resources/org/springframework/data/neo4j/support/SubReferenceTypeRepresentationStrategyOverride-context.xml rename to spring-data-neo4j-aspects/src/test/resources/org/springframework/data/neo4j/support/SubReferenceTypeRepresentationStrategyOverride-context.xml diff --git a/spring-data-neo4j-cross-store/pom.xml b/spring-data-neo4j-cross-store/pom.xml new file mode 100644 index 000000000..2c5d2bf72 --- /dev/null +++ b/spring-data-neo4j-cross-store/pom.xml @@ -0,0 +1,231 @@ + + 4.0.0 + + org.springframework.data + spring-data-neo4j-parent + 2.0.0.BUILD-SNAPSHOT + ../spring-data-neo4j-parent/pom.xml + + spring-data-neo4j-cross-store + jar + Spring Data Neo4J Cross-Store Handling + + http://www.springsource.org/spring-data/neo4j + + + + org.springframework + spring-beans + + + org.springframework + spring-tx + + + org.springframework + spring-aspects + + + org.springframework + spring-orm + + + + + org.springframework.data + spring-data-commons-core + + + org.springframework.data + spring-data-neo4j-aspects + + + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + compile + + + org.slf4j + slf4j-log4j12 + runtime + + + log4j + log4j + + + javax.mail + mail + + + javax.jms + jms + + + com.sun.jdmk + jmxtools + + + com.sun.jmx + jmxri + + + runtime + + + + javax.annotation + jsr250-api + true + + + + org.mockito + mockito-all + test + + + + junit + junit + + + + org.aspectj + aspectjrt + ${aspectj.version} + + + + cglib + cglib + 2.2 + + + + org.neo4j + neo4j-kernel + test-jar + test + + + + commons-configuration + commons-configuration + + + + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + 1.0.0.Final + + + + + org.hibernate + hibernate-entitymanager + 3.5.5-Final + test + + + hsqldb + hsqldb + 1.8.0.10 + test + + + javax.validation + validation-api + 1.0.0.GA + + + org.hibernate + hibernate-validator + 4.0.2.GA + test + + + + + + neo4j-public-repository + Neo4J Public Repository + http://m2.neo4j.org + + true + + + + spring-maven-snapshot + + true + + Springframework Maven SNAPSHOT Repository + http://maven.springframework.org/snapshot + + + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.2 + + + org.aspectj + aspectjrt + ${aspectj.version} + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + compile + test-compile + + + + + true + + + org.springframework + spring-aspects + + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + com.springsource.bundlor + com.springsource.bundlor.maven + + + + + diff --git a/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/config/CrossStoreNeo4jConfiguration.java b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/config/CrossStoreNeo4jConfiguration.java new file mode 100644 index 000000000..c6d638fef --- /dev/null +++ b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/config/CrossStoreNeo4jConfiguration.java @@ -0,0 +1,74 @@ +package org.springframework.data.neo4j.config; + +import org.neo4j.graphdb.Node; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.support.EntityInstantiator; +import org.springframework.data.neo4j.support.node.CrossStoreNodeEntityStateFactory; +import org.springframework.data.neo4j.support.node.NodeEntityInstantiator; +import org.springframework.data.neo4j.support.node.NodeEntityStateFactory; +import org.springframework.data.neo4j.support.node.PartialNodeEntityInstantiator; +import org.springframework.data.neo4j.transaction.ChainedTransactionManager; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.jta.JtaTransactionManager; + +import javax.persistence.EntityManagerFactory; + +/** + * @author mh + * @since 30.09.11 + */ +@Configuration +public class CrossStoreNeo4jConfiguration extends Neo4jAspectConfiguration { + + private EntityManagerFactory entityManagerFactory; + + public EntityManagerFactory getEntityManagerFactory() { + return entityManagerFactory; + } + + @Qualifier("&entityManagerFactory") + @Autowired(required = false) + public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + public boolean isUsingCrossStorePersistence() { + return entityManagerFactory != null; + } + + @Bean + protected EntityInstantiator graphEntityInstantiator() { + if (isUsingCrossStorePersistence()) { + return new PartialNodeEntityInstantiator(new NodeEntityInstantiator(mappingContext), entityManagerFactory); + } else { + return new NodeEntityInstantiator(mappingContext); + } + } + + @Bean + public PlatformTransactionManager transactionManager() { + if (isUsingCrossStorePersistence()) { + JpaTransactionManager jpaTm = new JpaTransactionManager(getEntityManagerFactory()); + JtaTransactionManager jtaTm = createJtaTransactionManager(); + return new ChainedTransactionManager(jpaTm, jtaTm); + } + else { + return createJtaTransactionManager(); + } + } + + @Bean + public NodeEntityStateFactory nodeEntityStateFactory() throws Exception { + + CrossStoreNodeEntityStateFactory entityStateFactory = new CrossStoreNodeEntityStateFactory(); + entityStateFactory.setGraphDatabaseContext(graphDatabaseContext()); + entityStateFactory.setEntityManagerFactory(entityManagerFactory); + entityStateFactory.setMappingContext(mappingContext()); + entityStateFactory.setNodeDelegatingFieldAccessorFactory(nodeDelegatingFieldAccessorFactory()); + return entityStateFactory; + } +} diff --git a/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/CrossStoreNodeEntityStateFactory.java b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/CrossStoreNodeEntityStateFactory.java new file mode 100644 index 000000000..ec8d5646f --- /dev/null +++ b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/CrossStoreNodeEntityStateFactory.java @@ -0,0 +1,55 @@ +package org.springframework.data.neo4j.support.node; + +import org.neo4j.graphdb.Node; +import org.springframework.data.neo4j.annotation.NodeEntity; +import org.springframework.data.neo4j.core.EntityState; +import org.springframework.data.neo4j.core.NodeBacked; +import org.springframework.data.neo4j.fieldaccess.DetachedEntityState; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; + +import javax.annotation.PostConstruct; +import javax.persistence.EntityManagerFactory; +import javax.persistence.PersistenceUnitUtil; + +/** + * @author mh + * @since 30.09.11 + */ +public class CrossStoreNodeEntityStateFactory extends NodeEntityStateFactory { + private PartialNodeEntityState.PartialNodeDelegatingFieldAccessorFactory delegatingFieldAccessorFactory; + private EntityManagerFactory entityManagerFactory; + + public EntityState getEntityState(final NodeBacked entity) { + final Class entityType = entity.getClass(); + final NodeEntity graphEntityAnnotation = entityType.getAnnotation(NodeEntity.class); // todo cache ?? + final Neo4jPersistentEntity persistentEntity = mappingContext.getPersistentEntity(entityType); + if (graphEntityAnnotation.partial()) { + final PartialNodeEntityState partialNodeEntityState = new PartialNodeEntityState(null, entity, entityType, graphDatabaseContext, getPersistenceUnitUtils(), delegatingFieldAccessorFactory, (Neo4jPersistentEntity) persistentEntity); + return new DetachedEntityState(partialNodeEntityState, graphDatabaseContext) { + @Override + protected boolean isDetached() { + return super.isDetached() || partialNodeEntityState.getId(entity) == null; + } + }; + } else { + NodeEntityState nodeEntityState = new NodeEntityState(null, entity, entityType, graphDatabaseContext, nodeDelegatingFieldAccessorFactory, (Neo4jPersistentEntity) persistentEntity); + // alternative was return new NestedTransactionEntityState(nodeEntityState,graphDatabaseContext); + return new DetachedEntityState(nodeEntityState, graphDatabaseContext); + } + } + + private PersistenceUnitUtil getPersistenceUnitUtils() { + if (entityManagerFactory == null) return null; + return entityManagerFactory.getPersistenceUnitUtil(); + } + + public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { + this.entityManagerFactory = entityManagerFactory; + } + + @PostConstruct + private void setUp() { + this.delegatingFieldAccessorFactory = new PartialNodeEntityState.PartialNodeDelegatingFieldAccessorFactory(graphDatabaseContext); + } + +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityInstantiator.java b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityInstantiator.java similarity index 91% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityInstantiator.java rename to spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityInstantiator.java index f6dc0c584..f74252785 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityInstantiator.java +++ b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityInstantiator.java @@ -17,9 +17,8 @@ package org.springframework.data.neo4j.support.node; import org.neo4j.graphdb.Node; -import org.springframework.data.neo4j.core.NodeBacked; +import org.springframework.data.neo4j.support.EntityInstantiator; import org.springframework.orm.jpa.EntityManagerFactoryUtils; -import org.springframework.data.persistence.EntityInstantiator; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; @@ -30,7 +29,7 @@ import javax.persistence.EntityManagerFactory; * @author Michael Hunger * @since 02.10.2010 */ -public class PartialNodeEntityInstantiator implements EntityInstantiator { +public class PartialNodeEntityInstantiator implements EntityInstantiator { private final NodeEntityInstantiator delegate; private EntityManagerFactory entityManagerFactory; @@ -50,7 +49,7 @@ public class PartialNodeEntityInstantiator implements EntityInstantiator generic type of the entity * @return */ - public T createEntityFromState(Node n, Class entityClass) { + public T createEntityFromState(Node n, Class entityClass) { if (n.hasProperty(PartialNodeEntityState.FOREIGN_ID)) { final Object foreignId = n.getProperty(PartialNodeEntityState.FOREIGN_ID); final T result = entityManager().find(entityClass, foreignId); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityState.java b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityState.java similarity index 91% rename from spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityState.java rename to spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityState.java index 2824d5611..5090f638b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityState.java +++ b/spring-data-neo4j-cross-store/src/main/java/org/springframework/data/neo4j/support/node/PartialNodeEntityState.java @@ -25,8 +25,8 @@ import org.springframework.data.neo4j.annotation.GraphProperty; import org.springframework.data.neo4j.annotation.RelatedTo; import org.springframework.data.neo4j.core.NodeBacked; import org.springframework.data.neo4j.fieldaccess.*; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; import javax.persistence.PersistenceUnitUtil; @@ -46,7 +46,7 @@ public class PartialNodeEntityState extends DefaultEn private final GraphDatabaseContext graphDatabaseContext; private PersistenceUnitUtil persistenceUnitUtil; - public PartialNodeEntityState(final Node underlyingState, final ENTITY entity, final Class type, final GraphDatabaseContext graphDatabaseContext, PersistenceUnitUtil persistenceUnitUtil, final PartialNodeDelegatingFieldAccessorFactory delegatingFieldAccessorFactory, final Neo4JPersistentEntity persistentEntity) { + public PartialNodeEntityState(final Node underlyingState, final ENTITY entity, final Class type, final GraphDatabaseContext graphDatabaseContext, PersistenceUnitUtil persistenceUnitUtil, final PartialNodeDelegatingFieldAccessorFactory delegatingFieldAccessorFactory, final Neo4jPersistentEntity persistentEntity) { super(underlyingState, entity, type, delegatingFieldAccessorFactory, persistentEntity); this.graphDatabaseContext = graphDatabaseContext; this.persistenceUnitUtil = persistenceUnitUtil; @@ -126,11 +126,11 @@ public class PartialNodeEntityState extends DefaultEn newPropertyFieldAccessorFactory(), newConvertingNodePropertyFieldAccessorFactory()) { @Override - public boolean accept(Neo4JPersistentProperty property) { + public boolean accept(Neo4jPersistentProperty property) { return property.isAnnotationPresent(GraphProperty.class) && super.accept(property); } }, - new JpaIdFieldAccessListenerFactory()); + new JpaIdFieldAccessListenerFactory(graphDatabaseContext)); } @Override @@ -144,7 +144,7 @@ public class PartialNodeEntityState extends DefaultEn newConvertingNodePropertyFieldAccessorFactory(), new SingleRelationshipFieldAccessorFactory(getGraphDatabaseContext()) { @Override - public boolean accept(Neo4JPersistentProperty property) { + public boolean accept(Neo4jPersistentProperty property) { return property.isAnnotationPresent(RelatedTo.class) && super.accept(property); } }, @@ -157,8 +157,8 @@ public class PartialNodeEntityState extends DefaultEn private ConvertingNodePropertyFieldAccessorFactory newConvertingNodePropertyFieldAccessorFactory() { return new ConvertingNodePropertyFieldAccessorFactory(getGraphDatabaseContext().getConversionService()) { @Override - public boolean accept(Neo4JPersistentProperty f) { - return f.isAnnotationPresent(GraphProperty.class) && super.accept(f); + public boolean accept(Neo4jPersistentProperty property) { + return property.isAnnotationPresent(GraphProperty.class) && super.accept(property); } }; } @@ -166,7 +166,7 @@ public class PartialNodeEntityState extends DefaultEn private PropertyFieldAccessorFactory newPropertyFieldAccessorFactory() { return new PropertyFieldAccessorFactory(getGraphDatabaseContext().getConversionService()) { @Override - public boolean accept(Neo4JPersistentProperty f) { + public boolean accept(Neo4jPersistentProperty f) { return f.isAnnotationPresent(GraphProperty.class) && super.accept(f); } }; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/Cuisine.java b/spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/Cuisine.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/Cuisine.java rename to spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/Cuisine.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/Recommendation.java b/spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/Recommendation.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/Recommendation.java rename to spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/Recommendation.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/RecommendationTest.java b/spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/RecommendationTest.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/RecommendationTest.java rename to spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/RecommendationTest.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/Restaurant.java b/spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/Restaurant.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/Restaurant.java rename to spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/Restaurant.java diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/User.java b/spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/User.java similarity index 100% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/partial/User.java rename to spring-data-neo4j-cross-store/src/test/java/org/springframework/data/neo4j/partial/User.java diff --git a/spring-data-neo4j-examples/hello-worlds/.idea/workspace.xml b/spring-data-neo4j-examples/hello-worlds/.idea/workspace.xml index 36675cc94..e0b4e25e1 100644 --- a/spring-data-neo4j-examples/hello-worlds/.idea/workspace.xml +++ b/spring-data-neo4j-examples/hello-worlds/.idea/workspace.xml @@ -149,7 +149,7 @@ - + @@ -608,7 +608,7 @@ - + diff --git a/spring-data-neo4j-parent/pom.xml b/spring-data-neo4j-parent/pom.xml index 1a91d9aae..d8ed36bb9 100644 --- a/spring-data-neo4j-parent/pom.xml +++ b/spring-data-neo4j-parent/pom.xml @@ -123,7 +123,7 @@ 1.2.15 1.8.4 1.6.1 - 3.0.5.RELEASE + 3.0.6.RELEASE 1.2.0.BUILD-SNAPSHOT 1.4.1 1.6.12.M1 @@ -284,6 +284,11 @@ spring-data-neo4j ${project.version} + + org.springframework.data + spring-data-neo4j-aspects + ${project.version} + diff --git a/spring-data-neo4j-rest/pom.xml b/spring-data-neo4j-rest/pom.xml index edc44e56f..a83a77817 100644 --- a/spring-data-neo4j-rest/pom.xml +++ b/spring-data-neo4j-rest/pom.xml @@ -31,7 +31,7 @@ org.springframework.data - spring-data-neo4j + spring-data-neo4j-aspects ${project.version} test test-jar diff --git a/spring-data-neo4j/pom.xml b/spring-data-neo4j/pom.xml index a015e1900..10d52f142 100644 --- a/spring-data-neo4j/pom.xml +++ b/spring-data-neo4j/pom.xml @@ -9,7 +9,7 @@ spring-data-neo4j jar - Spring Data Neo4J Core + Spring Data Neo4j http://www.springsource.org/spring-data/neo4j @@ -210,42 +210,6 @@ - - org.codehaus.mojo - aspectj-maven-plugin - 1.2 - - - org.aspectj - aspectjrt - ${aspectj.version} - - - org.aspectj - aspectjtools - ${aspectj.version} - - - - - - compile - test-compile - - - - - true - - - org.springframework - spring-aspects - - - 1.6 - 1.6 - - org.apache.maven.plugins maven-jar-plugin diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphTraversal.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphTraversal.java index 76463eb38..f6e876e5f 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphTraversal.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/GraphTraversal.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder; -import org.springframework.data.neo4j.core.NodeBacked; + /** * Field that provides access to an iterator which is created by applying the traversal that is built by the supplied @@ -41,12 +41,13 @@ public @interface GraphTraversal { /** * @return Builder for the {@link org.neo4j.graphdb.traversal.TraversalDescription} to be applied */ - Class traversalBuilder() default FieldTraversalDescriptionBuilder.class; + // FQN is a fix for javac compiler bug + Class traversalBuilder() default org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder.class; /** * @return target graph entity to be iterated over. */ - Class elementClass() default NodeBacked.class; + Class elementClass() default Object.class; /** * @return parameters that are passed to the @see FieldTraversalDescriptionBuilder#build diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Indexed.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Indexed.java index 2f63e8e49..cec054f6b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Indexed.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Indexed.java @@ -38,7 +38,8 @@ public @interface Indexed { String fieldName() default ""; - Level level() default Level.CLASS; + // FQN is a fix for javac compiler bug http://bugs.sun.com/view_bug.do?bug_id=6512707 + org.springframework.data.neo4j.annotation.Indexed.Level level() default org.springframework.data.neo4j.annotation.Indexed.Level.CLASS; static class Name { public static String getDefault(Field field) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Query.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Query.java index f7474592e..f732c367a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Query.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/Query.java @@ -50,5 +50,6 @@ public @interface Query { */ String[] params() default {}; - QueryType type() default QueryType.Cypher; + // FQN is a fix for javac compiler bug + org.springframework.data.neo4j.annotation.QueryType type() default org.springframework.data.neo4j.annotation.QueryType.Cypher; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedTo.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedTo.java index 0766cb0d9..8456ee39b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedTo.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedTo.java @@ -18,7 +18,7 @@ package org.springframework.data.neo4j.annotation; import org.neo4j.graphdb.Direction; import org.springframework.data.annotation.Reference; -import org.springframework.data.neo4j.core.NodeBacked; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -55,10 +55,11 @@ public @interface RelatedTo { /** * @return direction for the relationship, by default outgoing */ - Direction direction() default Direction.OUTGOING; + // FQN is a fix for javac compiler bug http://bugs.sun.com/view_bug.do?bug_id=6512707 + org.neo4j.graphdb.Direction direction() default org.neo4j.graphdb.Direction.OUTGOING; /** * @return target class, required for collection based fields (no generic inferring) */ - Class elementClass() default NodeBacked.class; + Class elementClass() default Object.class; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedToVia.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedToVia.java index ba341830d..3b5114a33 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedToVia.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelatedToVia.java @@ -23,7 +23,7 @@ import java.lang.annotation.Target; import org.neo4j.graphdb.Direction; import org.springframework.data.annotation.Reference; -import org.springframework.data.neo4j.core.RelationshipBacked; + /** * Annotation for {@link org.springframework.data.neo4j.annotation.NodeEntity} fields that relate to other entities via @@ -49,10 +49,11 @@ public @interface RelatedToVia { /** * @return direction for the relationship, by default outgoing */ - Direction direction() default Direction.OUTGOING; + // FQN is a fix for javac compiler bug http://bugs.sun.com/view_bug.do?bug_id=6512707 + org.neo4j.graphdb.Direction direction() default org.neo4j.graphdb.Direction.OUTGOING; /** * @return target relationship entity class */ - Class elementClass() default RelationshipBacked.class; + Class elementClass() default Object.class; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelationshipType.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelationshipType.java new file mode 100644 index 000000000..5f7f60b4b --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/annotation/RelationshipType.java @@ -0,0 +1,33 @@ +/** + * 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Field annotation for the type of a relationship entity. The field type must be a String or RelationshipType. + * + * @author Michael Hunger + * @since 27.08.2010 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface RelationshipType { +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java index 909362b48..805956da3 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/Neo4jConfiguration.java @@ -16,7 +16,6 @@ package org.springframework.data.neo4j.config; -import javax.persistence.EntityManagerFactory; import javax.validation.Validator; import org.neo4j.graphdb.GraphDatabaseService; @@ -26,32 +25,27 @@ import org.neo4j.kernel.AbstractGraphDatabase; import org.neo4j.kernel.impl.transaction.SpringTransactionManager; import org.neo4j.kernel.impl.transaction.UserTransactionImpl; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.ConversionService; import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory; 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.mapping.Neo4JMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jNodeConverterImpl; import org.springframework.data.neo4j.repository.DirectGraphRepositoryFactory; +import org.springframework.data.neo4j.support.EntityInstantiator; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import org.springframework.data.neo4j.support.node.Neo4jNodeBacking; import org.springframework.data.neo4j.support.node.NodeEntityInstantiator; import org.springframework.data.neo4j.support.node.NodeEntityStateFactory; -import org.springframework.data.neo4j.support.node.PartialNodeEntityInstantiator; -import org.springframework.data.neo4j.support.relationship.Neo4jRelationshipBacking; import org.springframework.data.neo4j.support.relationship.RelationshipEntityInstantiator; import org.springframework.data.neo4j.support.relationship.RelationshipEntityStateFactory; import org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory; import org.springframework.data.neo4j.template.Neo4jExceptionTranslator; -import org.springframework.data.neo4j.transaction.ChainedTransactionManager; -import org.springframework.data.persistence.EntityInstantiator; -import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.transaction.jta.UserTransactionAdapter; @@ -63,7 +57,7 @@ import org.springframework.transaction.jta.UserTransactionAdapter; * @author Thomas Risberg */ @Configuration -public class Neo4jConfiguration { +public abstract class Neo4jConfiguration { private GraphDatabaseService graphDatabaseService; @Autowired(required = false) @@ -79,26 +73,10 @@ public class Neo4jConfiguration { this.graphDatabaseService = graphDatabaseService; } - private EntityManagerFactory entityManagerFactory; - - public EntityManagerFactory getEntityManagerFactory() { - return entityManagerFactory; - } - - @Qualifier("&entityManagerFactory") - @Autowired(required = false) - public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { - this.entityManagerFactory = entityManagerFactory; - } - - public boolean isUsingCrossStorePersistence() { - return entityManagerFactory != null; - } - - @Bean + @Bean public GraphDatabaseContext graphDatabaseContext() throws Exception { - EntityInstantiator relationshipEntityInstantiator = graphRelationshipInstantiator(); - EntityInstantiator graphEntityInstantiator = graphEntityInstantiator(); + EntityInstantiator relationshipEntityInstantiator = graphRelationshipInstantiator(); + EntityInstantiator graphEntityInstantiator = graphEntityInstantiator(); TypeRepresentationStrategyFactory typeRepresentationStrategyFactory = new TypeRepresentationStrategyFactory(graphDatabaseService, graphEntityInstantiator, relationshipEntityInstantiator); @@ -106,6 +84,8 @@ public class Neo4jConfiguration { GraphDatabaseContext gdc = new GraphDatabaseContext(); gdc.setGraphDatabaseService(getGraphDatabaseService()); gdc.setConversionService(conversionService()); + gdc.setMappingContext(mappingContext()); + gdc.setConverter(neo4jConverter()); gdc.setNodeTypeRepresentationStrategy(typeRepresentationStrategyFactory.getNodeTypeRepresentationStrategy()); gdc.setRelationshipTypeRepresentationStrategy(typeRepresentationStrategyFactory.getRelationshipTypeRepresentationStrategy()); if (validator!=null) { @@ -114,6 +94,11 @@ public class Neo4jConfiguration { return gdc; } + @Bean + public Neo4jNodeConverterImpl neo4jConverter() throws Exception { + return new Neo4jNodeConverterImpl(nodeEntityStateFactory()); + } + @Bean protected ConversionService conversionService() throws Exception { return new Neo4jConversionServiceFactoryBean().getObject(); @@ -121,30 +106,18 @@ public class Neo4jConfiguration { @Bean protected RelationshipEntityInstantiator graphRelationshipInstantiator() { - return new RelationshipEntityInstantiator(); + return new RelationshipEntityInstantiator(mappingContext()); } @Bean - protected EntityInstantiator graphEntityInstantiator() { - if (isUsingCrossStorePersistence()) { - return new PartialNodeEntityInstantiator(new NodeEntityInstantiator(), entityManagerFactory); - } else { - return new NodeEntityInstantiator(); - } + protected EntityInstantiator graphEntityInstantiator() { + return new NodeEntityInstantiator(mappingContext()); } @Bean public DirectGraphRepositoryFactory directGraphRepositoryFactory() throws Exception { return new DirectGraphRepositoryFactory(graphDatabaseContext()); } - - @Bean - public Neo4jRelationshipBacking neo4jRelationshipBacking() throws Exception { - Neo4jRelationshipBacking aspect = Neo4jRelationshipBacking.aspectOf(); - aspect.setGraphDatabaseContext(graphDatabaseContext()); - aspect.setRelationshipEntityStateFactory(relationshipEntityStateFactory()); - return aspect; - } @Bean public RelationshipEntityStateFactory relationshipEntityStateFactory() throws Exception { @@ -156,17 +129,8 @@ public class Neo4jConfiguration { } @Bean - public Neo4jNodeBacking neo4jNodeBacking() throws Exception { - Neo4jNodeBacking aspect = Neo4jNodeBacking.aspectOf(); - aspect.setGraphDatabaseContext(graphDatabaseContext()); - NodeEntityStateFactory entityStateFactory = nodeEntityStateFactory(); - aspect.setNodeEntityStateFactory(entityStateFactory); - return aspect; - } - - @Bean - public Neo4JMappingContext mappingContext() { - return new Neo4JMappingContext(); + public Neo4jMappingContext mappingContext() { + return new Neo4jMappingContext(); } @Bean @@ -174,35 +138,27 @@ public class Neo4jConfiguration { NodeEntityStateFactory entityStateFactory = new NodeEntityStateFactory(); entityStateFactory.setGraphDatabaseContext(graphDatabaseContext()); - entityStateFactory.setEntityManagerFactory(entityManagerFactory); entityStateFactory.setMappingContext(mappingContext()); entityStateFactory.setNodeDelegatingFieldAccessorFactory(nodeDelegatingFieldAccessorFactory()); return entityStateFactory; } @Bean - public DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory() throws Exception { + public DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory() throws Exception { return new NodeDelegatingFieldAccessorFactory(graphDatabaseContext()); } @Bean - public DelegatingFieldAccessorFactory relationshipDelegatingFieldAccessorFactory() throws Exception { + public DelegatingFieldAccessorFactory relationshipDelegatingFieldAccessorFactory() throws Exception { return new RelationshipDelegatingFieldAccessorFactory(graphDatabaseContext()); } @Bean public PlatformTransactionManager transactionManager() { - if (isUsingCrossStorePersistence()) { - JpaTransactionManager jpaTm = new JpaTransactionManager(getEntityManagerFactory()); - JtaTransactionManager jtaTm = createJtaTransactionManager(); - return new ChainedTransactionManager(jpaTm, jtaTm); - } - else { - return createJtaTransactionManager(); - } + return createJtaTransactionManager(); } - private JtaTransactionManager createJtaTransactionManager() { + protected JtaTransactionManager createJtaTransactionManager() { JtaTransactionManager jtaTm = new JtaTransactionManager(); final GraphDatabaseService gds = getGraphDatabaseService(); if (gds instanceof AbstractGraphDatabase) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityPath.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityPath.java index 265d9d2f0..c226e3a55 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityPath.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityPath.java @@ -17,22 +17,22 @@ package org.springframework.data.neo4j.core; import org.neo4j.graphdb.Path; -import org.springframework.data.neo4j.core.RelationshipBacked; + /** * @author mh * @since 08.04.11 */ -public interface EntityPath extends Path { - T startEntity(Class... types); +public interface EntityPath extends Path { + T startEntity(Class... types); - T endEntity(Class... types); + T endEntity(Class... types); - T lastRelationshipEntity(Class... types); + T lastRelationshipEntity(Class... types); - Iterable nodeEntities(); + Iterable nodeEntities(); - Iterable relationshipEntities(Class... relationships); + Iterable relationshipEntities(Class... relationships); - Iterable allPathEntities(Class... relationships); + Iterable allPathEntities(Class... relationships); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityState.java index d2cd24560..49baca425 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/EntityState.java @@ -19,8 +19,8 @@ package org.springframework.data.neo4j.core; import java.lang.reflect.Field; import org.springframework.data.neo4j.fieldaccess.FieldAccessor; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; /** * Interface for classes encapsulating and delegating read and write field access of an GraphBacked entity to a number of field accessors. @@ -28,8 +28,8 @@ import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; * @author Michael Hunger * @since 15.09.2010 */ -public interface EntityState,STATE> { - ENTITY getEntity(); +public interface EntityState { + Object getEntity(); void setPersistentState(STATE state); @@ -58,7 +58,7 @@ public interface EntityState,STATE> { * @return sets the value in the entity and/or the state */ Object setValue(Field field, Object newVal); - Object setValue(Neo4JPersistentProperty property, Object newVal); + Object setValue(Neo4jPersistentProperty property, Object newVal); /** * callback for creating and initializing an initial state @@ -70,7 +70,7 @@ public interface EntityState,STATE> { boolean hasPersistentState(); STATE getPersistentState(); - ENTITY persist(); + Object persist(); - Neo4JPersistentEntity getPersistentEntity(); + Neo4jPersistentEntity getPersistentEntity(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/FieldTraversalDescriptionBuilder.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/FieldTraversalDescriptionBuilder.java index 47487bb84..618772ac0 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/FieldTraversalDescriptionBuilder.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/FieldTraversalDescriptionBuilder.java @@ -17,7 +17,7 @@ package org.springframework.data.neo4j.core; import org.neo4j.graphdb.traversal.TraversalDescription; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; /** * Interface for classes that build traversal descriptions. Those classes can be referred to by @@ -35,5 +35,5 @@ public interface FieldTraversalDescriptionBuilder { * @param property * @return the TraversalDescription to apply on fieldaccess, the start node is the current entity node */ - TraversalDescription build(NodeBacked start, Neo4JPersistentProperty property, String...params); + TraversalDescription build(Object start, Neo4jPersistentProperty property, String...params); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeTypeRepresentationStrategy.java index 18f9d10c1..9b9a73a12 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeTypeRepresentationStrategy.java @@ -18,6 +18,6 @@ package org.springframework.data.neo4j.core; import org.neo4j.graphdb.Node; -public interface NodeTypeRepresentationStrategy extends TypeRepresentationStrategy { +public interface NodeTypeRepresentationStrategy extends TypeRepresentationStrategy { } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/RelationshipTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/RelationshipTypeRepresentationStrategy.java index 1b74cd747..d48fc592b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/RelationshipTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/RelationshipTypeRepresentationStrategy.java @@ -18,6 +18,6 @@ package org.springframework.data.neo4j.core; import org.neo4j.graphdb.Relationship; -public interface RelationshipTypeRepresentationStrategy extends TypeRepresentationStrategy { +public interface RelationshipTypeRepresentationStrategy extends TypeRepresentationStrategy { } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/TypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/TypeRepresentationStrategy.java index 65c4aa5ba..4cde5b506 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/TypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/TypeRepresentationStrategy.java @@ -32,33 +32,33 @@ import org.neo4j.helpers.collection.ClosableIterable; * @author Michael Hunger * @since 13.09.2010 */ -public interface TypeRepresentationStrategy> { +public interface TypeRepresentationStrategy { /** * Callback for setting up and/or storing type information after creation. * * @param state Backing state of entity being created * @param type Type of entity being created */ - void postEntityCreation(S state, Class type); + void postEntityCreation(S state, Class type); /** * * @param clazz Type whose instances should be iterated over * @return lazy Iterable over all instances of the given type */ - ClosableIterable findAll(final Class clazz); + ClosableIterable findAll(final Class clazz); /** * @param entityClass * @return number of instances of this class contained in the graph */ - long count(final Class entityClass); + long count(final Class entityClass); /** * @param state * @return java type that of the node entity of this node */ - Class getJavaType(S state); + Class getJavaType(S state); /** * Callback for cleaning up type information before removal. If state does not have any @@ -77,7 +77,7 @@ public interface TypeRepresentationStrategy U createEntity(S state) throws IllegalStateException; + U createEntity(S state) throws IllegalStateException; /** * Instantiate the entity given its state. The type of the desired entity is also specified. @@ -90,7 +90,7 @@ public interface TypeRepresentationStrategy U createEntity(S state, Class type) throws IllegalStateException, IllegalArgumentException; + U createEntity(S state, Class type) throws IllegalStateException, IllegalArgumentException; /** * Instantiate the entity of the given type, with the given state as backing state. No checking @@ -100,5 +100,5 @@ public interface TypeRepresentationStrategy U projectEntity(S state, Class type); + U projectEntity(S state, Class type); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java index 2502ca1b6..35787ca81 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/AbstractNodeRelationshipFieldAccessor.java @@ -18,9 +18,10 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.*; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; +import org.springframework.util.Assert; import java.util.HashSet; import java.util.Set; @@ -29,14 +30,14 @@ import java.util.Set; * @author Michael Hunger * @since 11.09.2010 */ -public abstract class AbstractNodeRelationshipFieldAccessor implements FieldAccessor { +public abstract class AbstractNodeRelationshipFieldAccessor implements FieldAccessor { protected final RelationshipType type; - protected final Neo4JPersistentProperty property; + protected final Neo4jPersistentProperty property; protected final Direction direction; - protected final Class relatedType; + protected final Class relatedType; protected final GraphDatabaseContext graphDatabaseContext; - public AbstractNodeRelationshipFieldAccessor(Class clazz, GraphDatabaseContext graphDatabaseContext, Direction direction, RelationshipType type, Neo4JPersistentProperty property) { + public AbstractNodeRelationshipFieldAccessor(Class clazz, GraphDatabaseContext graphDatabaseContext, Direction direction, RelationshipType type, Neo4jPersistentProperty property) { this.relatedType = clazz; this.graphDatabaseContext = graphDatabaseContext; this.direction = direction; @@ -45,11 +46,11 @@ public abstract class AbstractNodeRelationshipFieldAccessor checkTargetIsSetOfNodebacked(Object newVal) { if (!(newVal instanceof Set)) { throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass()); @@ -78,20 +79,30 @@ public abstract class AbstractNodeRelationshipFieldAccessor createManagedSet(ENTITY entity, Set result) { - return new ManagedFieldAccessorSet(entity, result, property); + protected STATE getOrCreateState(Object value) { + final STATE state = getState(value); + if (state != null) return state; + final Object saved = graphDatabaseContext.save(value); + final STATE newState = getState(saved); + Assert.notNull(newState); + return newState; } - protected Set createEntitySetFromRelationshipEndNodes(ENTITY entity) { + protected ManagedFieldAccessorSet createManagedSet(Object entity, Set result) { + return new ManagedFieldAccessorSet(entity, result, property,graphDatabaseContext,this); + } + + protected Set createEntitySetFromRelationshipEndNodes(Object entity) { final Iterable nodes = getStatesFromEntity(entity); - final Set result = new HashSet(); + final Set result = new HashSet(); for (final TSTATE otherNode : nodes) { - TARGET target=graphDatabaseContext.createEntityFromState(otherNode, relatedType); + Object target=graphDatabaseContext.createEntityFromState(otherNode, relatedType); result.add(target); } return result; @@ -119,7 +130,8 @@ public abstract class AbstractNodeRelationshipFieldAccessor getStatesFromEntity(ENTITY entity); + protected abstract Iterable getStatesFromEntity(Object entity); - protected abstract STATE getState(ENTITY entity); + protected abstract STATE + getState(Object entity); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ConvertingNodePropertyFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ConvertingNodePropertyFieldAccessorFactory.java index 0ddf6def3..75f2e53e1 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ConvertingNodePropertyFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ConvertingNodePropertyFieldAccessorFactory.java @@ -16,62 +16,62 @@ package org.springframework.data.neo4j.fieldaccess; -import org.neo4j.graphdb.PropertyContainer; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.core.convert.ConversionService; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; -import scala.annotation.target.field; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * @author Michael Hunger * @since 12.09.2010 */ @Configurable -public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessorFactory> { +public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessorFactory { - ConversionService conversionService; - - public ConvertingNodePropertyFieldAccessorFactory(ConversionService conversionService) { - this.conversionService = conversionService; - } + + private final GraphDatabaseContext graphDatabaseContext; + private ConversionService conversionService; + + public ConvertingNodePropertyFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; + this.conversionService = graphDatabaseContext.getConversionService(); + } @Override - public boolean accept(final Neo4JPersistentProperty field) { - return field.isSerializableField(conversionService) && field.isDeserializableField(conversionService); + public boolean accept(final Neo4jPersistentProperty property) { + return property.isSerializableField(conversionService) && property.isDeserializableField(conversionService); } @Override - public FieldAccessor> forField(final Neo4JPersistentProperty property) { - return new ConvertingNodePropertyFieldAccessor(conversionService,property); + public FieldAccessor forField(final Neo4jPersistentProperty property) { + return new ConvertingNodePropertyFieldAccessor(property,graphDatabaseContext); } public static class ConvertingNodePropertyFieldAccessor extends PropertyFieldAccessorFactory.PropertyFieldAccessor { - private final ConversionService conversionService; - public ConvertingNodePropertyFieldAccessor(ConversionService conversionService, Neo4JPersistentProperty property) { - super(conversionService, property); - this.conversionService = conversionService; + public ConvertingNodePropertyFieldAccessor(Neo4jPersistentProperty property, GraphDatabaseContext graphDatabaseContext) { + super(graphDatabaseContext, property); } @Override - public Object setValue(final GraphBacked graphBacked, final Object newVal) { - super.setValue(graphBacked, serializePropertyValue(newVal)); + public Object setValue(final Object entity, final Object newVal) { + super.setValue(entity, serializePropertyValue(newVal)); return newVal; } @Override - public Object doGetValue(final GraphBacked graphBacked) { - return deserializePropertyValue(super.doGetValue(graphBacked)); + public Object doGetValue(final Object entity) { + return deserializePropertyValue(super.doGetValue(entity)); } private Object serializePropertyValue(final Object newVal) { - return conversionService.convert(newVal, String.class); + return graphDatabaseContext.getConversionService().convert(newVal, String.class); } private Object deserializePropertyValue(final Object value) { - return conversionService.convert(value, fieldType); + return graphDatabaseContext.getConversionService().convert(value, fieldType); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DefaultEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DefaultEntityState.java index 0043b4df7..345df25e0 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DefaultEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DefaultEntityState.java @@ -19,9 +19,9 @@ package org.springframework.data.neo4j.fieldaccess; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.data.neo4j.core.EntityState; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import java.lang.reflect.Field; import java.util.HashMap; @@ -32,17 +32,17 @@ import java.util.Map; * @author Michael Hunger * @since 12.09.2010 */ -public abstract class DefaultEntityState, STATE> implements EntityState { - protected final ENTITY entity; - protected final Class type; - private final Map> fieldAccessors = new HashMap>(); - private final Map>> fieldAccessorListeners = new HashMap>>(); +public abstract class DefaultEntityState implements EntityState { + protected final Object entity; + protected final Class type; + private final Map fieldAccessors = new HashMap(); + private final Map> fieldAccessorListeners = new HashMap>(); private STATE state; protected final static Log log= LogFactory.getLog(DefaultEntityState.class); - private final FieldAccessorFactoryProviders fieldAccessorFactoryProviders; - private final Neo4JPersistentEntity persistentEntity; + private final FieldAccessorFactoryProviders fieldAccessorFactoryProviders; + private final Neo4jPersistentEntity persistentEntity; - public DefaultEntityState(final STATE underlyingState, final ENTITY entity, final Class type, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4JPersistentEntity persistentEntity) { + public DefaultEntityState(final STATE underlyingState, final Object entity, final Class type, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4jPersistentEntity persistentEntity) { this.state = underlyingState; this.entity = entity; this.type = type; @@ -60,7 +60,7 @@ public abstract class DefaultEntityState, STAT public abstract void createAndAssignState(); @Override - public ENTITY getEntity() { + public Object getEntity() { return entity; } @@ -79,20 +79,20 @@ public abstract class DefaultEntityState, STAT return state; } - public Neo4JPersistentEntity getPersistentEntity() { + public Neo4jPersistentEntity getPersistentEntity() { return persistentEntity; } @Override public boolean isWritable(Field field) { - final FieldAccessor accessor = accessorFor(property(field)); + final FieldAccessor accessor = accessorFor(property(field)); if (accessor == null) return true; return accessor.isWriteable(entity); } @Override public Object getValue(final Field field) { - final FieldAccessor accessor = accessorFor(property(field)); + final FieldAccessor accessor = accessorFor(property(field)); if (accessor == null) return null; else return accessor.getValue(entity); } @@ -102,8 +102,8 @@ public abstract class DefaultEntityState, STAT } @Override - public Object setValue(final Neo4JPersistentProperty property, final Object newVal) { - final FieldAccessor accessor = accessorFor(property); + public Object setValue(final Neo4jPersistentProperty property, final Object newVal) { + final FieldAccessor accessor = accessorFor(property); final Object result=accessor!=null ? accessor.setValue(entity, newVal) : newVal; notifyListeners(property, result); return result; @@ -112,34 +112,29 @@ public abstract class DefaultEntityState, STAT @Override public Object getDefaultImplementation(Field field) { - final FieldAccessor accessor = accessorFor(property(field)); + final FieldAccessor accessor = accessorFor(property(field)); if (accessor == null) return null; else return accessor.getDefaultImplementation(); } - protected Neo4JPersistentProperty property(Field field) { + protected Neo4jPersistentProperty property(Field field) { return persistentEntity.getPersistentProperty(field.getName()); } - protected FieldAccessor accessorFor(final Neo4JPersistentProperty property) { + protected FieldAccessor accessorFor(final Neo4jPersistentProperty property) { return fieldAccessors.get(property); } - private void notifyListeners(final Neo4JPersistentProperty field, final Object result) { + private void notifyListeners(final Neo4jPersistentProperty field, final Object result) { if (!fieldAccessorListeners.containsKey(field) || fieldAccessorListeners.get(field) == null) return; - for (final FieldAccessListener listener : fieldAccessorListeners.get(field)) { + for (final FieldAccessListener listener : fieldAccessorListeners.get(field)) { listener.valueChanged(entity, null, result); // todo oldValue } } protected Object getIdFromEntity() { - final Neo4JPersistentProperty idProperty = fieldAccessorFactoryProviders.getIdProperty(); + final Neo4jPersistentProperty idProperty = fieldAccessorFactoryProviders.getIdProperty(); if (idProperty==null) return null; - try { - return idProperty.getValue(entity); - } catch (IllegalAccessException e) { - log.warn("Error accessing id field "+idProperty); - return null; - } + return idProperty.getValue(entity); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java index 7a142e5df..31c3fefc7 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java @@ -21,24 +21,23 @@ import org.apache.commons.logging.LogFactory; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.PropertyHandler; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.util.TypeInformation; -import java.lang.reflect.Field; import java.util.*; -public abstract class DelegatingFieldAccessorFactory implements FieldAccessorFactory { +public abstract class DelegatingFieldAccessorFactory implements FieldAccessorFactory { private final static Log log = LogFactory.getLog(DelegatingFieldAccessorFactory.class); protected final GraphDatabaseContext graphDatabaseContext; - protected abstract Collection> createListenerFactories(); + protected abstract Collection createListenerFactories(); - protected abstract Collection> createAccessorFactories(); + protected abstract Collection createAccessorFactories(); public DelegatingFieldAccessorFactory(final GraphDatabaseContext graphDatabaseContext) { this.graphDatabaseContext = graphDatabaseContext; @@ -52,45 +51,45 @@ public abstract class DelegatingFieldAccessorFactory implements FieldAccessor } @Override - public boolean accept(final Neo4JPersistentProperty f) { + public boolean accept(final Neo4jPersistentProperty f) { return true; } - final Collection> fieldAccessorFactories = new ArrayList>(); - final Collection> fieldAccessorListenerFactories = new ArrayList>(); + final Collection fieldAccessorFactories = new ArrayList(); + final Collection fieldAccessorListenerFactories = new ArrayList(); - public FieldAccessor forField(final Neo4JPersistentProperty property) { - final FieldAccessorFactory factory = factoryForField(property); + public FieldAccessor forField(final Neo4jPersistentProperty property) { + final FieldAccessorFactory factory = factoryForField(property); return factory != null ? factory.forField(property) : null; } - private FieldAccessorFactory factoryForField(final Neo4JPersistentProperty property) { + private FieldAccessorFactory factoryForField(final Neo4jPersistentProperty property) { if (property.isSyntheticField()) return null; - for (final FieldAccessorFactory fieldAccessorFactory : fieldAccessorFactories) { + for (final FieldAccessorFactory fieldAccessorFactory : fieldAccessorFactories) { if (fieldAccessorFactory.accept(property)) { if (log.isInfoEnabled()) log.info("Factory " + fieldAccessorFactory + " used for field: " + property); - return (FieldAccessorFactory) fieldAccessorFactory; + return (FieldAccessorFactory) fieldAccessorFactory; } } if (log.isWarnEnabled()) log.warn("No FieldAccessor configured for field: " + property); return null; } - public List> listenersFor(final Neo4JPersistentProperty property) { - final List> result = new ArrayList>(); - final List> fieldAccessListenerFactories = getFieldAccessListenerFactories(property); - for (final FieldAccessorListenerFactory fieldAccessorListenerFactory : fieldAccessListenerFactories) { - final FieldAccessListener listener = fieldAccessorListenerFactory.forField(property); + public List listenersFor(final Neo4jPersistentProperty property) { + final List result = new ArrayList(); + final List fieldAccessListenerFactories = getFieldAccessListenerFactories(property); + for (final FieldAccessorListenerFactory fieldAccessorListenerFactory : fieldAccessListenerFactories) { + final FieldAccessListener listener = fieldAccessorListenerFactory.forField(property); result.add(listener); } return result; } - private List> getFieldAccessListenerFactories(final Neo4JPersistentProperty property) { - final List> result = new ArrayList>(); - for (final FieldAccessorListenerFactory fieldAccessorListenerFactory : fieldAccessorListenerFactories) { + private List getFieldAccessListenerFactories(final Neo4jPersistentProperty property) { + final List result = new ArrayList(); + for (final FieldAccessorListenerFactory fieldAccessorListenerFactory : fieldAccessorListenerFactories) { if (fieldAccessorListenerFactory.accept(property)) { - result.add((FieldAccessorListenerFactory) fieldAccessorListenerFactory); + result.add((FieldAccessorListenerFactory) fieldAccessorListenerFactory); } } return result; @@ -101,25 +100,25 @@ public abstract class DelegatingFieldAccessorFactory implements FieldAccessor private final Map, FieldAccessorFactoryProviders> accessorFactoryProviderCache = new HashMap, FieldAccessorFactoryProviders>(); - public FieldAccessorFactoryProviders accessorFactoriesFor(final Neo4JPersistentEntity type) { + public FieldAccessorFactoryProviders accessorFactoriesFor(final Neo4jPersistentEntity type) { synchronized (this) { final TypeInformation typeInformation = type.getTypeInformation(); final FieldAccessorFactoryProviders fieldAccessorFactoryProviders = accessorFactoryProviderCache.get(typeInformation); if (fieldAccessorFactoryProviders != null) return fieldAccessorFactoryProviders; - final FieldAccessorFactoryProviders newFieldAccessorFactories = new FieldAccessorFactoryProviders(typeInformation); - type.doWithProperties(new PropertyHandler() { + final FieldAccessorFactoryProviders newFieldAccessorFactories = new FieldAccessorFactoryProviders(typeInformation,graphDatabaseContext); + type.doWithProperties(new PropertyHandler() { @Override - public void doWithPersistentProperty(Neo4JPersistentProperty property) { - final FieldAccessorFactory factory = factoryForField(property); + public void doWithPersistentProperty(Neo4jPersistentProperty property) { + final FieldAccessorFactory factory = factoryForField(property); final List listenerFactories = (List) getFieldAccessListenerFactories(property); newFieldAccessorFactories.add(property, factory, listenerFactories); } }); - type.doWithAssociations(new AssociationHandler() { + type.doWithAssociations(new AssociationHandler() { @Override - public void doWithAssociation(Association association) { - final Neo4JPersistentProperty property = association.getInverse(); - final FieldAccessorFactory factory = factoryForField(property); + public void doWithAssociation(Association association) { + final Neo4jPersistentProperty property = association.getInverse(); + final FieldAccessorFactory factory = factoryForField(property); final List listenerFactories = (List) getFieldAccessListenerFactories(property); newFieldAccessorFactories.add(property, factory, listenerFactories); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DetachedEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DetachedEntityState.java index 977d94c47..86f3dfafc 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DetachedEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DetachedEntityState.java @@ -20,10 +20,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.neo4j.graphdb.Transaction; import org.springframework.data.neo4j.core.EntityState; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + + +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.util.ObjectUtils; @@ -39,14 +39,14 @@ import static org.springframework.data.neo4j.support.DoReturn.unwrap; * @author Michael Hunger * @since 15.09.2010 */ -public class DetachedEntityState, STATE> implements EntityState { +public class DetachedEntityState implements EntityState { private final Map dirty = new HashMap(); - protected final EntityState delegate; + protected final EntityState delegate; private final static Log log = LogFactory.getLog(DetachedEntityState.class); private GraphDatabaseContext graphDatabaseContext; - private Neo4JPersistentEntity persistentEntity; + private Neo4jPersistentEntity persistentEntity; - public DetachedEntityState(final EntityState delegate, GraphDatabaseContext graphDatabaseContext) { + public DetachedEntityState(final EntityState delegate, GraphDatabaseContext graphDatabaseContext) { this.delegate = delegate; this.persistentEntity = delegate.getPersistentEntity(); this.graphDatabaseContext = graphDatabaseContext; @@ -58,7 +58,7 @@ public class DetachedEntityState, STATE> imple } @Override - public ENTITY getEntity() { + public Object getEntity() { return delegate.getEntity(); } @@ -73,14 +73,14 @@ public class DetachedEntityState, STATE> imple } @Override - public Neo4JPersistentEntity getPersistentEntity() { + public Neo4jPersistentEntity getPersistentEntity() { return persistentEntity; } @Override public Object getValue(final Field field) { if (isDetached()) { - if (getEntity().getPersistentState()==null || isDirty(field)) { + if (graphDatabaseContext.getPersistentState(getEntity())==null || isDirty(field)) { if (log.isDebugEnabled()) log.debug("Outside of transaction, GET value from field " + field); Object entityValue = getValueFromEntity(field); if (entityValue != null) { @@ -89,7 +89,7 @@ public class DetachedEntityState, STATE> imple Object defaultValue = getDefaultImplementation(field); if (defaultValue != null) { - final ENTITY entity = getEntity(); + final Object entity = getEntity(); try { field.setAccessible(true); field.set(entity, defaultValue); @@ -137,12 +137,12 @@ public class DetachedEntityState, STATE> imple return setValue(property(field),newVal); } - private Neo4JPersistentProperty property(Field field) { + private Neo4jPersistentProperty property(Field field) { return persistentEntity.getPersistentProperty(field.getName()); } @Override - public Object setValue(final Neo4JPersistentProperty property, final Object newVal) { + public Object setValue(final Neo4jPersistentProperty property, final Object newVal) { if (isDetached()) { final Field field = property.getField(); if (!isDirty(field) && isWritable(field)) { @@ -186,7 +186,7 @@ public class DetachedEntityState, STATE> imple * always runs inside of a transaction */ private void flushDirty() { - final ENTITY entity = getEntity(); + final Object entity = getEntity(); if (!hasPersistentState()) { // createAndAssignState(); throw new IllegalStateException("Flushing detached entity without a persistent state, this had to be created first."); @@ -199,15 +199,16 @@ public class DetachedEntityState, STATE> imple final Field field = entry.getKey(); Object valueFromEntity = getValueFromEntity(field); cascadePersist(valueFromEntity); - if (log.isDebugEnabled()) log.debug("Flushing dirty Entity new node " + entity.getPersistentState() + " field " + field+ " with value "+ valueFromEntity); + if (log.isDebugEnabled()) log.debug("Flushing dirty Entity new node " + entity + " field " + field+ " with value "+ valueFromEntity); checkConcurrentModification(entity, entry, field); delegate.setValue(field, valueFromEntity); } } } + private void cascadePersist(Object valueFromEntity) { - if (valueFromEntity instanceof NodeBacked) { + /* TODO if (valueFromEntity instanceof NodeBacked) { ((NodeBacked) valueFromEntity).persist(); } if (valueFromEntity instanceof Collection) { @@ -217,6 +218,7 @@ public class DetachedEntityState, STATE> imple } } } + */ } @Override @@ -226,7 +228,7 @@ public class DetachedEntityState, STATE> imple private Object getValueFromEntity(final Field field) { - final ENTITY entity = getEntity(); + final Object entity = getEntity(); try { field.setAccessible(true); return field.get(entity); @@ -235,12 +237,12 @@ public class DetachedEntityState, STATE> imple } } - private void checkConcurrentModification(final ENTITY entity, final Map.Entry entry, final Field field) { + private void checkConcurrentModification(final Object entity, final Map.Entry entry, final Field field) { final ExistingValue previousValue = entry.getValue(); if (previousValue.mustCheckConcurrentModification()) { final Object nodeValue = unwrap(delegate.getValue(field)); if (!ObjectUtils.nullSafeEquals(nodeValue, previousValue.value)) { - throw new ConcurrentModificationException("Node " + entity.getPersistentState() + " field " + field + " changed in between previous " + previousValue + " current " + nodeValue); // todo or just overwrite + throw new ConcurrentModificationException("Node " + entity + " field " + field + " changed in between previous " + previousValue + " current " + nodeValue); // todo or just overwrite } } } @@ -272,11 +274,11 @@ public class DetachedEntityState, STATE> imple // todo always create an transaction for persist, atomic operation when no outside tx exists @Override - public ENTITY persist() { + public Object persist() { if (!isDetached()) return getEntity(); Transaction tx = graphDatabaseContext.beginTx(); try { - ENTITY result = delegate.persist(); + Object result = delegate.persist(); flushDirty(); tx.success(); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicProperties.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicProperties.java index 53bb9332b..531660a47 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicProperties.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicProperties.java @@ -38,12 +38,12 @@ import org.springframework.data.neo4j.fieldaccess.DynamicPropertiesFieldAccessor * Person p = new Person(); * p.persist(); * p.personalProperties.setProperty("ZIP", 8000); - * p.personalProperties.setProperty("City", "Zürich"); + * p.personalProperties.setProperty("City", "Zürich"); * * results in a node with the properties: *
  * "personalProperties-ZIP" => 8000
- * "personalProperties-City" => "Zürich"
+ * "personalProperties-City" => "Zürich"
  * 
*/ public interface DynamicProperties { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicPropertiesFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicPropertiesFieldAccessorFactory.java index 91f66589e..eb74c634d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicPropertiesFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/DynamicPropertiesFieldAccessorFactory.java @@ -15,68 +15,70 @@ */ package org.springframework.data.neo4j.fieldaccess; -import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.helpers.collection.IteratorUtil; import org.springframework.core.convert.ConversionService; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.DoReturn; +import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * This accessor factory creates {@link DynamicPropertiesFieldAccessor}s for @NodeEntity properties of type * {@link DynamicProperties}. */ -public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFactory> { +public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFactory { - private final ConversionService conversionService; + private final GraphDatabaseContext graphDatabaseContext; - public DynamicPropertiesFieldAccessorFactory(final ConversionService conversionService) { - this.conversionService = conversionService; + public DynamicPropertiesFieldAccessorFactory(final GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; } @Override - public boolean accept(Neo4JPersistentProperty f) { + public boolean accept(Neo4jPersistentProperty f) { return DynamicProperties.class.isAssignableFrom(f.getType()); } @Override - public FieldAccessor> forField(Neo4JPersistentProperty field) { - return new DynamicPropertiesFieldAccessor(conversionService, + public FieldAccessor forField(Neo4jPersistentProperty field) { + return new DynamicPropertiesFieldAccessor(graphDatabaseContext, field.getNeo4jPropertyName(), field); } - public static class DynamicPropertiesFieldAccessor implements FieldAccessor> { - private final ConversionService conversionService; + public static class DynamicPropertiesFieldAccessor implements FieldAccessor { private final String propertyNamePrefix; - private final Neo4JPersistentProperty field; + private final Neo4jPersistentProperty field; + private final GraphDatabaseContext graphDatabaseContext; - public DynamicPropertiesFieldAccessor(ConversionService conversionService, String propertyName, Neo4JPersistentProperty field) { - this.conversionService = conversionService; + public DynamicPropertiesFieldAccessor(GraphDatabaseContext graphDatabaseContext, String propertyName, Neo4jPersistentProperty field) { + this.graphDatabaseContext = graphDatabaseContext; this.propertyNamePrefix = propertyName; this.field = field; } @Override - public Object setValue(final GraphBacked entity, final Object newVal) { - final PropertyContainer propertyContainer = entity.getPersistentState(); + public Object setValue(final Object entity, final Object newVal) { + final PropertyContainer propertyContainer = graphDatabaseContext.getPersistentState(entity); PrefixedDynamicProperties dynamicProperties; if (newVal instanceof ManagedPrefixedDynamicProperties) { // newVal is already a managed container - dynamicProperties = (ManagedPrefixedDynamicProperties) newVal; + dynamicProperties = (ManagedPrefixedDynamicProperties) newVal; } else { // newVal is not a managed prefixed container and therefore contains // pure key/values that must be converted to a prefixed form dynamicProperties = new PrefixedDynamicProperties(propertyNamePrefix); - DynamicProperties newPropertiesVal = (DynamicProperties)newVal; - for(String key : newPropertiesVal.getPropertyKeys()) { - dynamicProperties.setProperty(key, newPropertiesVal.getProperty(key)); - } - } + if (newVal != null) { + DynamicProperties newPropertiesVal = (DynamicProperties) newVal; + for (String key : newPropertiesVal.getPropertyKeys()) { + dynamicProperties.setProperty(key, newPropertiesVal.getProperty(key)); + } + } + } Set dynamicProps = dynamicProperties.getPrefixedPropertyKeys(); @@ -105,10 +107,9 @@ public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFacto } @Override - public Object getValue(final GraphBacked entity) { - PropertyContainer element = entity.getPersistentState(); - ManagedPrefixedDynamicProperties props = ManagedPrefixedDynamicProperties.create(propertyNamePrefix, - field, entity); + public Object getValue(final Object entity) { + PropertyContainer element = graphDatabaseContext.getPersistentState(entity); + ManagedPrefixedDynamicProperties props = ManagedPrefixedDynamicProperties.create(propertyNamePrefix, field, entity,graphDatabaseContext,this); for (String key : element.getPropertyKeys()) { props.setPropertyIfPrefixed(key, element.getProperty(key)); } @@ -116,7 +117,7 @@ public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFacto } @Override - public boolean isWriteable(final GraphBacked entity) { + public boolean isWriteable(final Object entity) { return true; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessListener.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessListener.java index d58dcede7..8bcd7f1ee 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessListener.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessListener.java @@ -18,10 +18,8 @@ package org.springframework.data.neo4j.fieldaccess; /** * interface for listeners for field modifications, used for instance for indexing or tracing - * @param - * @param */ -public interface FieldAccessListener { +public interface FieldAccessListener { /** * callback method after modifying field write operation @@ -29,6 +27,6 @@ public interface FieldAccessListener { * @param oldVal * @param newVal */ - void valueChanged(ENTITY entity, Object oldVal, Object newVal); + void valueChanged(Object entity, Object oldVal, Object newVal); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessor.java index 22b7f85a0..69f25338d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessor.java @@ -18,12 +18,9 @@ package org.springframework.data.neo4j.fieldaccess; /** * interface for field accessors, encapsulates reading and writing from fields and write support information about the field. - * It is used by the {@link org.springframework.data.neo4j.core.EntityState} which is delegated to by the - * {@link org.springframework.data.neo4j.support.node.Neo4jNodeBacking} {@link org.springframework.data.neo4j.support.relationship.Neo4jRelationshipBacking} - * aspects. - * @param + * It is used by the {@link org.springframework.data.neo4j.core.EntityState}. */ -public interface FieldAccessor { +public interface FieldAccessor { /** * Returns a default implementation for a field or {@code null} if none is provided. @@ -41,18 +38,18 @@ public interface FieldAccessor { * @return the written value or a DoReturn wrapper with the written value or null. * DoReturn indicates that the aspect should not proceed to the original field access but instead return immediately. */ - Object setValue(ENTITY entity, Object newVal); + Object setValue(Object entity, Object newVal); /** * @param entity * @return the value or a DoReturn wrapper with the value for the field. * DoReturn indicates that the aspect should not proceed to the original field access but instead return immediately. */ - Object getValue(ENTITY entity); + Object getValue(Object entity); /** * @param entity * @return false for read only or computed fields, true otherwise */ - boolean isWriteable(ENTITY entity); + boolean isWriteable(Object entity); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactory.java index d0f2060d0..aaf71259a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactory.java @@ -16,9 +16,7 @@ package org.springframework.data.neo4j.fieldaccess; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; - -import java.lang.reflect.Field; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; /** * Factory interface for a single field / field accessor. Provides means to check if a certain field is eligible for this @@ -27,18 +25,18 @@ import java.lang.reflect.Field; * @author Michael Hunger * @since 12.09.2010 */ -public interface FieldAccessorFactory { +public interface FieldAccessorFactory { /** * * @param f field to check * @return true if this factory is responsible for creating a accessor for this field */ - boolean accept(Neo4JPersistentProperty f); + boolean accept(Neo4jPersistentProperty f); /** * * @param f the field to create an accessor for * @return a field accessor for the field or null if none can be created */ - FieldAccessor forField(Neo4JPersistentProperty f); + FieldAccessor forField(Neo4jPersistentProperty f); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactoryProviders.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactoryProviders.java index 04d61174e..f45da0230 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactoryProviders.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorFactoryProviders.java @@ -16,10 +16,11 @@ package org.springframework.data.neo4j.fieldaccess; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.util.TypeInformation; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -32,31 +33,31 @@ import java.util.Map; public class FieldAccessorFactoryProviders { static class FieldAccessorFactoryProvider { - private final Neo4JPersistentProperty property; - private final FieldAccessorFactory fieldAccessorFactory; - private final List> fieldAccessorListenerFactories; + private final Neo4jPersistentProperty property; + private final FieldAccessorFactory fieldAccessorFactory; + private final List fieldAccessorListenerFactories; - FieldAccessorFactoryProvider(final Neo4JPersistentProperty property, final FieldAccessorFactory fieldAccessorFactory, final List fieldAccessorListenerFactories) { + FieldAccessorFactoryProvider(final Neo4jPersistentProperty property, final FieldAccessorFactory fieldAccessorFactory, final List fieldAccessorListenerFactories) { this.property = property; this.fieldAccessorFactory = fieldAccessorFactory; this.fieldAccessorListenerFactories = fieldAccessorListenerFactories; } - public FieldAccessor accessor() { + public FieldAccessor accessor() { if (fieldAccessorFactory == null) return null; return fieldAccessorFactory.forField(property); } - public List> listeners() { + public List listeners() { if (fieldAccessorListenerFactories == null) return null; - final List> listeners = new ArrayList>(fieldAccessorListenerFactories.size()); - for (final FieldAccessorListenerFactory fieldAccessorListenerFactory : fieldAccessorListenerFactories) { + final List listeners = new ArrayList(fieldAccessorListenerFactories.size()); + for (final FieldAccessorListenerFactory fieldAccessorListenerFactory : fieldAccessorListenerFactories) { listeners.add(fieldAccessorListenerFactory.forField(property)); } return listeners; } - public Neo4JPersistentProperty getProperty() { + public Neo4jPersistentProperty getProperty() { return property; } } @@ -64,37 +65,37 @@ public class FieldAccessorFactoryProviders { private final TypeInformation type; private final List> fieldAccessorFactoryProviders = new ArrayList>(); private final IdFieldAccessorFactory idFieldAccessorFactory; - private Neo4JPersistentProperty idProperty; + private Neo4jPersistentProperty idProperty; - FieldAccessorFactoryProviders(TypeInformation type) { + FieldAccessorFactoryProviders(TypeInformation type, GraphDatabaseContext graphDatabaseContext) { this.type = type; - idFieldAccessorFactory = new IdFieldAccessorFactory(); + idFieldAccessorFactory= new IdFieldAccessorFactory(graphDatabaseContext); } - public Map> getFieldAccessors() { - final Map> result = new HashMap>(fieldAccessorFactoryProviders.size(),1); + public Map getFieldAccessors() { + final Map result = new HashMap(fieldAccessorFactoryProviders.size(),1); for (final FieldAccessorFactoryProvider fieldAccessorFactoryProvider : fieldAccessorFactoryProviders) { - final FieldAccessor accessor = fieldAccessorFactoryProvider.accessor(); + final FieldAccessor accessor = fieldAccessorFactoryProvider.accessor(); result.put(fieldAccessorFactoryProvider.getProperty(), accessor); } return result; } - public Map>> getFieldAccessListeners() { - final Map>> result = new HashMap>>(fieldAccessorFactoryProviders.size(),1); + public Map> getFieldAccessListeners() { + final Map> result = new HashMap>(fieldAccessorFactoryProviders.size(),1); for (final FieldAccessorFactoryProvider fieldAccessorFactoryProvider : fieldAccessorFactoryProviders) { - final List> listeners = (List>) fieldAccessorFactoryProvider.listeners(); + final List listeners = (List) fieldAccessorFactoryProvider.listeners(); result.put(fieldAccessorFactoryProvider.getProperty(), listeners); } return result; } - public void add(Neo4JPersistentProperty property, FieldAccessorFactory fieldAccessorFactory, List listenerFactories) { + public void add(Neo4jPersistentProperty property, FieldAccessorFactory fieldAccessorFactory, List listenerFactories) { fieldAccessorFactoryProviders.add(new FieldAccessorFactoryProvider(property, fieldAccessorFactory, listenerFactories)); if (property.isIdProperty()) this.idProperty = property; } - public Neo4JPersistentProperty getIdProperty() { + public Neo4jPersistentProperty getIdProperty() { return idProperty; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorListenerFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorListenerFactory.java index f6d65350d..a683ff448 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorListenerFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/FieldAccessorListenerFactory.java @@ -16,7 +16,7 @@ package org.springframework.data.neo4j.fieldaccess; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; /** * factory interface for field accessor listeners. Provides means to check if a field is eligible for this factory @@ -25,16 +25,16 @@ import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; * @author Michael Hunger * @since 12.09.2010 */ -public interface FieldAccessorListenerFactory { +public interface FieldAccessorListenerFactory { /** * * @param property@return true if this factory is able to create a listener for the field */ - boolean accept(Neo4JPersistentProperty property); + boolean accept(Neo4jPersistentProperty property); /** * * @param property@return newly created field listener */ - FieldAccessListener forField(Neo4JPersistentProperty property); + FieldAccessListener forField(Neo4jPersistentProperty property); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IdFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IdFieldAccessorFactory.java index 02af6fa4f..47c93c864 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IdFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IdFieldAccessorFactory.java @@ -16,8 +16,13 @@ package org.springframework.data.neo4j.fieldaccess; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; +import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import static org.springframework.data.neo4j.support.DoReturn.doReturn; @@ -25,37 +30,52 @@ import static org.springframework.data.neo4j.support.DoReturn.doReturn; * @author Michael Hunger * @since 12.09.2010 */ -public class IdFieldAccessorFactory implements FieldAccessorFactory { - @Override - public boolean accept(final Neo4JPersistentProperty property) { +public class IdFieldAccessorFactory implements FieldAccessorFactory { + private final GraphDatabaseContext graphDatabaseContext; + + public IdFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; + } + + @Override + public boolean accept(final Neo4jPersistentProperty property) { return property.isIdProperty(); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty property) { - return new IdFieldAccessor(property); + public FieldAccessor forField(final Neo4jPersistentProperty property) { + return new IdFieldAccessor(property,graphDatabaseContext); } - public static class IdFieldAccessor implements FieldAccessor { - protected final Neo4JPersistentProperty property; + public static class IdFieldAccessor implements FieldAccessor { + protected final Neo4jPersistentProperty property; + private final GraphDatabaseContext graphDatabaseContext; - public IdFieldAccessor(final Neo4JPersistentProperty property) { + public IdFieldAccessor(final Neo4jPersistentProperty property, GraphDatabaseContext graphDatabaseContext) { this.property = property; - } + this.graphDatabaseContext = graphDatabaseContext; + } @Override - public boolean isWriteable(NodeBacked nodeBacked) { + public boolean isWriteable(Object entity) { return false; } @Override - public Object setValue(final NodeBacked nodeBacked, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { return newVal; } @Override - public Object getValue(final NodeBacked nodeBacked) { - return doReturn(nodeBacked.getPersistentState().getId()); + public Object getValue(final Object entity) { + final PropertyContainer state = graphDatabaseContext.getPersistentState(entity); + if (state instanceof Node) { + return doReturn(((Node)state).getId()); + } + if (state instanceof Relationship) { + return doReturn(((Relationship)state).getId()); + } + throw new MappingException("Error retrieving id value from "+entity); } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java index 7d554a58d..f794b0097 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java @@ -22,50 +22,51 @@ import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.index.Index; import org.neo4j.index.lucene.ValueContext; import org.springframework.data.neo4j.annotation.Indexed; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; -public class IndexingPropertyFieldAccessorListenerFactory> implements FieldAccessorListenerFactory { +public class IndexingPropertyFieldAccessorListenerFactory implements FieldAccessorListenerFactory { private final PropertyFieldAccessorFactory propertyFieldAccessorFactory; private final ConvertingNodePropertyFieldAccessorFactory convertingNodePropertyFieldAccessorFactory; private final IndexProvider indexProvider; + private final GraphDatabaseContext graphDatabaseContext; public IndexingPropertyFieldAccessorListenerFactory(final GraphDatabaseContext graphDatabaseContext, final PropertyFieldAccessorFactory propertyFieldAccessorFactory, final ConvertingNodePropertyFieldAccessorFactory convertingNodePropertyFieldAccessorFactory) { + this.graphDatabaseContext = graphDatabaseContext; indexProvider = new IndexProvider(graphDatabaseContext); this.propertyFieldAccessorFactory = propertyFieldAccessorFactory; this.convertingNodePropertyFieldAccessorFactory = convertingNodePropertyFieldAccessorFactory; } @Override - public boolean accept(final Neo4JPersistentProperty property) { + public boolean accept(final Neo4jPersistentProperty property) { return isPropertyField(property) && property.isIndexed(); } - private boolean isPropertyField(final Neo4JPersistentProperty property) { + private boolean isPropertyField(final Neo4jPersistentProperty property) { return propertyFieldAccessorFactory.accept(property) || convertingNodePropertyFieldAccessorFactory.accept(property); } @Override - public FieldAccessListener forField(Neo4JPersistentProperty property) { - return (FieldAccessListener) new IndexingPropertyFieldAccessorListener(property, indexProvider); + public FieldAccessListener forField(Neo4jPersistentProperty property) { + return new IndexingPropertyFieldAccessorListener(property, indexProvider,graphDatabaseContext); } - public static class IndexProvider> { + public static class IndexProvider { private final GraphDatabaseContext graphDatabaseContext; public IndexProvider(GraphDatabaseContext graphDatabaseContext) { this.graphDatabaseContext = graphDatabaseContext; } - private String getIndexKey(Neo4JPersistentProperty property) { + private String getIndexKey(Neo4jPersistentProperty property) { Indexed indexed = property.getAnnotation(Indexed.class); if (indexed==null || indexed.fieldName().isEmpty()) return property.getNeo4jPropertyName(); return indexed.fieldName(); @@ -75,7 +76,7 @@ public class IndexingPropertyFieldAccessorListenerFactory getIndex(Neo4JPersistentProperty property, GraphBacked instance) { + private Index getIndex(Neo4jPersistentProperty property, Object instance) { final Indexed indexedAnnotation = property.getAnnotation(Indexed.class); final Class type = (Class) property.getOwner().getType(); final String providedIndexName = indexedAnnotation.indexName().isEmpty() ? null : indexedAnnotation.indexName(); @@ -94,26 +95,28 @@ public class IndexingPropertyFieldAccessorListenerFactory implements FieldAccessListener, Object> { + public static class IndexingPropertyFieldAccessorListener implements FieldAccessListener { private final static Log log = LogFactory.getLog( IndexingPropertyFieldAccessorListener.class ); protected final String indexKey; - private final Neo4JPersistentProperty property; + private final Neo4jPersistentProperty property; private final IndexProvider indexProvider; + private final GraphDatabaseContext graphDatabaseContext; - public IndexingPropertyFieldAccessorListener(final Neo4JPersistentProperty property, IndexProvider indexProvider) { + public IndexingPropertyFieldAccessorListener(final Neo4jPersistentProperty property, IndexProvider indexProvider, GraphDatabaseContext graphDatabaseContext) { this.property = property; this.indexProvider = indexProvider; + this.graphDatabaseContext = graphDatabaseContext; indexKey = indexProvider.getIndexKey(property); } @Override - public void valueChanged(GraphBacked graphBacked, Object oldVal, Object newVal) { - Index index = indexProvider.getIndex(property, graphBacked); + public void valueChanged(Object entity, Object oldVal, Object newVal) { + Index index = indexProvider.getIndex(property, entity); if (newVal instanceof Number) newVal = ValueContext.numeric((Number) newVal); - final T state = graphBacked.getPersistentState(); + final T state = graphDatabaseContext.getPersistentState(entity); index.remove(state, indexKey); if (newVal != null) { index.add(state, indexKey, newVal); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedFieldAccessorSet.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedFieldAccessorSet.java index a1026d1ed..a2f008473 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedFieldAccessorSet.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedFieldAccessorSet.java @@ -17,11 +17,14 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; import org.springframework.data.neo4j.core.EntityState; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + + +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.DoReturn; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import java.util.AbstractSet; import java.util.Collection; @@ -31,15 +34,19 @@ import java.util.Set; /** * @param */ -public class ManagedFieldAccessorSet extends AbstractSet { - private final ENTITY entity; +public class ManagedFieldAccessorSet extends AbstractSet { + private final Object entity; final Set delegate; - private final Neo4JPersistentProperty property; + private final Neo4jPersistentProperty property; + private final GraphDatabaseContext ctx; + private final FieldAccessor fieldAccessor; - public ManagedFieldAccessorSet(final ENTITY entity, final Object newVal, final Neo4JPersistentProperty property) { + public ManagedFieldAccessorSet(final Object entity, final Object newVal, final Neo4jPersistentProperty property, GraphDatabaseContext ctx, FieldAccessor fieldAccessor) { this.entity = entity; this.property = property; - delegate = (Set) newVal; + this.ctx = ctx; + this.fieldAccessor = fieldAccessor; + delegate = (Set) newVal; } @Override @@ -65,26 +72,21 @@ public class ManagedFieldAccessorSet extends AbstractSet { } private void update() { - if (entity instanceof NodeBacked) { - NodeBacked nodeBacked = (NodeBacked) entity; - final EntityState entityState = nodeBacked.getEntityState(); - updateValue(entityState); + final Neo4jPersistentEntity persistentEntity = property.getOwner(); + final PropertyContainer persistentState = persistentEntity.getPersistentState(entity, ctx); + if (persistentEntity.isNodeEntity()) { + updateValue(); } - if (entity instanceof RelationshipBacked) { - RelationshipBacked relationshipBacked = (RelationshipBacked) entity; - updateValue(relationshipBacked.getEntityState()); + if (persistentEntity.isRelationshipEntity()) { + updateValue(); } } - private Object updateValue(EntityState entityState) { - try { - final Object newValue = entityState.setValue(property, delegate); - if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue); - property.setValue(entity, newValue); - return newValue; - } catch (IllegalAccessException e) { - throw new RuntimeException("Could not update field "+ property +" to new value of type "+delegate.getClass()); - } + private Object updateValue() { + final Object newValue = fieldAccessor.setValue(entity,delegate); + if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue); + property.setValue(entity, newValue); + return newValue; } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java index 74514cf11..7eae2fb26 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ManagedPrefixedDynamicProperties.java @@ -19,40 +19,41 @@ import java.lang.reflect.Field; import java.util.Map; import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.PropertyContainer; import org.springframework.data.neo4j.core.EntityState; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + + + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.DoReturn; +import org.springframework.data.neo4j.support.GraphDatabaseContext; +import scala.annotation.target.field; /** * Updates the entity containing such a ManagedPrefixedDynamicProperties when some property is added, changed or * deleted. - * - * @param - * type of the entity (Node or Relationships) */ -public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties { - private final ENTITY entity; - private final Neo4JPersistentProperty property; +public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties { + private final Object entity; + private final GraphDatabaseContext graphDatabaseContext; + private final FieldAccessor fieldAccessor; + private final Neo4jPersistentProperty property; + private boolean isNode; - public ManagedPrefixedDynamicProperties(String prefix, final Neo4JPersistentProperty property, final ENTITY entity) { - super(prefix); - this.property = property; - this.entity = entity; + public ManagedPrefixedDynamicProperties(String prefix, final Neo4jPersistentProperty property, final Object entity, GraphDatabaseContext graphDatabaseContext, FieldAccessor fieldAccessor) { + this(prefix,10,property,entity,graphDatabaseContext,fieldAccessor); } - public ManagedPrefixedDynamicProperties(String prefix, int initialCapacity, final Neo4JPersistentProperty property, final ENTITY entity) { + public ManagedPrefixedDynamicProperties(String prefix, int initialCapacity, final Neo4jPersistentProperty property, final Object entity, GraphDatabaseContext graphDatabaseContext, FieldAccessor fieldAccessor) { super(prefix, initialCapacity); this.property = property; this.entity = entity; + this.graphDatabaseContext = graphDatabaseContext; + this.fieldAccessor = fieldAccessor; + this.isNode = property.getOwner().isNodeEntity(); } - public static ManagedPrefixedDynamicProperties create(String prefix, final Neo4JPersistentProperty property, - final E entity) { - return new ManagedPrefixedDynamicProperties(prefix, property, entity); + public static ManagedPrefixedDynamicProperties create(String prefix, final Neo4jPersistentProperty property, final Object entity, GraphDatabaseContext graphDatabaseContext, FieldAccessor fieldAccessor) { + return new ManagedPrefixedDynamicProperties(prefix, property, entity,graphDatabaseContext,fieldAccessor); } @Override @@ -76,35 +77,20 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicPro @Override public DynamicProperties createFrom(Map map) { - DynamicProperties d = new ManagedPrefixedDynamicProperties(prefix, map.size(), property, entity); + DynamicProperties d = new ManagedPrefixedDynamicProperties(prefix, map.size(), property, entity,graphDatabaseContext,fieldAccessor); d.setPropertiesFrom(map); return d; } private void update() { - if (entity instanceof NodeBacked) { - NodeBacked nodeBacked = (NodeBacked) entity; - final EntityState entityState = nodeBacked.getEntityState(); - updateValue(entityState); - } - if (entity instanceof RelationshipBacked) { - RelationshipBacked relationshipBacked = (RelationshipBacked) entity; - updateValue(relationshipBacked.getEntityState()); - } + updateValue(); } - private Object updateValue(EntityState entityState) { - try { - final Object newValue = entityState.setValue(property, this); - if (newValue instanceof DoReturn) - return DoReturn.unwrap(newValue); - final Field field = this.property.getField(); - field.setAccessible(true); - field.set(entity, newValue); - return newValue; - } catch (IllegalAccessException e) { - throw new RuntimeException("Could not update field " + property + " to new value of type " - + this.getClass()); - } + private Object updateValue() { + final Object newValue = fieldAccessor.setValue(entity, this); + if (newValue instanceof DoReturn) + return DoReturn.unwrap(newValue); + property.setValue(entity, newValue); + return newValue; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/Neo4jConversionServiceFactoryBean.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/Neo4jConversionServiceFactoryBean.java index 6d6adec9c..279189d20 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/Neo4jConversionServiceFactoryBean.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/Neo4jConversionServiceFactoryBean.java @@ -101,7 +101,7 @@ public class Neo4jConversionServiceFactoryBean implements FactoryBean { +public class NodeDelegatingFieldAccessorFactory extends DelegatingFieldAccessorFactory { public NodeDelegatingFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { super(graphDatabaseContext); } @Override - protected Collection> createListenerFactories() { - return Arrays.>asList( + protected Collection createListenerFactories() { + return Arrays.asList( new IndexingPropertyFieldAccessorListenerFactory( graphDatabaseContext, - new PropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()), - new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext.getConversionService())), + new PropertyFieldAccessorFactory(graphDatabaseContext), + new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext)), new ValidatingNodePropertyFieldAccessorListenerFactory(graphDatabaseContext) ); } @Override - protected Collection> createAccessorFactories() { - return Arrays.>asList( - new IdFieldAccessorFactory(), + protected Collection createAccessorFactories() { + return Arrays.asList( + new IdFieldAccessorFactory(graphDatabaseContext), new TransientFieldAccessorFactory(), - new TraversalFieldAccessorFactory(), - new QueryFieldAccessorFactory(), - new PropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()), - new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()), + new TraversalFieldAccessorFactory(graphDatabaseContext), + new QueryFieldAccessorFactory(graphDatabaseContext), + new PropertyFieldAccessorFactory(graphDatabaseContext), + new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext), new SingleRelationshipFieldAccessorFactory(graphDatabaseContext), new OneToNRelationshipFieldAccessorFactory(graphDatabaseContext), new ReadOnlyOneToNRelationshipFieldAccessorFactory(graphDatabaseContext), new OneToNRelationshipEntityFieldAccessorFactory(graphDatabaseContext), - new DynamicPropertiesFieldAccessorFactory(graphDatabaseContext.getConversionService()) + new DynamicPropertiesFieldAccessorFactory(graphDatabaseContext) ); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java index b2444a2fa..9aa99aba2 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java @@ -16,21 +16,13 @@ package org.springframework.data.neo4j.fieldaccess; -import org.neo4j.graphdb.Direction; -import org.neo4j.graphdb.DynamicRelationshipType; -import org.springframework.data.neo4j.annotation.RelatedTo; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; -import org.springframework.data.neo4j.support.GenericTypeExtractor; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import java.lang.reflect.Field; - /** * @author Michael Hunger * @since 12.09.2010 */ -public abstract class NodeRelationshipFieldAccessorFactory implements FieldAccessorFactory { +public abstract class NodeRelationshipFieldAccessorFactory implements FieldAccessorFactory { protected GraphDatabaseContext graphDatabaseContext; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeToNodesRelationshipFieldAccessor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeToNodesRelationshipFieldAccessor.java index 5aaf17043..82f9ab8b6 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeToNodesRelationshipFieldAccessor.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeToNodesRelationshipFieldAccessor.java @@ -20,12 +20,12 @@ import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; -import org.springframework.data.neo4j.support.GraphDatabaseContext; -import java.lang.reflect.Field; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; +import org.springframework.util.Assert; + import java.util.HashSet; import java.util.Set; @@ -33,8 +33,8 @@ import java.util.Set; * @author Michael Hunger * @since 12.09.2010 */ -public abstract class NodeToNodesRelationshipFieldAccessor extends AbstractNodeRelationshipFieldAccessor { - public NodeToNodesRelationshipFieldAccessor(final Class clazz, final GraphDatabaseContext graphDatabaseContext, final Direction direction, final RelationshipType type, Neo4JPersistentProperty property) { +public abstract class NodeToNodesRelationshipFieldAccessor extends AbstractNodeRelationshipFieldAccessor { + public NodeToNodesRelationshipFieldAccessor(final Class clazz, final GraphDatabaseContext graphDatabaseContext, final Direction direction, final RelationshipType type, Neo4jPersistentProperty property) { super(clazz, graphDatabaseContext, direction, type,property); } @@ -48,7 +48,7 @@ public abstract class NodeToNodesRelationshipFieldAccessor getStatesFromEntity(final NodeBacked entity) { + protected Iterable getStatesFromEntity(final Object entity) { final Node entityNode = getState(entity); final Set result = new HashSet(); for (final Relationship rel : entityNode.getRelationships(type, direction)) { @@ -58,7 +58,8 @@ public abstract class NodeToNodesRelationshipFieldAccessor { +public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccessorFactory { private GraphDatabaseContext graphDatabaseContext; @@ -42,50 +40,51 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess } @Override - public boolean accept(final Neo4JPersistentProperty property) { + public boolean accept(final Neo4jPersistentProperty property) { return property.isRelationship() && !property.getRelationshipInfo().targetsNodes() && property.getRelationshipInfo().isMultiple(); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty property) { + public FieldAccessor forField(final Neo4jPersistentProperty property) { final RelationshipInfo relationshipInfo = property.getRelationshipInfo(); - return new OneToNRelationshipEntityFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class) relationshipInfo.getTargetType().getType(), graphDatabaseContext,property); + return new OneToNRelationshipEntityFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class) relationshipInfo.getTargetType().getType(), graphDatabaseContext,property); } - public static class OneToNRelationshipEntityFieldAccessor extends AbstractNodeRelationshipFieldAccessor { + public static class OneToNRelationshipEntityFieldAccessor extends AbstractNodeRelationshipFieldAccessor { - public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty property) { + public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4jPersistentProperty property) { super(elementClass, graphDatabaseContext, direction, type, property); } @Override - public Object setValue(final NodeBacked entity, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); } @Override - public boolean isWriteable(NodeBacked nodeBacked) { + public boolean isWriteable(Object entity) { return false; } @Override - public Object getValue(final NodeBacked entity) { + public Object getValue(final Object entity) { checkUnderlyingNode(entity); - final Set result = createEntitySetFromRelationships(entity); - return doReturn(new ManagedFieldAccessorSet(entity, result, property)); + final Set result = createEntitySetFromRelationships(entity); + return doReturn(new ManagedFieldAccessorSet(entity, result, property,graphDatabaseContext, this)); } - private Set createEntitySetFromRelationships(final NodeBacked entity) { - final Set result = new HashSet(); + private Set createEntitySetFromRelationships(final Object entity) { + final Set result = new HashSet(); for (final Relationship rel : getStatesFromEntity(entity)) { - final RelationshipBacked relationshipEntity = graphDatabaseContext.createEntityFromState(rel, relatedType); + final Object relationshipEntity = graphDatabaseContext.createEntityFromState(rel, relatedType); result.add(relationshipEntity); } return result; } @Override - protected Iterable getStatesFromEntity(final NodeBacked entity) { - return entity.getPersistentState().getRelationships(type, direction); + protected Iterable getStatesFromEntity(final Object entity) { + final Node persistentState = property.getOwner().getPersistentState(entity, graphDatabaseContext); + return persistentState.getRelationships(type, direction); } @Override @@ -94,8 +93,8 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess } @Override - protected Node getState(final NodeBacked nodeBacked) { - return nodeBacked.getPersistentState(); + protected Node getState(final Object entity) { + return property.getOwner().getPersistentState(entity, graphDatabaseContext); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java index 0db6d82e4..bc5f716da 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java @@ -19,8 +19,7 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.RelationshipType; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.mapping.RelationshipInfo; import org.springframework.data.neo4j.support.GraphDatabaseContext; @@ -36,26 +35,26 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel } @Override - public boolean accept(final Neo4JPersistentProperty property) { + public boolean accept(final Neo4jPersistentProperty property) { if (!property.isRelationship()) return false; final RelationshipInfo info = property.getRelationshipInfo(); return info.isMultiple() && info.targetsNodes() && !info.isReadonly(); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty property) { + public FieldAccessor forField(final Neo4jPersistentProperty property) { final RelationshipInfo relationshipInfo = property.getRelationshipInfo(); - final Class targetType = (Class) relationshipInfo.getTargetType().getType(); + final Class targetType = (Class) relationshipInfo.getTargetType().getType(); return new OneToNRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), targetType, graphDatabaseContext,property); } - public static class OneToNRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor { + public static class OneToNRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor { - public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty property) { + public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4jPersistentProperty property) { super(elementClass, graphDatabaseContext, direction, type,property); } - public Object setValue(final NodeBacked entity, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { final Node node = checkUnderlyingNode(entity); if (newVal == null) { removeMissingRelationships(node, Collections.emptySet()); @@ -64,13 +63,13 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel final Set targetNodes = checkTargetIsSetOfNodebacked(newVal); removeMissingRelationships(node, targetNodes); createAddedRelationships(node, targetNodes); - return createManagedSet(entity, (Set) newVal); + return createManagedSet(entity, (Set) newVal); } - @Override - public Object getValue(final NodeBacked entity) { + @Override + public Object getValue(final Object entity) { checkUnderlyingNode(entity); - final Set result = createEntitySetFromRelationshipEndNodes(entity); + final Set result = createEntitySetFromRelationshipEndNodes(entity); return doReturn(createManagedSet(entity, result)); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PropertyFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PropertyFieldAccessorFactory.java index 6153f7202..de7499b31 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PropertyFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/PropertyFieldAccessorFactory.java @@ -17,9 +17,10 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.PropertyContainer; + import org.springframework.core.convert.ConversionService; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import static org.springframework.data.neo4j.support.DoReturn.doReturn; @@ -27,45 +28,47 @@ import static org.springframework.data.neo4j.support.DoReturn.doReturn; * @author Michael Hunger * @since 12.09.2010 */ -public class PropertyFieldAccessorFactory implements FieldAccessorFactory> { +public class PropertyFieldAccessorFactory implements FieldAccessorFactory { - private final ConversionService conversionService; + private final GraphDatabaseContext graphDatabaseContext; - public PropertyFieldAccessorFactory(ConversionService conversionService) { - this.conversionService = conversionService; + public PropertyFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; } @Override - public boolean accept(final Neo4JPersistentProperty f) { + public boolean accept(final Neo4jPersistentProperty f) { return f.isNeo4jPropertyType(); } @Override - public FieldAccessor> forField(final Neo4JPersistentProperty field) { - return new PropertyFieldAccessor(conversionService, field); + public FieldAccessor forField(final Neo4jPersistentProperty field) { + return new PropertyFieldAccessor(graphDatabaseContext, field); } - public static class PropertyFieldAccessor implements FieldAccessor> { - private final ConversionService conversionService; - private final Neo4JPersistentProperty property; + public static class PropertyFieldAccessor implements FieldAccessor { + protected final GraphDatabaseContext graphDatabaseContext; + protected final Neo4jPersistentProperty property; protected final String propertyName; protected final Class fieldType; + private ConversionService conversionService; - public PropertyFieldAccessor(ConversionService conversionService, Neo4JPersistentProperty property) { - this.conversionService = conversionService; + public PropertyFieldAccessor(GraphDatabaseContext graphDatabaseContext, Neo4jPersistentProperty property) { + this.graphDatabaseContext = graphDatabaseContext; + conversionService = graphDatabaseContext.getConversionService(); this.property = property; this.propertyName = property.getNeo4jPropertyName(); this.fieldType = property.getType() ; } @Override - public boolean isWriteable(final GraphBacked graphBacked) { + public boolean isWriteable(final Object entity) { return true; } @Override - public Object setValue(final GraphBacked graphBacked, final Object newVal) { - final PropertyContainer propertyContainer = graphBacked.getPersistentState(); + public Object setValue(final Object entity, final Object newVal) { + final PropertyContainer propertyContainer = graphDatabaseContext.getPersistentState(entity); if (newVal==null) { propertyContainer.removeProperty(propertyName); } else { @@ -75,17 +78,17 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory graphBacked) { - return doReturn(doGetValue(graphBacked)); + public final Object getValue(final Object entity) { + return doReturn(doGetValue(entity)); } - protected Object doGetValue(final GraphBacked graphBacked) { - PropertyContainer element = graphBacked.getPersistentState(); + protected Object doGetValue(final Object entity) { + PropertyContainer element = graphDatabaseContext.getPersistentState(entity); if (element.hasProperty(propertyName)) { Object value = element.getProperty(propertyName); if (value == null || fieldType.isInstance(value)) return value; - if (conversionService!=null) { - return conversionService.convert(value, fieldType); + if (graphDatabaseContext.getConversionService() !=null) { + return graphDatabaseContext.getConversionService().convert(value, fieldType); } return value; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/QueryFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/QueryFieldAccessorFactory.java index acb35a657..555be4a99 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/QueryFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/QueryFieldAccessorFactory.java @@ -16,46 +16,52 @@ package org.springframework.data.neo4j.fieldaccess; +import org.neo4j.graphdb.Node; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.neo4j.annotation.Query; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; -import org.springframework.data.neo4j.support.GenericTypeExtractor; -import org.springframework.data.util.TypeInformation; -import java.lang.reflect.Field; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; + import java.util.HashMap; import java.util.Map; import static org.springframework.data.neo4j.support.DoReturn.doReturn; -public class QueryFieldAccessorFactory implements FieldAccessorFactory { - @Override - public boolean accept(final Neo4JPersistentProperty f) { +public class QueryFieldAccessorFactory implements FieldAccessorFactory { + private final GraphDatabaseContext graphDatabaseContext; + + public QueryFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; + } + + @Override + public boolean accept(final Neo4jPersistentProperty f) { final Query query = f.getAnnotation(Query.class); - return query != null - && !query.value().isEmpty(); + return query != null && !query.value().isEmpty(); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty field) { - return new QueryFieldAccessor(field); + public FieldAccessor forField(final Neo4jPersistentProperty property) { + return new QueryFieldAccessor(property,graphDatabaseContext); } /** * @author Michael Hunger * @since 12.09.2010 */ - public static class QueryFieldAccessor implements FieldAccessor { - protected final Neo4JPersistentProperty property; - private final String query; + public static class QueryFieldAccessor implements FieldAccessor { + protected final Neo4jPersistentProperty property; + private final GraphDatabaseContext graphDatabaseContext; + private final String query; private Class target; protected String[] annotationParams; private boolean iterableResult; - public QueryFieldAccessor(final Neo4JPersistentProperty property) { + public QueryFieldAccessor(final Neo4jPersistentProperty property, GraphDatabaseContext graphDatabaseContext) { this.property = property; + this.graphDatabaseContext = graphDatabaseContext; final Query query = property.getAnnotation(Query.class); this.annotationParams = query.params(); if ((this.annotationParams.length % 2) != 0) { @@ -66,35 +72,34 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory resolveTarget(Query query, Neo4JPersistentProperty property) { + private Class resolveTarget(Query query, Neo4jPersistentProperty property) { if (!query.elementClass().equals(Object.class)) return query.elementClass(); return property.getTypeInformation().getActualType().getType(); } @Override - public boolean isWriteable(NodeBacked nodeBacked) { + public boolean isWriteable(Object entity) { return false; } @Override - public Object setValue(final NodeBacked nodeBacked, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { throw new InvalidDataAccessApiUsageException("Cannot set readonly query field " + property); } @Override - public Object getValue(final NodeBacked nodeBacked) { - return doReturn(executeQuery(nodeBacked, this.query, createPlaceholderParams(nodeBacked))); + public Object getValue(final Object entity) { + return doReturn(executeQuery(entity, this.query, createPlaceholderParams(entity))); } - private Object executeQuery(NodeBacked nodeBacked, String queryString, Map params) { - if (!iterableResult) return nodeBacked.findByQuery(queryString,this.target,params); - if (Map.class.isAssignableFrom(target)) return nodeBacked.findAllByQuery(queryString,params); - return nodeBacked.findAllByQuery(queryString, this.target,params); + private Object executeQuery(Object entity, String queryString, Map params) { + return graphDatabaseContext.executeQuery(entity,queryString,params,property); } - private Map createPlaceholderParams(NodeBacked nodeBacked) { + private Map createPlaceholderParams(Object entity) { Map params=new HashMap(); - params.put("start",nodeBacked.getNodeId()); + final Node startNode = graphDatabaseContext.getPersistentState(entity); + params.put("start", startNode.getId()); if (annotationParams.length==0) return params; for (int i = 0; i < annotationParams.length; i+=2) { params.put(annotationParams[i],annotationParams[i+1]); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java index 42efa399a..b8efd5937 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java @@ -19,13 +19,11 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.RelationshipType; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.mapping.RelationshipInfo; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import java.lang.reflect.Field; - public class ReadOnlyOneToNRelationshipFieldAccessorFactory extends NodeRelationshipFieldAccessorFactory { public ReadOnlyOneToNRelationshipFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { @@ -33,30 +31,30 @@ public class ReadOnlyOneToNRelationshipFieldAccessorFactory extends NodeRelation } @Override - public boolean accept(final Neo4JPersistentProperty f) { + public boolean accept(final Neo4jPersistentProperty f) { if (!f.isRelationship()) return false; final RelationshipInfo info = f.getRelationshipInfo(); return info.isMultiple() && info.targetsNodes() && info.isReadonly(); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty property) { + public FieldAccessor forField(final Neo4jPersistentProperty property) { final RelationshipInfo relationshipInfo = property.getRelationshipInfo(); - return new ReadOnlyOneToNRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class) property.getRelationshipInfo().getTargetType().getType(), graphDatabaseContext,property); + return new ReadOnlyOneToNRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class) property.getRelationshipInfo().getTargetType().getType(), graphDatabaseContext,property); } public static class ReadOnlyOneToNRelationshipFieldAccessor extends OneToNRelationshipFieldAccessorFactory.OneToNRelationshipFieldAccessor { - public ReadOnlyOneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty field) { + public ReadOnlyOneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class elementClass, final GraphDatabaseContext graphDatabaseContext, Neo4jPersistentProperty field) { super(type,direction,elementClass, graphDatabaseContext, field); } @Override - public boolean isWriteable(NodeBacked nodeBacked) { + public boolean isWriteable(Object entity) { return false; } - public Object setValue(final NodeBacked entity, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipDelegatingFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipDelegatingFieldAccessorFactory.java index c8a7be125..c98ab27e9 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipDelegatingFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipDelegatingFieldAccessorFactory.java @@ -18,33 +18,33 @@ package org.springframework.data.neo4j.fieldaccess; import java.util.Arrays; import java.util.Collection; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.neo4j.support.GraphDatabaseContext; -public class RelationshipDelegatingFieldAccessorFactory extends DelegatingFieldAccessorFactory { +public class RelationshipDelegatingFieldAccessorFactory extends DelegatingFieldAccessorFactory { public RelationshipDelegatingFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { super(graphDatabaseContext); } @Override - protected Collection> createListenerFactories() { - return Arrays.>asList( + protected Collection createListenerFactories() { + return Arrays.asList( new IndexingPropertyFieldAccessorListenerFactory( graphDatabaseContext, - new PropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()), - new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()) + new PropertyFieldAccessorFactory(graphDatabaseContext), + new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext) )); } @Override - protected Collection> createAccessorFactories() { - return Arrays.>asList( + protected Collection createAccessorFactories() { + return Arrays.asList( new TransientFieldAccessorFactory(), new RelationshipNodeFieldAccessorFactory(graphDatabaseContext), - new PropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()), - new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext.getConversionService()), - new DynamicPropertiesFieldAccessorFactory(graphDatabaseContext.getConversionService()) + new PropertyFieldAccessorFactory(graphDatabaseContext), + new ConvertingNodePropertyFieldAccessorFactory(graphDatabaseContext), + new DynamicPropertiesFieldAccessorFactory(graphDatabaseContext) ); } } \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipNodeFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipNodeFieldAccessorFactory.java index e131a45ff..aad379144 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipNodeFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/RelationshipNodeFieldAccessorFactory.java @@ -21,9 +21,9 @@ import org.neo4j.graphdb.Relationship; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.neo4j.annotation.EndNode; import org.springframework.data.neo4j.annotation.StartNode; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; import static org.springframework.data.neo4j.support.DoReturn.doReturn; @@ -32,7 +32,7 @@ import static org.springframework.data.neo4j.support.DoReturn.doReturn; * @author Michael Hunger * @since 21.09.2010 */ -public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactory { +public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactory { private GraphDatabaseContext graphDatabaseContext; @@ -42,20 +42,20 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor } @Override - public boolean accept(final Neo4JPersistentProperty f) { + public boolean accept(final Neo4jPersistentProperty f) { return isStartNodeField(f) || isEndNodeField(f); } - private boolean isEndNodeField(final Neo4JPersistentProperty f) { + private boolean isEndNodeField(final Neo4jPersistentProperty f) { return f.isAnnotationPresent(EndNode.class); } - private boolean isStartNodeField(final Neo4JPersistentProperty f) { + private boolean isStartNodeField(final Neo4jPersistentProperty f) { return f.isAnnotationPresent(StartNode.class); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty property) { + public FieldAccessor forField(final Neo4jPersistentProperty property) { if (isStartNodeField(property)) { return new RelationshipNodeFieldAccessor(property, graphDatabaseContext) { @Override @@ -76,36 +76,36 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor return null; } - public static abstract class RelationshipNodeFieldAccessor implements FieldAccessor { + public static abstract class RelationshipNodeFieldAccessor implements FieldAccessor { - private final Neo4JPersistentProperty property; + private final Neo4jPersistentProperty property; private final GraphDatabaseContext graphDatabaseContext; - public RelationshipNodeFieldAccessor(final Neo4JPersistentProperty property, final GraphDatabaseContext graphDatabaseContext) { + public RelationshipNodeFieldAccessor(final Neo4jPersistentProperty property, final GraphDatabaseContext graphDatabaseContext) { this.property = property; this.graphDatabaseContext = graphDatabaseContext; } @Override - public Object setValue(final RelationshipBacked relationshipBacked, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { throw new InvalidDataAccessApiUsageException("Cannot change start or end node of existing relationship."); } @Override - public Object getValue(final RelationshipBacked relationshipBacked) { - final Relationship relationship = relationshipBacked.getPersistentState(); + public Object getValue(final Object entity) { + final Relationship relationship = graphDatabaseContext.getPersistentState(entity); final Node node = getNode(relationship); if (node == null) { return null; } - final NodeBacked result = graphDatabaseContext.createEntityFromState(node, (Class) property.getType()); + final Object result = graphDatabaseContext.createEntityFromState(node, (Class) property.getType()); return doReturn(result); } protected abstract Node getNode(Relationship relationship); @Override - public boolean isWriteable(final RelationshipBacked relationshipBacked) { + public boolean isWriteable(final Object entity) { return false; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java index ac5b114af..bdd5eb47d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java @@ -19,13 +19,11 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.RelationshipType; -import org.springframework.data.neo4j.annotation.RelatedTo; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.mapping.RelationshipInfo; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import java.lang.reflect.Field; import java.util.Collections; import java.util.Set; @@ -38,23 +36,23 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel } @Override - public boolean accept(final Neo4JPersistentProperty property) { + public boolean accept(final Neo4jPersistentProperty property) { return property.isRelationship() && property.getRelationshipInfo().targetsNodes() && !property.getRelationshipInfo().isMultiple(); } @Override - public FieldAccessor forField(final Neo4JPersistentProperty property) { + public FieldAccessor forField(final Neo4jPersistentProperty property) { final RelationshipInfo relationshipInfo = property.getRelationshipInfo(); - return new SingleRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class) relationshipInfo.getTargetType().getType(), graphDatabaseContext,property); + return new SingleRelationshipFieldAccessor(relationshipInfo.getRelationshipType(), relationshipInfo.getDirection(), (Class) relationshipInfo.getTargetType().getType(), graphDatabaseContext,property); } - public static class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor { - public SingleRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class clazz, final GraphDatabaseContext graphDatabaseContext, Neo4JPersistentProperty property) { + public static class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor { + public SingleRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class clazz, final GraphDatabaseContext graphDatabaseContext, Neo4jPersistentProperty property) { super(clazz, graphDatabaseContext, direction, type, property); } @Override - public Object setValue(final NodeBacked entity, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { final Node node=checkUnderlyingNode(entity); if (newVal == null) { removeMissingRelationships(node, Collections.emptySet()); @@ -67,10 +65,10 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel } @Override - public Object getValue(final NodeBacked entity) { + public Object getValue(final Object entity) { checkUnderlyingNode(entity); - final Set result = createEntitySetFromRelationshipEndNodes(entity); - final NodeBacked singleEntity = result.isEmpty() ? null : result.iterator().next(); + final Set result = createEntitySetFromRelationshipEndNodes(entity); + final Object singleEntity = result.isEmpty() ? null : result.iterator().next(); return doReturn(singleEntity); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TransientFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TransientFieldAccessorFactory.java index d0fe090ac..af4b34c03 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TransientFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TransientFieldAccessorFactory.java @@ -17,17 +17,17 @@ package org.springframework.data.neo4j.fieldaccess; import org.neo4j.graphdb.PropertyContainer; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; -public class TransientFieldAccessorFactory implements FieldAccessorFactory> { +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; + +public class TransientFieldAccessorFactory implements FieldAccessorFactory { @Override - public boolean accept(final Neo4JPersistentProperty property) { + public boolean accept(final Neo4jPersistentProperty property) { return property.isTransient(); } @Override - public FieldAccessor> forField(final Neo4JPersistentProperty property) { + public FieldAccessor forField(final Neo4jPersistentProperty property) { return new TransientFieldAccessor(property); } @@ -35,25 +35,25 @@ public class TransientFieldAccessorFactory implements FieldAccessorFactory> { - protected final Neo4JPersistentProperty property; + public static class TransientFieldAccessor implements FieldAccessor { + protected final Neo4jPersistentProperty property; - public TransientFieldAccessor(final Neo4JPersistentProperty property) { + public TransientFieldAccessor(final Neo4jPersistentProperty property) { this.property = property; } @Override - public Object setValue(final GraphBacked graphBacked, final Object newVal) { + public Object setValue(final Object entity, final Object newVal) { return newVal; } @Override - public boolean isWriteable(final GraphBacked graphBacked) { + public boolean isWriteable(final Object entity) { return true; } @Override - public Object getValue(final GraphBacked graphBacked) { + public Object getValue(final Object entity) { return null; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java index 30cb98074..9bdc0ed76 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java @@ -23,19 +23,24 @@ import org.neo4j.graphdb.traversal.TraversalDescription; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.neo4j.annotation.GraphTraversal; import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; -import org.springframework.data.neo4j.support.GenericTypeExtractor; + + +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import static org.springframework.data.neo4j.support.DoReturn.doReturn; -public class TraversalFieldAccessorFactory implements FieldAccessorFactory { - @Override - public boolean accept(final Neo4JPersistentProperty f) { +public class TraversalFieldAccessorFactory implements FieldAccessorFactory { + private final GraphDatabaseContext graphDatabaseContext; + + public TraversalFieldAccessorFactory(GraphDatabaseContext graphDatabaseContext) { + this.graphDatabaseContext = graphDatabaseContext; + } + + @Override + public boolean accept(final Neo4jPersistentProperty f) { final GraphTraversal graphEntityTraversal = f.getAnnotation(GraphTraversal.class); return graphEntityTraversal != null && graphEntityTraversal.traversalBuilder() != FieldTraversalDescriptionBuilder.class @@ -44,32 +49,36 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory forField(final Neo4JPersistentProperty property) { - return new TraversalFieldAccessor(property); + public FieldAccessor forField(final Neo4jPersistentProperty property) { + return new TraversalFieldAccessor(property,graphDatabaseContext); } /** * @author Michael Hunger * @since 12.09.2010 */ - public static class TraversalFieldAccessor implements FieldAccessor { - protected final Neo4JPersistentProperty property; - private final FieldTraversalDescriptionBuilder fieldTraversalDescriptionBuilder; + public static class TraversalFieldAccessor implements FieldAccessor { + protected final Neo4jPersistentProperty property; + private final GraphDatabaseContext graphDatabaseContext; + private final FieldTraversalDescriptionBuilder fieldTraversalDescriptionBuilder; private Class target; protected String[] params; - public TraversalFieldAccessor(final Neo4JPersistentProperty property) { + public TraversalFieldAccessor(final Neo4jPersistentProperty property, GraphDatabaseContext graphDatabaseContext) { this.property = property; + this.graphDatabaseContext = graphDatabaseContext; final GraphTraversal graphEntityTraversal = property.getAnnotation(GraphTraversal.class); this.target = resolveTarget(graphEntityTraversal,property); this.params = graphEntityTraversal.params(); this.fieldTraversalDescriptionBuilder = createTraversalDescription(graphEntityTraversal); } - private Class resolveTarget(GraphTraversal graphTraversal, Neo4JPersistentProperty property) { - if (!graphTraversal.elementClass().equals(NodeBacked.class)) return graphTraversal.elementClass(); + private Class resolveTarget(GraphTraversal graphTraversal, Neo4jPersistentProperty property) { + if (!graphTraversal.elementClass().equals(Object.class)) return graphTraversal.elementClass(); final Class result = property.getTypeInformation().getActualType().getType(); - Class[] allowedTypes={NodeBacked.class,RelationshipBacked.class,Node.class,Relationship.class, Path.class}; + if (graphDatabaseContext.isNodeEntity(result)) return result; + if (graphDatabaseContext.isRelationshipEntity(result)) return result; + Class[] allowedTypes={Node.class,Relationship.class, Path.class}; if (!checkTypes(result,allowedTypes)) throw new IllegalArgumentException("The target result type "+result+" of the traversal is no subclass of the allowed types: "+property+" "+allowedTypes); return result; } @@ -83,19 +92,19 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory> implements FieldAccessorListenerFactory { +class ValidatingNodePropertyFieldAccessorListenerFactory implements FieldAccessorListenerFactory { private final GraphDatabaseContext graphDatabaseContext; @@ -43,11 +41,11 @@ class ValidatingNodePropertyFieldAccessorListenerFactory forField(Neo4JPersistentProperty property) { + public FieldAccessListener forField(Neo4jPersistentProperty property) { return new ValidatingNodePropertyFieldAccessorListener(property,graphDatabaseContext.getValidator()); } @@ -64,21 +62,21 @@ class ValidatingNodePropertyFieldAccessorListenerFactory implements FieldAccessListener, Object> { + public static class ValidatingNodePropertyFieldAccessorListener implements FieldAccessListener { private final static Log log = LogFactory.getLog( ValidatingNodePropertyFieldAccessorListener.class ); private String propertyName; private Validator validator; - private Neo4JPersistentEntity entityType; + private Neo4jPersistentEntity entityType; - public ValidatingNodePropertyFieldAccessorListener(final Neo4JPersistentProperty field, Validator validator) { + public ValidatingNodePropertyFieldAccessorListener(final Neo4jPersistentProperty field, Validator validator) { this.propertyName = field.getName(); - this.entityType = (Neo4JPersistentEntity) field.getOwner(); + this.entityType = (Neo4jPersistentEntity) field.getOwner(); this.validator = validator; } @Override - public void valueChanged(GraphBacked graphBacked, Object oldVal, Object newVal) { + public void valueChanged(Object entity, Object oldVal, Object newVal) { if (validator==null) return; Set> constraintViolations = validator.validateValue((Class)entityType.getType(), propertyName, newVal); if (!constraintViolations.isEmpty()) throw new ValidationException("Error validating field "+propertyName+ " of "+entityType+": "+constraintViolations); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JMappingContext.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JMappingContext.java index 2c02c8a73..589eb8866 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JMappingContext.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JMappingContext.java @@ -16,6 +16,7 @@ package org.springframework.data.neo4j.mapping; +import org.neo4j.graphdb.PropertyContainer; import org.springframework.data.mapping.context.AbstractMappingContext; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -25,20 +26,20 @@ import java.beans.PropertyDescriptor; import java.lang.reflect.Field; /** - * Neo4J specific {@link MappingContext} implementation. Simply creates {@link Neo4JPersistentEntityImpl} and - * {@link Neo4JPersistentProperty} instances. + * Neo4J specific {@link MappingContext} implementation. Simply creates {@link Neo4jPersistentEntityImpl} and + * {@link Neo4jPersistentProperty} instances. * * @author Oliver Gierke */ -public class Neo4JMappingContext extends AbstractMappingContext, Neo4JPersistentProperty> { +public class Neo4jMappingContext extends AbstractMappingContext, Neo4jPersistentProperty> { /* * (non-Javadoc) * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) */ @Override - protected Neo4JPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { - return new Neo4JPersistentEntityImpl(typeInformation); + protected Neo4jPersistentEntityImpl createPersistentEntity(TypeInformation typeInformation) { + return new Neo4jPersistentEntityImpl(typeInformation); } /* @@ -46,8 +47,22 @@ public class Neo4JMappingContext extends AbstractMappingContext owner, SimpleTypeHolder simpleTypeHolder) { - return new Neo4JPersistentPropertyImpl(field, descriptor, owner, simpleTypeHolder); + protected Neo4jPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, + Neo4jPersistentEntityImpl owner, SimpleTypeHolder simpleTypeHolder) { + return new Neo4jPersistentPropertyImpl(field, descriptor, owner, simpleTypeHolder); } + + public boolean isNodeEntity(Class type) { + return getPersistentEntity(type).isNodeEntity(); + } + + public boolean isRelationshipEntity(Class type) { + return getPersistentEntity(type).isRelationshipEntity(); + } + + public void setPersistentState(Object entity, PropertyContainer pc) { + final Neo4jPersistentEntityImpl persistentEntity = getPersistentEntity(entity.getClass()); + persistentEntity.setPersistentState(entity, pc); + } + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntity.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntity.java index 99d30469a..dd247864a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntity.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntity.java @@ -16,14 +16,25 @@ package org.springframework.data.neo4j.mapping; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.PropertyContainer; import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * Interface for Neo4J specific {@link PersistentEntity}. * * @author Oliver Gierke */ -public interface Neo4JPersistentEntity extends PersistentEntity { +public interface Neo4jPersistentEntity extends PersistentEntity { boolean useShortNames(); + + boolean isNodeEntity(); + + boolean isRelationshipEntity(); + + void setPersistentState(Object entity, PropertyContainer pc); + + T getPersistentState(Object entity, GraphDatabaseContext service); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntityImpl.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntityImpl.java index 4dfc3c0f2..afddc2a90 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntityImpl.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentEntityImpl.java @@ -16,9 +16,14 @@ package org.springframework.data.neo4j.mapping; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.RelationshipEntity; +import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.util.TypeInformation; import java.lang.annotation.Annotation; @@ -26,20 +31,20 @@ import java.util.IdentityHashMap; import java.util.Map; /** - * Implementation of {@link Neo4JPersistentEntity}. + * Implementation of {@link Neo4jPersistentEntity}. * * @author Oliver Gierke */ -class Neo4JPersistentEntityImpl extends BasicPersistentEntity implements Neo4JPersistentEntity { +public class Neo4jPersistentEntityImpl extends BasicPersistentEntity implements Neo4jPersistentEntity { private Map,Annotation> annotations=new IdentityHashMap,Annotation>(); /** - * Creates a new {@link Neo4JPersistentEntityImpl} instance. + * Creates a new {@link Neo4jPersistentEntityImpl} instance. * * @param information must not be {@literal null}. */ - public Neo4JPersistentEntityImpl(TypeInformation information) { + public Neo4jPersistentEntityImpl(TypeInformation information) { super(information); for (Annotation annotation : information.getType().getAnnotations()) { annotations.put(annotation.annotationType(),annotation); @@ -54,7 +59,59 @@ class Neo4JPersistentEntityImpl extends BasicPersistentEntity T getAnnotation(Class annotationType) { return (T) annotations.get(annotationType); } + + private boolean hasAnnotation(Class annotationType) { + return annotations.containsKey(annotationType); + } + + @Override + public void setPersistentState(Object entity, PropertyContainer state) { + final Neo4jPersistentProperty idProperty = getIdProperty(); + Object id = getStateId(state); + idProperty.setValue(entity, id); + } + + private Object getStateId(PropertyContainer state) { + if (state==null) return null; + if (isNodeEntity()) { + return ((Node) state).getId(); + } + if (isRelationshipEntity()) { + return ((Relationship)state).getId(); + } + throw new MappingException("Entity has to be annotated with @NodeEntity or @RelationshipEntity"+this); + } + + @Override + public T getPersistentState(Object entity, GraphDatabaseContext service) { + final Neo4jPersistentProperty idProperty = getIdProperty(); + if (idProperty==null) throw new MappingException("No field annotated with @GraphId found in "+ getEntityName()); + if (!Number.class.isAssignableFrom(idProperty.getType())) throw new IllegalArgumentException("The id of "+getEntityName()+" "+idProperty+" is not a number"); + final Number id = (Number) idProperty.getValue(entity); + if (id==null) return null; // todo create new node? + if (isNodeEntity()) { + return (T) service.getNodeById(id.longValue()); + } + if (isRelationshipEntity()) { + return (T) service.getRelationshipById(id.longValue()); + } + throw new IllegalArgumentException("The entity "+getEntityName()+" has to be either annotated with @NodeEntity or @RelationshipEntity"); + } + + public String getEntityName() { + return getType().getName(); + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentProperty.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentProperty.java index d4c2ca3dc..850767f9f 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentProperty.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentProperty.java @@ -18,9 +18,7 @@ package org.springframework.data.neo4j.mapping; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.neo4j.annotation.Indexed; -import javax.persistence.Id; import java.lang.annotation.Annotation; import java.util.Collection; @@ -30,7 +28,7 @@ import java.util.Collection; * * @author Oliver Gierke */ -public interface Neo4JPersistentProperty extends PersistentProperty { +public interface Neo4jPersistentProperty extends PersistentProperty { /** * Returns whether the property represents a relationship. If this returns {@literal true}, clients can expect @@ -50,7 +48,7 @@ public interface Neo4JPersistentProperty extends PersistentProperty boolean isAnnotationPresent(Class annotationType); - void setValue(Object entity, Object newValue) throws IllegalAccessException; + void setValue(Object entity, Object newValue); - Object getValue(final Object entity) throws IllegalAccessException; + Object getValue(final Object entity); + + Neo4jPersistentEntity getOwner(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java index 13a09873a..173badd68 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4JPersistentPropertyImpl.java @@ -20,10 +20,11 @@ import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.model.AbstractPersistentProperty; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.neo4j.annotation.*; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.util.TypeInformation; import java.beans.PropertyDescriptor; @@ -34,20 +35,20 @@ import java.util.IdentityHashMap; import java.util.Map; /** - * Implementation of {@link Neo4JPersistentProperty}. + * Implementation of {@link Neo4jPersistentProperty}. * * @author Oliver Gierke */ -class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty implements - Neo4JPersistentProperty { +class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty implements + Neo4jPersistentProperty { private final RelationshipInfo relationshipInfo; private final boolean isIdProperty; private IndexInfo indexInfo; private Map, ? extends Annotation> annotations; - public Neo4JPersistentPropertyImpl(Field field, PropertyDescriptor propertyDescriptor, - PersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + public Neo4jPersistentPropertyImpl(Field field, PropertyDescriptor propertyDescriptor, + PersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { super(field, propertyDescriptor, owner, simpleTypeHolder); this.annotations = extractAnnotations(field); this.relationshipInfo = extractRelationshipInfo(field); @@ -91,9 +92,13 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty typeInformation, final Class annotationClass) { @@ -106,8 +111,8 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty createAssociation() { - return new Association(this, null); + protected Association createAssociation() { + return new Association(this, null); } @Override @@ -131,16 +136,14 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty type = getType(); - if (Iterable.class.isAssignableFrom(type) || NodeBacked.class.isAssignableFrom(type) || RelationshipBacked.class.isAssignableFrom(type)) - return false; - return true; + return !(getTypeInformation().isCollectionLike() || isRelationship()); } public boolean isSerializableField(final ConversionService conversionService) { @@ -175,10 +178,14 @@ class Neo4JPersistentPropertyImpl extends AbstractPersistentProperty getOwner() { + return (Neo4jPersistentEntity)super.getOwner(); + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverter.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverter.java new file mode 100644 index 000000000..ef75ea73b --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverter.java @@ -0,0 +1,30 @@ +/** + * 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.Node; +import org.springframework.data.convert.EntityConverter; +import org.springframework.data.convert.EntityReader; +import org.springframework.data.convert.EntityWriter; + +/** + * @author mh + * @since 27.09.11 + */ +public interface Neo4jNodeConverter extends EntityConverter, Neo4jPersistentProperty, Object, Node>, EntityWriter, + EntityReader { + +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverterImpl.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverterImpl.java new file mode 100644 index 000000000..5334ebcde --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverterImpl.java @@ -0,0 +1,183 @@ +/** + * 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.Node; +import org.neo4j.graphdb.NotFoundException; +import org.neo4j.graphdb.Transaction; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.AssociationHandler; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PropertyHandler; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.model.BeanWrapper; +import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.neo4j.core.EntityState; + +import org.springframework.data.neo4j.support.DoReturn; +import org.springframework.data.neo4j.support.GraphDatabaseContext; +import org.springframework.data.neo4j.support.node.NodeEntityStateFactory; +import org.springframework.util.Assert; + +import javax.annotation.PostConstruct; +import java.lang.reflect.InvocationTargetException; + +/** + * @author mh + * @since 27.09.11 + */ +public class Neo4jNodeConverterImpl implements Neo4jNodeConverter { + private final NodeEntityStateFactory nodeEntityStateFactory; + + public Neo4jNodeConverterImpl(NodeEntityStateFactory nodeEntityStateFactory) { + this.nodeEntityStateFactory = nodeEntityStateFactory; + nodeEntityStateFactory.setCreateDetachableEntities(false); + } + + @Override + public MappingContext, Neo4jPersistentProperty> getMappingContext() { + return nodeEntityStateFactory.getMappingContext(); + } + + @Override + public ConversionService getConversionService() { + return getGraphDatabaseContext().getConversionService(); + } + + @Override + public R read(Class targetType, Node node) { + Assert.notNull(targetType); + Assert.notNull(node); + final Neo4jPersistentEntity persistentEntity = (Neo4jPersistentEntity) getMappingContext().getPersistentEntity(targetType); + final GraphDatabaseContext graphDatabaseContext = nodeEntityStateFactory.getGraphDatabaseContext(); + final R entity = graphDatabaseContext.createEntityFromState(node, targetType); + final BeanWrapper, R> wrapper = BeanWrapper.create(entity, getConversionService()); + final Transaction tx = getGraphDatabaseContext().beginTx(); + try { + //final Node targetNode = useGetOrCreateNode(node, persistentEntity, wrapper); + final EntityState nodeState = nodeEntityStateFactory.getEntityState(entity); + nodeState.setPersistentState(node); + nodeState.persist(); + persistentEntity.doWithProperties(new PropertyHandler() { + @Override + public void doWithPersistentProperty(Neo4jPersistentProperty property) { + if (!nodeState.isWritable(property.getField())) return; + final Object value = nodeState.getValue(property.getField()); + setProperty(wrapper, property, value); + } + }); + persistentEntity.doWithAssociations(new AssociationHandler() { + @Override + public void doWithAssociation(Association association) { + final Neo4jPersistentProperty property = association.getInverse(); + if (!nodeState.isWritable(property.getField())) return; + final Object value = nodeState.getValue(property.getField()); + setProperty(wrapper, property, value); + } + }); + tx.success(); + return entity; + } finally { + tx.finish(); + } + } + + @Override + public void write(Object source, final Node node) { + Assert.notNull(source); + final Neo4jPersistentEntity persistentEntity = getMappingContext().getPersistentEntity(source.getClass()); + final BeanWrapper, Object> wrapper = BeanWrapper.create(source, getConversionService()); + final Transaction tx = getGraphDatabaseContext().beginTx(); + try { + //final Node targetNode = useGetOrCreateNode(node, persistentEntity, wrapper); + final EntityState nodeState = nodeEntityStateFactory.getEntityState(source); + nodeState.setPersistentState(node); + nodeState.persist(); + persistentEntity.doWithProperties(new PropertyHandler() { + @Override + public void doWithPersistentProperty(Neo4jPersistentProperty property) { + if (!nodeState.isWritable(property.getField())) return; + final Object value = getProperty(wrapper, property); + nodeState.setValue(property, value); + } + }); + persistentEntity.doWithAssociations(new AssociationHandler() { + @Override + public void doWithAssociation(Association association) { + final Neo4jPersistentProperty property = association.getInverse(); + if (!nodeState.isWritable(property.getField())) return; + final Object value = getProperty(wrapper, property); + nodeState.setValue(property, value); + } + }); + tx.success(); + } finally { + tx.finish(); + } + } + + private Node useGetOrCreateNode(Node node, Neo4jPersistentEntity persistentEntity, BeanWrapper, Object> wrapper) { + if (node != null) return node; + final Neo4jPersistentProperty idProperty = persistentEntity.getIdProperty(); + final Long id = getProperty(wrapper, idProperty, Long.class, true); + if (id == null) { + final Node newNode = getGraphDatabaseContext().createNode(); + setProperty(wrapper, idProperty, newNode.getId()); + return newNode; + } + try { + return getGraphDatabaseContext().getNodeById(id); + } catch (NotFoundException nfe) { + throw new MappingException("Could not find node with id " + id); + } + } + + private GraphDatabaseContext getGraphDatabaseContext() { + return nodeEntityStateFactory.getGraphDatabaseContext(); + } + + private T getProperty(BeanWrapper, Object> wrapper, Neo4jPersistentProperty property, Class type, boolean fieldAccessOnly) { + try { + return wrapper.getProperty(property, type, fieldAccessOnly); + } catch (IllegalAccessException e) { + throw new MappingException("Error retrieving property " + property.getName() + " from " + wrapper.getBean(), e); + } catch (InvocationTargetException e) { + throw new MappingException("Error retrieving property " + property.getName() + " from " + wrapper.getBean(), e.getTargetException()); + } + } + + private Object getProperty(BeanWrapper, Object> wrapper, Neo4jPersistentProperty property) { + try { + return wrapper.getProperty(property); + } catch (IllegalAccessException e) { + throw new MappingException("Error retrieving property " + property.getName() + " from " + wrapper.getBean(), e); + } catch (InvocationTargetException e) { + throw new MappingException("Error retrieving property " + property.getName() + " from " + wrapper.getBean(), e.getTargetException()); + } + } + + private void setProperty(BeanWrapper, ?> wrapper, Neo4jPersistentProperty property, Object value) { + try { + wrapper.setProperty(property, DoReturn.unwrap(value)); + } catch (IllegalAccessException e) { + throw new MappingException("Setting property " + property.getName() + " to " + value + " on " + wrapper.getBean(), e); + } catch (InvocationTargetException e) { + throw new MappingException("Setting property " + property.getName() + " to " + value + " on " + wrapper.getBean(), e.getTargetException()); + } + } + +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/RelationshipInfo.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/RelationshipInfo.java index f108ff596..9fbfd51b4 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/RelationshipInfo.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/mapping/RelationshipInfo.java @@ -24,8 +24,8 @@ import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.RelatedTo; import org.springframework.data.neo4j.annotation.RelatedToVia; import org.springframework.data.neo4j.annotation.RelationshipEntity; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; @@ -80,7 +80,7 @@ public class RelationshipInfo { annotation.type().isEmpty() ? field.getName() : annotation.type(), annotation.direction(), typeInformation, - annotation.elementClass() != NodeBacked.class ? ClassTypeInformation.from(annotation.elementClass()) : null, + annotation.elementClass() != Object.class ? ClassTypeInformation.from(annotation.elementClass()) : null, true); } @@ -89,7 +89,7 @@ public class RelationshipInfo { annotation.type().isEmpty() ? field.getName() : annotation.type(), annotation.direction(), typeInformation, - annotation.elementClass() != RelationshipBacked.class ? ClassTypeInformation.from(annotation.elementClass()) : null, + annotation.elementClass() != Object.class ? ClassTypeInformation.from(annotation.elementClass()) : null, false); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java index 6a7c9a4fd..aed2065e1 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/AbstractGraphRepository.java @@ -17,8 +17,10 @@ package org.springframework.data.neo4j.repository; import org.apache.lucene.search.NumericRangeQuery; +import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexHits; import org.neo4j.helpers.collection.ClosableIterable; @@ -27,7 +29,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.neo4j.core.GraphBacked; + import org.springframework.data.neo4j.support.GraphDatabaseContext; import java.util.ArrayList; @@ -43,7 +45,7 @@ import java.util.List; * @param Type of backing state, either Node or Relationship */ @org.springframework.stereotype.Repository -public abstract class AbstractGraphRepository> implements GraphRepository, NamedIndexRepository { +public abstract class AbstractGraphRepository implements GraphRepository, NamedIndexRepository { public static final ClosableIterable EMPTY_CLOSABLE_ITERABLE = new ClosableIterable() { @Override public void close() { @@ -247,7 +249,16 @@ public abstract class AbstractGraphRepository entities) { for (T entity : entities) { - entity.remove(); + delete(entity); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/CRUDRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/CRUDRepository.java index 2be6aec0f..14bcd24f7 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/CRUDRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/CRUDRepository.java @@ -20,7 +20,7 @@ import org.neo4j.helpers.collection.ClosableIterable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.neo4j.core.GraphBacked; + import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.transaction.annotation.Transactional; @@ -29,7 +29,7 @@ import org.springframework.transaction.annotation.Transactional; * CRUD interface for graph repositories, used as base repository for crud operations */ @NoRepositoryBean -public interface CRUDRepository> extends PagingAndSortingRepository { +public interface CRUDRepository extends PagingAndSortingRepository { /** * persists an entity by forwarding to entity.persist() diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/DirectGraphRepositoryFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/DirectGraphRepositoryFactory.java index f5bf57deb..07e7e3a63 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/DirectGraphRepositoryFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/DirectGraphRepositoryFactory.java @@ -16,9 +16,9 @@ package org.springframework.data.neo4j.repository; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + + import org.springframework.data.neo4j.support.GraphDatabaseContext; /** @@ -33,9 +33,9 @@ public class DirectGraphRepositoryFactory { } @SuppressWarnings({"unchecked"}) - public > GraphRepository createGraphRepository(Class clazz) { - if (NodeBacked.class.isAssignableFrom(clazz)) return new NodeGraphRepository(clazz, graphDatabaseContext); - if (RelationshipBacked.class.isAssignableFrom(clazz)) return new RelationshipGraphRepository(clazz, graphDatabaseContext); + public GraphRepository createGraphRepository(Class clazz) { + if (graphDatabaseContext.isNodeEntity(clazz)) return new NodeGraphRepository(clazz, graphDatabaseContext); + if (graphDatabaseContext.isRelationshipEntity(clazz)) return new RelationshipGraphRepository(clazz, graphDatabaseContext); throw new IllegalArgumentException("Can't create graph repository for non graph entity of type "+clazz); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphEntityInformation.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphEntityInformation.java index 132847016..e3432ddeb 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphEntityInformation.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphEntityInformation.java @@ -17,14 +17,14 @@ package org.springframework.data.neo4j.repository; import org.neo4j.graphdb.PropertyContainer; -import org.springframework.data.neo4j.core.GraphBacked; + import org.springframework.data.repository.core.EntityInformation; /** * @author mh * @since 28.03.11 */ -public interface GraphEntityInformation> extends EntityInformation { +public interface GraphEntityInformation extends EntityInformation { boolean isNodeEntity(); boolean isPartialEntity(); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphMetamodelEntityInformation.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphMetamodelEntityInformation.java index 2e0605c46..2d6cacaee 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphMetamodelEntityInformation.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphMetamodelEntityInformation.java @@ -21,16 +21,18 @@ package org.springframework.data.neo4j.repository; * @since 28.03.11 */ +import org.neo4j.graphdb.Node; import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.RelationshipEntity; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + + import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.repository.core.support.AbstractEntityInformation; -public class GraphMetamodelEntityInformation> extends AbstractEntityInformation implements GraphEntityInformation { +public class GraphMetamodelEntityInformation extends AbstractEntityInformation implements GraphEntityInformation { private GraphDatabaseContext graphDatabaseContext; private final RelationshipEntity relationshipEntity; @@ -61,13 +63,19 @@ public class GraphMetamodelEntityInformation> extends CRUDRepository, IndexRepository, TraversalRepository { +public interface GraphRepository extends CRUDRepository, IndexRepository, TraversalRepository { } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java index 034a8abcf..cb7cec0df 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactory.java @@ -16,6 +16,9 @@ package org.springframework.data.neo4j.repository; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; import org.neo4j.helpers.collection.IteratorUtil; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -24,8 +27,8 @@ import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.annotation.QueryType; import org.springframework.data.neo4j.annotation.RelationshipEntity; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.neo4j.support.GenericTypeExtractor; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.conversion.EntityResultConverter; @@ -165,11 +168,11 @@ public class GraphRepositoryFactory extends RepositoryFactorySupport { } - private Map resolveParams(Object[] parameters) { + private Map resolveParams(Object[] parameters, GraphDatabaseContext graphDatabaseContext) { Map params=new HashMap(); for (Parameter parameter : getParameters().getBindableParameters()) { final Object value = parameters[parameter.getIndex()]; - params.put(parameter.getName(),resolveParameter(value)); + params.put(parameter.getName(),resolveParameter(value,graphDatabaseContext)); } return params; } @@ -200,12 +203,14 @@ public class GraphRepositoryFactory extends RepositoryFactorySupport { return result; } - private Object resolveParameter(Object parameter) { - if (parameter instanceof NodeBacked) { - return ((NodeBacked)parameter).getNodeId(); + private Object resolveParameter(Object parameter, GraphDatabaseContext graphDatabaseContext) { + final Class type = parameter.getClass(); + final PropertyContainer state = graphDatabaseContext.getPersistentState(parameter); + if (graphDatabaseContext.isNodeEntity(type)) { + return ((Node) state).getId(); } - if (parameter instanceof RelationshipBacked) { - return ((RelationshipBacked)parameter).getRelationshipId(); + if (graphDatabaseContext.isRelationshipEntity(type)) { + return ((Relationship)state).getId(); } return parameter; } @@ -308,14 +313,16 @@ public class GraphRepositoryFactory extends RepositoryFactorySupport { private static abstract class GraphRepositoryQuery implements RepositoryQuery { private final GraphQueryMethod queryMethod; + private final GraphDatabaseContext graphDatabaseContext; public GraphRepositoryQuery(GraphQueryMethod queryMethod, RepositoryMetadata metadata, final GraphDatabaseContext graphDatabaseContext) { this.queryMethod = queryMethod; + this.graphDatabaseContext = graphDatabaseContext; } @Override public Object execute(Object[] parameters) { - Map params = queryMethod.resolveParams(parameters); + Map params = queryMethod.resolveParams(parameters,graphDatabaseContext); final String queryString = queryMethod.prepareQuery(parameters); return dispatchQuery(queryString,params,queryMethod.getPageable(parameters)); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactoryBean.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactoryBean.java index 0b5380daa..e69d7d02b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactoryBean.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/GraphRepositoryFactoryBean.java @@ -17,7 +17,8 @@ package org.springframework.data.neo4j.repository; import org.neo4j.graphdb.PropertyContainer; -import org.springframework.data.neo4j.core.GraphBacked; + +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport; @@ -27,7 +28,7 @@ import org.springframework.util.Assert; * @author mh * @since 28.03.11 */ -public class GraphRepositoryFactoryBean, T extends GraphBacked> +public class GraphRepositoryFactoryBean, T> extends TransactionalRepositoryFactoryBeanSupport { private GraphDatabaseContext graphDatabaseContext; @@ -36,10 +37,8 @@ public class GraphRepositoryFactoryBean> { +public interface IndexRepository { T findByPropertyValue(String property, Object value); ClosableIterable findAllByPropertyValue(String property, Object value); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NamedIndexRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NamedIndexRepository.java index 62b9524df..14a257a12 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NamedIndexRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NamedIndexRepository.java @@ -17,13 +17,13 @@ package org.springframework.data.neo4j.repository; import org.neo4j.helpers.collection.ClosableIterable; -import org.springframework.data.neo4j.core.GraphBacked; + /** * @author mh * @since 29.03.11 */ -public interface NamedIndexRepository> { +public interface NamedIndexRepository { T findByPropertyValue(String indexName, String property, Object value); ClosableIterable findAllByPropertyValue(String indexName, String property, Object value); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NodeGraphRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NodeGraphRepository.java index c2cd5bc4d..54abbba5c 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NodeGraphRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/NodeGraphRepository.java @@ -18,10 +18,10 @@ package org.springframework.data.neo4j.repository; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.traversal.TraversalDescription; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.support.GraphDatabaseContext; -public class NodeGraphRepository extends AbstractGraphRepository implements GraphRepository { +public class NodeGraphRepository extends AbstractGraphRepository implements GraphRepository { public NodeGraphRepository(final Class clazz, final GraphDatabaseContext graphDatabaseContext) { super(graphDatabaseContext, clazz); @@ -33,13 +33,13 @@ public class NodeGraphRepository extends AbstractGraphRepo } @Override - public Iterable findAllByTraversal(final N startNode, final TraversalDescription traversalDescription) { - return (Iterable) startNode.findAllByTraversal((Class) clazz, traversalDescription); + public Iterable findAllByTraversal(final N start, final TraversalDescription traversalDescription) { + return graphDatabaseContext.findAllByTraversal(start, clazz, traversalDescription); } @Override public T save(T entity) { - return (T) ((NodeBacked)entity).persist(); + return (T)graphDatabaseContext.save(entity); } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/RelationshipGraphRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/RelationshipGraphRepository.java index 2234d0f8b..c4aca4152 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/RelationshipGraphRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/RelationshipGraphRepository.java @@ -18,11 +18,11 @@ package org.springframework.data.neo4j.repository; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.traversal.TraversalDescription; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.neo4j.support.GraphDatabaseContext; -public class RelationshipGraphRepository extends AbstractGraphRepository implements GraphRepository { +public class RelationshipGraphRepository extends AbstractGraphRepository implements GraphRepository { public RelationshipGraphRepository(final Class clazz, final GraphDatabaseContext graphDatabaseContext) { super(graphDatabaseContext, clazz); @@ -34,7 +34,7 @@ public class RelationshipGraphRepository extends A } @Override - public Iterable findAllByTraversal(final N startNode, final TraversalDescription traversalDescription) { + public Iterable findAllByTraversal(final N startNode, final TraversalDescription traversalDescription) { throw new UnsupportedOperationException("Traversal not able to start at relationship"); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/TraversalRepository.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/TraversalRepository.java index 3f4a0ca69..f5da0a49b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/TraversalRepository.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/TraversalRepository.java @@ -17,14 +17,14 @@ package org.springframework.data.neo4j.repository; import org.neo4j.graphdb.traversal.TraversalDescription; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; + + /** * @author mh * @since 29.03.11 */ -public interface TraversalRepository> { +public interface TraversalRepository { /** * Traversal based finder that returns a lazy Iterable over the traversal results * @@ -33,5 +33,5 @@ public interface TraversalRepository> { * @param Start node entity type * @return Iterable over traversal result */ - Iterable findAllByTraversal(N startNode, TraversalDescription traversalDescription); + Iterable findAllByTraversal(N startNode, TraversalDescription traversalDescription); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java index 6d81d3f11..aa4f002d6 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/CypherQuery.java @@ -19,7 +19,7 @@ package org.springframework.data.neo4j.repository.query; import java.util.HashSet; import java.util.Set; -import org.springframework.data.neo4j.mapping.Neo4JMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; @@ -34,12 +34,12 @@ public class CypherQuery { private final String TEMPLATE = "start %s match % where %"; - private final Neo4JMappingContext context; + private final Neo4jMappingContext context; private final TypeInformation rootType; private final Set matchClauses; - public CypherQuery(TypeInformation rootType, Neo4JMappingContext context) { + public CypherQuery(TypeInformation rootType, Neo4jMappingContext context) { Assert.notNull(rootType); Assert.notNull(context); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/MatchClause.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/MatchClause.java index f7795710d..201121d18 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/MatchClause.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/MatchClause.java @@ -16,8 +16,8 @@ package org.springframework.data.neo4j.repository.query; -import org.springframework.data.neo4j.mapping.Neo4JMappingContext; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.mapping.RelationshipInfo; import org.springframework.data.repository.query.parser.Property; import org.springframework.util.Assert; @@ -30,15 +30,15 @@ import org.springframework.util.StringUtils; */ class MatchClause { - private final Iterable properties; + private final Iterable properties; /** - * Creates a new {@link MatchClause} using the given {@link Neo4JMappingContext} and {@link Property}. + * Creates a new {@link MatchClause} using the given {@link org.springframework.data.neo4j.mapping.Neo4jMappingContext} and {@link Property}. * * @param context must not be {@literal null}. * @param property must not be {@literal null}. */ - public MatchClause(Neo4JMappingContext context, Property property) { + public MatchClause(Neo4jMappingContext context, Property property) { Assert.notNull(context); Assert.notNull(property); @@ -56,7 +56,7 @@ class MatchClause { String intermediate = null; - for (Neo4JPersistentProperty property : properties) { + for (Neo4jPersistentProperty property : properties) { if (!property.isRelationship()) { return intermediate; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/AbstractConstructorEntityInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/AbstractConstructorEntityInstantiator.java new file mode 100644 index 000000000..419a79163 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/AbstractConstructorEntityInstantiator.java @@ -0,0 +1,169 @@ +/** + * 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 java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.data.persistence.StateBackedCreator; +import org.springframework.data.persistence.StateProvider; +import org.springframework.util.ClassUtils; +import sun.reflect.ReflectionFactory; + +/** + * Try for a constructor taking state: failing that, try a no-arg constructor and then setUnderlyingNode(). + * + * @author Rod Johnson + */ +public abstract class AbstractConstructorEntityInstantiator implements EntityInstantiator { + + private final Log log = LogFactory.getLog(getClass()); + private final Map, StateBackedCreator> cache = new HashMap, StateBackedCreator>(); + + public T createEntityFromState(STATE n, Class c) { + try { + StateBackedCreator creator = (StateBackedCreator) cache.get(c); + if (creator != null) + return creator.create(n, c); + synchronized (cache) { + creator = (StateBackedCreator) cache.get(c); + if (creator != null) + return creator.create(n, c); + Class stateClass = (Class) n.getClass(); + creator = createInstantiator(c, stateClass); + cache.put(c, creator); + return creator.create(n, c); + } + } catch (IllegalArgumentException e) { + throw e; + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e.getTargetException()); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + public void setInstantiators( + Map, StateBackedCreator> instantiators) { + this.cache.putAll(instantiators); + } + + protected StateBackedCreator createInstantiator(Class type, + final Class stateType) { + StateBackedCreator creator = stateTakingConstructorInstantiator(type, stateType); + if (creator != null) + return creator; + creator = emptyConstructorStateSettingInstantiator(type, stateType); + if (creator != null) + return creator; + return createFailingInstantiator(stateType); + } + + protected StateBackedCreator createFailingInstantiator( + final Class stateType) { + return new StateBackedCreator() { + public T create(STATE n, Class c) throws Exception { + throw new IllegalArgumentException(getFailingMessageForClass(c, stateType)); + } + }; + } + + protected String getFailingMessageForClass(Class entityClass, Class stateClass) { + return getClass().getSimpleName() + ": entity " + entityClass + " must have either a constructor taking [" + + stateClass + "] or a no-arg constructor and state setter."; + } + + private StateBackedCreator emptyConstructorStateSettingInstantiator( + Class type, Class stateType) { + final Constructor constructor = getNoArgConstructor(type); + if (constructor == null) + return null; + + log.info("Using " + type + " no-arg constructor"); + + return new StateBackedCreator() { + public T create(STATE state, Class c) throws Exception { + try { + StateProvider.setUnderlyingState(state); + T newInstance = constructor.newInstance(); + setState(newInstance, state); + return newInstance; + } finally { + StateProvider.retrieveState(); + } + } + }; + } + + protected StateBackedCreator createWithoutConstructorInvocation( + final Class type, Class stateType) { + ReflectionFactory rf = ReflectionFactory.getReflectionFactory(); + Constructor objectConstructor = getDeclaredConstructor(Object.class); + final Constructor serializationConstructor = rf.newConstructorForSerialization(type, objectConstructor); + return new StateBackedCreator() { + public T create(STATE state, Class c) throws Exception { + final T result = type.cast(serializationConstructor.newInstance()); + setState(result, state); + return result; + } + }; + } + + protected Constructor getNoArgConstructor(Class type) { + Constructor constructor = ClassUtils.getConstructorIfAvailable(type); + if (constructor != null) + return constructor; + return getDeclaredConstructor(type); + } + + protected StateBackedCreator stateTakingConstructorInstantiator( + Class type, Class stateType) { + Class stateInterface = (Class) stateType.getInterfaces()[0]; + final Constructor constructor = ClassUtils.getConstructorIfAvailable(type, stateInterface); + if (constructor == null) + return null; + + log.info("Using " + type + " constructor taking " + stateInterface); + return new StateBackedCreator() { + public T create(STATE n, Class c) throws Exception { + return constructor.newInstance(n); + } + }; + } + + protected Constructor getDeclaredConstructor(Class c) { + try { + final Constructor declaredConstructor = c.getDeclaredConstructor(); + declaredConstructor.setAccessible(true); + return declaredConstructor; + } catch (NoSuchMethodException e) { + return null; + } + } + + /** + * Subclasses must implement to set state + * + * @param entity + * @param s + */ + protected abstract void setState(Object entity, STATE s); + +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/EntityInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/EntityInstantiator.java new file mode 100644 index 000000000..c86076e2a --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/EntityInstantiator.java @@ -0,0 +1,44 @@ +/** + * 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; + +/** + * Interface to be implemented by classes that can instantiate and configure entities. The framework must do this when + * creating objects resulting from finders, even when there may be no no-arg constructor supplied by the user. + * + * @author Rod Johnson + */ +public interface EntityInstantiator { + + /* + * The best solution if available is to add a constructor that takes Node + * to each GraphEntity. This means generating an aspect beside every + * class as Roo presently does. + * + * An alternative that does not require Roo + * is a user-authored constructor taking Node and calling setUnderlyingNode() + * but this is less elegant and pollutes the domain object. + * + * If the user supplies a no-arg constructor, instantiation can occur by invoking it + * prior to calling setUnderlyingNode(). + * + * If the user does NOT supply a no-arg constructor, we must rely on Sun-specific + * code to instantiate entities without invoking a constructor. + */ + + T createEntityFromState(STATE s, Class c); + +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java index 38d065539..d742e05fd 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/GraphDatabaseContext.java @@ -21,12 +21,21 @@ 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.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.neo4j.annotation.Indexed; import org.springframework.data.neo4j.core.*; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jNodeConverter; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntityImpl; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; +import org.springframework.data.neo4j.support.query.CypherQueryExecutor; +import org.springframework.data.util.TypeInformation; import javax.transaction.Status; import javax.transaction.SystemException; @@ -51,28 +60,31 @@ public class GraphDatabaseContext { private GraphDatabaseService graphDatabaseService; private ConversionService conversionService; - + private Neo4jNodeConverter converter; private Validator validator; private NodeTypeRepresentationStrategy nodeTypeRepresentationStrategy; private RelationshipTypeRepresentationStrategy relationshipTypeRepresentationStrategy; + private Neo4jMappingContext mappingContext; + private final CypherQueryExecutor executor = new CypherQueryExecutor(this); - public > Index getIndex(Class type) { + public Index getIndex(Class type) { return getIndex(type, null); } - public > Index getIndex(Class type, String indexName) { + public Index getIndex(Class type, String indexName) { return getIndex(type, indexName, false); } - public > Index getIndex(Class type, String indexName, boolean fullText) { + public Index getIndex(Class type, String indexName, boolean fullText) { if (indexName==null) indexName = Indexed.Name.get(type); Map config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : null; - if (NodeBacked.class.isAssignableFrom(type)) return (Index) getIndexManager().forNodes(indexName, config); - if (RelationshipBacked.class.isAssignableFrom(type)) return (Index) getIndexManager().forRelationships(indexName, config); + + if (mappingContext.isNodeEntity(type)) return (Index) getIndexManager().forNodes(indexName, config); + if (mappingContext.isRelationshipEntity(type)) return (Index) getIndexManager().forRelationships(indexName, config); throw new IllegalArgumentException("Wrong index type supplied: " + type+" expected Node- or Relationship-Entity"); } @@ -92,37 +104,95 @@ public class GraphDatabaseContext { } } - public > ClosableIterable findAll(final Class entityClass) { + public ClosableIterable findAll(final Class entityClass) { return getTypeRepresentationStrategy(entityClass).findAll(entityClass); } - public > long count(final Class entityClass) { + public long count(final Class entityClass) { return getTypeRepresentationStrategy(entityClass).count(entityClass); } - public > T createEntityFromStoredType(S state) { + public T createEntityFromStoredType(S state) { return getTypeRepresentationStrategy(state).createEntity(state); } - public > T createEntityFromState(S state, Class type) { + public T createEntityFromState(S state, Class 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 > T projectTo(GraphBacked entity, Class targetType) { - S state = entity.getPersistentState(); + public T projectTo(Object entity, Class targetType) { + S state = getPersistentState(entity); return getTypeRepresentationStrategy(state, targetType).projectEntity(state, targetType); } + + public S getPersistentState(Object entity) { + if (entity instanceof PropertyContainer) { + return (S) entity; + } + if (isManaged(entity)) { + return ((ManagedEntity)entity).getPersistentState(); + } + final Class type = entity.getClass(); + final Neo4jPersistentEntityImpl persistentEntity = mappingContext.getPersistentEntity(type); + return (S)persistentEntity.getPersistentState(entity,this); + } - public > void postEntityCreation(S node, Class entityClass) { + // todo depending on type of mapping + public void setPersistentState(Object entity, S state) { + if (entity instanceof PropertyContainer) { + return; + } + if (isManaged(entity)) { + ((ManagedEntity)entity).setPersistentState(state); + return; + } + final Class type = entity.getClass(); + final Neo4jPersistentEntityImpl persistentEntity = mappingContext.getPersistentEntity(type); + persistentEntity.setPersistentState(entity,state); + } + + public void postEntityCreation(S node, Class entityClass) { getTypeRepresentationStrategy(node, entityClass).postEntityCreation(node, entityClass); } + public Iterable findAllByTraversal(Object entity, Class targetType, TraversalDescription traversalDescription) { + final Neo4jPersistentEntityImpl persistentEntity = mappingContext.getPersistentEntity(entity.getClass()); + final PropertyContainer state = persistentEntity.getPersistentState(entity, this); + 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) traverser.nodes(); + if (Relationship.class.isAssignableFrom(targetType)) return (Iterable) traverser.relationships(); + if (Path.class.isAssignableFrom(targetType)) return (Iterable) traverser; + return (Iterable)convertToGraphEntity(traverser, targetType); + } - public void removeNodeEntity(NodeBacked entity) { - Node node = entity.getPersistentState(); + private Iterable convertToGraphEntity(Traverser traverser, final Class targetType) { + if (isNodeEntity(targetType)) { + return new IterableWrapper(traverser.nodes()) { + @Override + protected Object underlyingObjectToObject(Node node) { + return createEntityFromState(node,targetType); + } + }; + } + if (isRelationshipEntity(targetType)) { + return new IterableWrapper(traverser.relationships()) { + @Override + protected Object underlyingObjectToObject(Relationship relationship) { + return createEntityFromState(relationship, targetType); + } + }; + } + throw new IllegalStateException("Can't determine valid type for traversal target "+targetType); + + } + + + public void removeNodeEntity(Object entity) { + Node node = getPersistentState(entity); if (node == null) return; nodeTypeRepresentationStrategy.preEntityRemoval(node); for (Relationship relationship : node.getRelationships()) { @@ -132,8 +202,8 @@ public class GraphDatabaseContext { node.delete(); } - public void removeRelationshipEntity(RelationshipBacked entity) { - Relationship relationship = entity.getPersistentState(); + public void removeRelationshipEntity(Object entity) { + Relationship relationship = getPersistentState(entity); if (relationship == null) return; removeRelationship(relationship); } @@ -165,34 +235,31 @@ public class GraphDatabaseContext { @SuppressWarnings("unchecked") - private > - TypeRepresentationStrategy getTypeRepresentationStrategy(Class type) { - if (NodeBacked.class.isAssignableFrom(type)) { - return (TypeRepresentationStrategy) nodeTypeRepresentationStrategy; - } else if (RelationshipBacked.class.isAssignableFrom(type)) { - return (TypeRepresentationStrategy) relationshipTypeRepresentationStrategy; + private TypeRepresentationStrategy getTypeRepresentationStrategy(Class 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 > - TypeRepresentationStrategy getTypeRepresentationStrategy(S state, Class type) { - if (state instanceof Node && NodeBacked.class.isAssignableFrom(type)) { - return (TypeRepresentationStrategy) nodeTypeRepresentationStrategy; - } else if (state instanceof Relationship && RelationshipBacked.class.isAssignableFrom(type)) { - return (TypeRepresentationStrategy) relationshipTypeRepresentationStrategy; + private TypeRepresentationStrategy getTypeRepresentationStrategy(S state, Class type) { + if (state instanceof Node && mappingContext.isNodeEntity(type)) { + return (TypeRepresentationStrategy) nodeTypeRepresentationStrategy; + } else if (state instanceof Relationship && mappingContext.isRelationshipEntity(type)) { + return (TypeRepresentationStrategy) relationshipTypeRepresentationStrategy; } throw new IllegalArgumentException("Type "+type+" is not NodeBacked nor RelationshipBacked."); } @SuppressWarnings("unchecked") - private > - TypeRepresentationStrategy getTypeRepresentationStrategy(S state) { + private TypeRepresentationStrategy getTypeRepresentationStrategy(S state) { if (state instanceof Node) { - return (TypeRepresentationStrategy) nodeTypeRepresentationStrategy; + return (TypeRepresentationStrategy) nodeTypeRepresentationStrategy; } else if (state instanceof Relationship) { - return (TypeRepresentationStrategy) relationshipTypeRepresentationStrategy; + return (TypeRepresentationStrategy) relationshipTypeRepresentationStrategy; } throw new IllegalArgumentException("Type is not NodeBacked nor RelationshipBacked."); } @@ -280,7 +347,46 @@ public class GraphDatabaseContext { this.validator = validatorFactory; } + public void setMappingContext(Neo4jMappingContext mappingContext) { + this.mappingContext = mappingContext; + } + public boolean isNodeEntity(Class targetType) { + return mappingContext.isNodeEntity(targetType); + } + public boolean isRelationshipEntity(Class targetType) { + return mappingContext.isRelationshipEntity(targetType); + } + + public Object save(Object entity) { + if (isManaged(entity)) { + return ((ManagedEntity)entity).persist(); + } else { + final Node node = this.getPersistentState(entity); + this.converter.write(entity, node); + return entity; // TODO ? + } + } + + private boolean isManaged(Object entity) { + return entity instanceof ManagedEntity; + } + + public void setConverter(Neo4jNodeConverter converter) { + this.converter = converter; + } + + public Object executeQuery(Object entity, String queryString, Map params, Neo4jPersistentProperty property) { + final TypeInformation typeInformation = property.getTypeInformation(); + final Class targetType = typeInformation.getActualType().getType(); + if (typeInformation.isCollectionLike()) { + return executor.query(queryString, targetType, params); + } + if (typeInformation.isMap()) { + return executor.queryForList(queryString, params); + } + return executor.queryForObject(queryString, targetType, params); + } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/EntityEvaluatorTest.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java similarity index 77% rename from spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/EntityEvaluatorTest.java rename to spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java index 406cc78af..84d238695 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/support/EntityEvaluatorTest.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.data.neo4j.support; -import org.junit.Test; +import org.neo4j.graphdb.PropertyContainer; /** * @author mh - * @since 26.02.11 + * @since 02.10.11 */ -public class EntityEvaluatorTest { - @Test - public void testEvaluate() throws Exception { +public interface ManagedEntity { + T persist(); - } + S getPersistentState(); + + void setPersistentState(S state); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java index 6e17d347c..6f94cf41e 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/conversion/EntityResultConverter.java @@ -18,8 +18,8 @@ package org.springframework.data.neo4j.support.conversion; import org.springframework.core.convert.ConversionService; import org.springframework.data.neo4j.core.EntityPath; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + import org.springframework.data.neo4j.conversion.DefaultConverter; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.path.ConvertingEntityPath; @@ -39,10 +39,10 @@ public class EntityResultConverter extends DefaultConverter { @Override protected Object doConvert(Object value, Class sourceType, Class targetType) { - if (NodeBacked.class.isAssignableFrom(targetType)) { + if (ctx.isNodeEntity(targetType)) { return ctx.createEntityFromState(toNode(value,sourceType),targetType); } - if (RelationshipBacked.class.isAssignableFrom(targetType)) { + if (ctx.isRelationshipEntity(targetType)) { return ctx.createEntityFromState(toRelationship(value,sourceType),targetType); } if (EntityPath.class.isAssignableFrom(targetType)) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityInstantiator.java index 4f069700d..27be3cea1 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityInstantiator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityInstantiator.java @@ -17,8 +17,10 @@ package org.springframework.data.neo4j.support.node; import org.neo4j.graphdb.Node; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.persistence.AbstractConstructorEntityInstantiator; +import org.neo4j.graphdb.Relationship; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.support.AbstractConstructorEntityInstantiator; + /** * Implementation of an entity instantiator for neo4j graphdb nodes, binding the entity type to a NodeBacked and the @@ -27,13 +29,25 @@ import org.springframework.data.persistence.AbstractConstructorEntityInstantiato * * @author Rod Johnson */ -public class NodeEntityInstantiator extends AbstractConstructorEntityInstantiator{ - - @Override - protected void setState(NodeBacked entity, Node s) { - entity.setPersistentState(s); +public class NodeEntityInstantiator extends AbstractConstructorEntityInstantiator { + + private final Neo4jMappingContext mappingContext; + + public NodeEntityInstantiator(Neo4jMappingContext mappingContext) { + this.mappingContext = mappingContext; + } + + + @Override + protected void setState(Object entity, Node node) { + setPersistentState(entity,node); } + protected void setPersistentState(Object entity, Node node) { + this.mappingContext.setPersistentState(entity,node); + } + + @Override protected String getFailingMessageForClass(Class entityClass, Class stateClass) { return entityClass.getSimpleName() + ": entity must have a no-arg constructor."; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityState.java index 0c665f2d8..bf286904d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityState.java @@ -19,21 +19,21 @@ package org.springframework.data.neo4j.support.node; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotInTransactionException; import org.springframework.dao.InvalidDataAccessResourceUsageException; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.fieldaccess.DefaultEntityState; import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * @author Michael Hunger * @since 21.09.2010 */ -public class NodeEntityState extends DefaultEntityState { +public class NodeEntityState extends DefaultEntityState { private final GraphDatabaseContext graphDatabaseContext; - public NodeEntityState(final Node underlyingState, final ENTITY entity, final Class type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory, Neo4JPersistentEntity persistentEntity) { + public NodeEntityState(final Node underlyingState, final Object entity, final Class type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory, Neo4jPersistentEntity persistentEntity) { super(underlyingState, entity, type, nodeDelegatingFieldAccessorFactory,persistentEntity); this.graphDatabaseContext = graphDatabaseContext; } @@ -64,7 +64,13 @@ public class NodeEntityState extends DefaultEntitySta } @Override - public ENTITY persist() { + public void setPersistentState(Node node) { + graphDatabaseContext.setPersistentState(entity,node); + super.setPersistentState(node); + } + + @Override + public Object persist() { if (getPersistentState() == null) { createAndAssignState(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityStateFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityStateFactory.java index 54963ea87..b966b1d2a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityStateFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeEntityStateFactory.java @@ -19,55 +19,35 @@ package org.springframework.data.neo4j.support.node; import org.neo4j.graphdb.Node; import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.core.EntityState; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory; import org.springframework.data.neo4j.fieldaccess.DetachedEntityState; -import org.springframework.data.neo4j.mapping.Neo4JMappingContext; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; -import javax.persistence.PersistenceUnitUtil; - public class NodeEntityStateFactory { - private GraphDatabaseContext graphDatabaseContext; - - private EntityManagerFactory entityManagerFactory; + protected GraphDatabaseContext graphDatabaseContext; - private DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory; + private DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory; - private PartialNodeEntityState.PartialNodeDelegatingFieldAccessorFactory delegatingFieldAccessorFactory; + protected Neo4jMappingContext mappingContext; - private Neo4JMappingContext mappingContext; + private boolean createDetachableEntities = true; - public EntityState getEntityState(final NodeBacked entity) { - final Class entityType = entity.getClass(); + public EntityState getEntityState(final Object entity) { + final Class entityType = entity.getClass(); final NodeEntity graphEntityAnnotation = entityType.getAnnotation(NodeEntity.class); // todo cache ?? - final Neo4JPersistentEntity persistentEntity = mappingContext.getPersistentEntity(entityType); - if (graphEntityAnnotation.partial()) { - final PartialNodeEntityState partialNodeEntityState = new PartialNodeEntityState(null, entity, entityType, graphDatabaseContext, getPersistenceUnitUtils(), delegatingFieldAccessorFactory, (Neo4JPersistentEntity) persistentEntity); - return new DetachedEntityState(partialNodeEntityState, graphDatabaseContext) { - @Override - protected boolean isDetached() { - return super.isDetached() || partialNodeEntityState.getId(entity) == null; - } - }; - } else { - NodeEntityState nodeEntityState = new NodeEntityState(null, entity, entityType, graphDatabaseContext, nodeDelegatingFieldAccessorFactory, (Neo4JPersistentEntity) persistentEntity); - // alternative was return new NestedTransactionEntityState(nodeEntityState,graphDatabaseContext); - return new DetachedEntityState(nodeEntityState, graphDatabaseContext); - } - } - - private PersistenceUnitUtil getPersistenceUnitUtils() { - if (entityManagerFactory == null) return null; - return entityManagerFactory.getPersistenceUnitUtil(); + final Neo4jPersistentEntity persistentEntity = mappingContext.getPersistentEntity(entityType); + NodeEntityState nodeEntityState = new NodeEntityState(null, entity, entityType, graphDatabaseContext, nodeDelegatingFieldAccessorFactory, (Neo4jPersistentEntity) persistentEntity); + // alternative was return new NestedTransactionEntityState(nodeEntityState,graphDatabaseContext); + if (createDetachableEntities) return new DetachedEntityState(nodeEntityState, graphDatabaseContext); + return nodeEntityState; } public void setNodeDelegatingFieldAccessorFactory( - DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory) { + DelegatingFieldAccessorFactory nodeDelegatingFieldAccessorFactory) { this.nodeDelegatingFieldAccessorFactory = nodeDelegatingFieldAccessorFactory; } @@ -75,24 +55,19 @@ public class NodeEntityStateFactory { this.graphDatabaseContext = graphDatabaseContext; } - public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { - this.entityManagerFactory = entityManagerFactory; - } - - public Neo4JMappingContext getMappingContext() { + public Neo4jMappingContext getMappingContext() { return mappingContext; } - public void setMappingContext(Neo4JMappingContext mappingContext) { + public void setMappingContext(Neo4jMappingContext mappingContext) { this.mappingContext = mappingContext; } - @PostConstruct - private void setUp() { - this.delegatingFieldAccessorFactory = new PartialNodeEntityState.PartialNodeDelegatingFieldAccessorFactory(graphDatabaseContext); - } - public GraphDatabaseContext getGraphDatabaseContext() { return graphDatabaseContext; } + + public void setCreateDetachableEntities(boolean createDetachableEntities) { + this.createDetachableEntities = createDetachableEntities; + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/ConvertingEntityPath.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/ConvertingEntityPath.java index 89d434766..5a9a05bf0 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/ConvertingEntityPath.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/ConvertingEntityPath.java @@ -22,9 +22,9 @@ import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.data.neo4j.core.EntityPath; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; + + + import org.springframework.data.neo4j.support.GraphDatabaseContext; import java.util.Iterator; @@ -33,25 +33,25 @@ import java.util.Iterator; * @author mh * @since 26.02.11 */ -public class ConvertingEntityPath implements EntityPath { +public class ConvertingEntityPath implements EntityPath { @Override - public T startEntity(Class... types) { + public T startEntity(Class... types) { return createNodeEntityFromFirstParameterOrStoredType(startNode(), types); } - private T createNodeEntityFromFirstParameterOrStoredType(Node node, Class...types) { + private T createNodeEntityFromFirstParameterOrStoredType(Node node, Class...types) { if (node==null) return null; if (types==null || types.length==0) return graphDatabaseContext.createEntityFromStoredType(node); return graphDatabaseContext.createEntityFromState(node,types[0]); } @Override - public T endEntity(Class... types) { + public T endEntity(Class... types) { return createNodeEntityFromFirstParameterOrStoredType(endNode(),types); } @Override - public T lastRelationshipEntity(Class... types) { + public T lastRelationshipEntity(Class... types) { Relationship relationship = lastRelationship(); if (relationship==null) return null; return graphDatabaseContext.createEntityFromState(relationship, getFirstOrDefault((Class) DefaultRelationshipBacked.class, types)); @@ -63,7 +63,7 @@ public class ConvertingEntityPath imp } @Override - public Iterable nodeEntities() { + public Iterable nodeEntities() { return new IterableWrapper(nodes()) { @Override protected T underlyingObjectToObject(Node node) { @@ -73,7 +73,7 @@ public class ConvertingEntityPath imp } @Override - public Iterable relationshipEntities(final Class... relationships) { + public Iterable relationshipEntities(final Class... relationships) { return new IterableWrapper(relationships()) { @Override protected T underlyingObjectToObject(Relationship relationship) { @@ -83,7 +83,7 @@ public class ConvertingEntityPath imp } @Override - public Iterable allPathEntities(final Class... relationships) { + public Iterable allPathEntities(final Class... relationships) { return new IterableWrapper(delegate) { @Override protected T underlyingObjectToObject(PropertyContainer element) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/DefaultRelationshipBacked.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/DefaultRelationshipBacked.java index ce5baf458..29841e0b0 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/DefaultRelationshipBacked.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/DefaultRelationshipBacked.java @@ -19,7 +19,7 @@ package org.springframework.data.neo4j.support.path; import org.springframework.data.neo4j.annotation.EndNode; import org.springframework.data.neo4j.annotation.RelationshipEntity; import org.springframework.data.neo4j.annotation.StartNode; -import org.springframework.data.neo4j.core.NodeBacked; + /** * @author mh @@ -27,10 +27,6 @@ import org.springframework.data.neo4j.core.NodeBacked; */ @RelationshipEntity public class DefaultRelationshipBacked { - @StartNode - NodeBacked startNode; - - @EndNode - NodeBacked endNode; - + @StartNode Object start; + @EndNode Object end; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityEvaluator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityEvaluator.java index 96c8d2981..e63a34d68 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityEvaluator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityEvaluator.java @@ -20,14 +20,14 @@ import org.neo4j.graphdb.Path; import org.neo4j.graphdb.traversal.Evaluation; import org.neo4j.graphdb.traversal.Evaluator; import org.springframework.data.neo4j.core.EntityPath; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * @author mh * @since 26.02.11 */ -public abstract class EntityEvaluator implements Evaluator { +public abstract class EntityEvaluator implements Evaluator { private GraphDatabaseContext graphDatabaseContext; @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityMapper.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityMapper.java index 4ae42cef5..b17b85fb2 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityMapper.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityMapper.java @@ -18,14 +18,14 @@ package org.springframework.data.neo4j.support.path; import org.neo4j.graphdb.Path; import org.springframework.data.neo4j.core.EntityPath; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * @author mh * @since 26.02.11 */ -public abstract class EntityMapper implements PathMapper { +public abstract class EntityMapper implements PathMapper { private GraphDatabaseContext graphDatabaseContext; protected EntityMapper(GraphDatabaseContext graphDatabaseContext) { @@ -39,7 +39,7 @@ public abstract class EntityMapper(graphDatabaseContext, path)); } - public abstract static class WithoutResult extends EntityMapper { + public abstract static class WithoutResult extends EntityMapper { protected WithoutResult(GraphDatabaseContext graphDatabaseContext) { super(graphDatabaseContext); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityPathPathIterableWrapper.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityPathPathIterableWrapper.java index e87c071ac..386b3385a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityPathPathIterableWrapper.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/path/EntityPathPathIterableWrapper.java @@ -19,14 +19,14 @@ package org.springframework.data.neo4j.support.path; import org.neo4j.graphdb.Path; import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.data.neo4j.core.EntityPath; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.support.GraphDatabaseContext; /** * @author mh * @since 04.04.11 */ -public class EntityPathPathIterableWrapper extends IterableWrapper, Path> { +public class EntityPathPathIterableWrapper extends IterableWrapper, Path> { private final GraphDatabaseContext graphDatabaseContext; public EntityPathPathIterableWrapper(Iterable paths, GraphDatabaseContext graphDatabaseContext) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityInstantiator.java index 3dafe68ad..1e2972a5e 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityInstantiator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityInstantiator.java @@ -17,8 +17,9 @@ package org.springframework.data.neo4j.support.relationship; import org.neo4j.graphdb.Relationship; -import org.springframework.data.neo4j.core.RelationshipBacked; -import org.springframework.data.persistence.AbstractConstructorEntityInstantiator; + +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.support.AbstractConstructorEntityInstantiator; import sun.reflect.ReflectionFactory; /** @@ -27,21 +28,27 @@ import sun.reflect.ReflectionFactory; * Part of the SPI, not intended for public use. */ -public class RelationshipEntityInstantiator extends AbstractConstructorEntityInstantiator { +public class RelationshipEntityInstantiator extends AbstractConstructorEntityInstantiator { - @Override - protected void setState(RelationshipBacked entity, Relationship relationship) { - entity.setPersistentState(relationship); + private final Neo4jMappingContext mappingContext; + + public RelationshipEntityInstantiator(Neo4jMappingContext mappingContext) { + this.mappingContext = mappingContext; } @Override - protected String getFailingMessageForClass(Class entityClass, Class stateClass) { + protected void setState(Object entity, Relationship relationship) { + this.mappingContext.setPersistentState(entity,relationship); + } + + @Override + protected String getFailingMessageForClass(Class entityClass, Class relationshipClass) { return entityClass.getSimpleName() + ": entity must have a no-arg constructor."; } /* - protected StateBackedCreator createInstantiator(Class type, final Class stateType) { + protected StateBackedCreator createInstantiator(Class type, final Class stateType) { StateBackedCreator creator = createWithoutConstructorInvocation(type,stateType); if (creator !=null) return creator; return createFailingInstantiator(stateType); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityState.java index 83905bb02..d76c1cd0a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityState.java @@ -17,12 +17,13 @@ package org.springframework.data.neo4j.support.relationship; import org.neo4j.graphdb.NotInTransactionException; +import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import org.springframework.dao.InvalidDataAccessResourceUsageException; -import org.springframework.data.neo4j.core.RelationshipBacked; + import org.springframework.data.neo4j.fieldaccess.DefaultEntityState; import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; import org.springframework.data.neo4j.support.GraphDatabaseContext; @@ -30,25 +31,26 @@ import org.springframework.data.neo4j.support.GraphDatabaseContext; * @author Michael Hunger * @since 21.09.2010 */ -public class RelationshipEntityState extends DefaultEntityState { +public class RelationshipEntityState extends DefaultEntityState { private final GraphDatabaseContext graphDatabaseContext; - public RelationshipEntityState(final Relationship underlyingState, final ENTITY entity, final Class type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4JPersistentEntity persistentEntity) { + public RelationshipEntityState(final Relationship underlyingState, final Object entity, final Class type, final GraphDatabaseContext graphDatabaseContext, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4jPersistentEntity persistentEntity) { super(underlyingState, entity, type, delegatingFieldAccessorFactory, persistentEntity); this.graphDatabaseContext = graphDatabaseContext; } @Override public void createAndAssignState() { - if (entity.getPersistentState()!=null) return; + final PropertyContainer state = graphDatabaseContext.getPersistentState(entity); + if (state !=null) return; try { final Object id = getIdFromEntity(); if (id instanceof Number) { final Relationship relationship = graphDatabaseContext.getRelationshipById(((Number) id).longValue()); setPersistentState(relationship); if (log.isInfoEnabled()) - log.info("Entity reattached " + entity.getClass() + "; used Relationship [" + entity.getPersistentState() + "];"); + log.info("Entity reattached " + entity.getClass() + "; used Relationship [" + state + "];"); return; } @@ -61,7 +63,7 @@ public class RelationshipEntityState extends } @Override - public ENTITY persist() { + public Object persist() { createAndAssignState(); return entity; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityStateFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityStateFactory.java index d529d6a62..6906dd2d4 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityStateFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/relationship/RelationshipEntityStateFactory.java @@ -18,22 +18,22 @@ package org.springframework.data.neo4j.support.relationship; import org.neo4j.graphdb.Relationship; import org.springframework.data.neo4j.core.EntityState; -import org.springframework.data.neo4j.core.RelationshipBacked; + import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory; -import org.springframework.data.neo4j.mapping.Neo4JMappingContext; -import org.springframework.data.neo4j.mapping.Neo4JPersistentEntity; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; import org.springframework.data.neo4j.support.GraphDatabaseContext; public class RelationshipEntityStateFactory { private GraphDatabaseContext graphDatabaseContext; - private DelegatingFieldAccessorFactory relationshipDelegatingFieldAccessorFactory; - private Neo4JMappingContext mappingContext; + private DelegatingFieldAccessorFactory relationshipDelegatingFieldAccessorFactory; + private Neo4jMappingContext mappingContext; - public EntityState getEntityState(final RelationshipBacked entity) { - final Class entityType = entity.getClass(); - return new RelationshipEntityState(null,entity, entityType, graphDatabaseContext, relationshipDelegatingFieldAccessorFactory, (Neo4JPersistentEntity) mappingContext.getPersistentEntity(entityType)); + public EntityState getEntityState(final Object entity) { + final Class entityType = entity.getClass(); + return new RelationshipEntityState(null,entity, entityType, graphDatabaseContext, relationshipDelegatingFieldAccessorFactory, (Neo4jPersistentEntity) mappingContext.getPersistentEntity(entityType)); } public void setGraphDatabaseContext(GraphDatabaseContext graphDatabaseContext) { @@ -41,11 +41,11 @@ public class RelationshipEntityStateFactory { } public void setRelationshipDelegatingFieldAccessorFactory( - DelegatingFieldAccessorFactory delegatingFieldAccessorFactory) { + DelegatingFieldAccessorFactory delegatingFieldAccessorFactory) { this.relationshipDelegatingFieldAccessorFactory = delegatingFieldAccessorFactory; } - public void setMappingContext(Neo4JMappingContext mappingContext) { + public void setMappingContext(Neo4jMappingContext mappingContext) { this.mappingContext = mappingContext; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/EntityTypeCache.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/EntityTypeCache.java index 0b4422224..f2841c43f 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/EntityTypeCache.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/EntityTypeCache.java @@ -16,7 +16,7 @@ package org.springframework.data.neo4j.support.typerepresentation; import org.neo4j.graphdb.NotFoundException; -import org.springframework.data.neo4j.core.GraphBacked; + import java.util.Map; import java.util.WeakHashMap; @@ -29,14 +29,14 @@ class EntityTypeCache { private final Map> cache = new WeakHashMap>(); @SuppressWarnings({"unchecked"}) - > Class getClassForName(String className) { + Class getClassForName(String className) { try { - Class result = (Class) cache.get(className); + Class result = (Class) cache.get(className); if (result != null) return result; synchronized (cache) { - result = (Class) cache.get(className); + result = (Class) cache.get(className); if (result != null) return result; - result = (Class) Class.forName(className); + result = (Class) Class.forName(className); cache.put(className, result); return result; } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingNodeTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingNodeTypeRepresentationStrategy.java index 78bac7bf7..f6abaa8b6 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingNodeTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingNodeTypeRepresentationStrategy.java @@ -24,10 +24,9 @@ import org.neo4j.helpers.collection.ClosableIterable; import org.neo4j.helpers.collection.FilteringIterable; import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.data.neo4j.annotation.NodeEntity; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy; -import org.springframework.data.persistence.EntityInstantiator; +import org.springframework.data.neo4j.support.EntityInstantiator; import java.util.HashMap; import java.util.Map; @@ -38,12 +37,12 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent public static final String INDEX_NAME = "__types__"; public static final String TYPE_PROPERTY_NAME = "__type__"; public static final String INDEX_KEY = "className"; - private EntityInstantiator graphEntityInstantiator; + private EntityInstantiator graphEntityInstantiator; private GraphDatabaseService graphDb; private final EntityTypeCache typeCache; public IndexingNodeTypeRepresentationStrategy(GraphDatabaseService graphDb, - EntityInstantiator graphEntityInstantiator) { + EntityInstantiator graphEntityInstantiator) { this.graphDb = graphDb; this.graphEntityInstantiator = graphEntityInstantiator; typeCache = new EntityTypeCache(); @@ -58,12 +57,12 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent } @Override - public void postEntityCreation(Node state, Class type) { + public void postEntityCreation(Node state, Class type) { addToNodeTypesIndex(state, type); state.setProperty(TYPE_PROPERTY_NAME, type.getName()); } - private void addToNodeTypesIndex(Node node, Class entityClass) { + private void addToNodeTypesIndex(Node node, Class entityClass) { Class klass = entityClass; while (klass.getAnnotation(NodeEntity.class) != null) { getNodeTypesIndex().add(node, INDEX_KEY, klass.getName()); @@ -72,17 +71,17 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent } @Override - public ClosableIterable findAll(Class clazz) { + public ClosableIterable findAll(Class clazz) { return findAllNodeBacked(clazz); } - private ClosableIterable findAllNodeBacked(Class clazz) { + private ClosableIterable findAllNodeBacked(Class clazz) { final IndexHits allEntitiesOfType = getNodeTypesIndex().get(INDEX_KEY, clazz.getName()); - return new FilteringClosableIterable(allEntitiesOfType); + return new FilteringClosableIterable(allEntitiesOfType); } @Override - public long count(Class entityClass) { + public long count(Class entityClass) { long count = 0; for (Object o : getNodeTypesIndex().get(INDEX_KEY, entityClass.getName())) { count += 1; @@ -91,7 +90,7 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent } @Override - public Class getJavaType(Node node) { + public Class getJavaType(Node node) { if (node == null) throw new IllegalArgumentException("Node is null"); String className = (String) node.getProperty(TYPE_PROPERTY_NAME); return typeCache.getClassForName(className); @@ -104,8 +103,8 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent @Override @SuppressWarnings("unchecked") - public U createEntity(Node state) { - Class javaType = getJavaType(state); + public U createEntity(Node state) { + Class javaType = getJavaType(state); if (javaType == null) { throw new IllegalStateException("No type stored on node."); } @@ -114,8 +113,8 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent @Override @SuppressWarnings("unchecked") - public U createEntity(Node state, Class type) { - Class javaType = getJavaType(state); + public U createEntity(Node state, Class type) { + Class javaType = getJavaType(state); if (javaType == null) { throw new IllegalStateException("No type stored on node."); } @@ -126,25 +125,25 @@ public class IndexingNodeTypeRepresentationStrategy implements NodeTypeRepresent } @Override - public U projectEntity(Node state, Class type) { + public U projectEntity(Node state, Class type) { return graphEntityInstantiator.createEntityFromState(state, type); } - private class FilteringClosableIterable extends FilteringIterable implements ClosableIterable { + private class FilteringClosableIterable extends FilteringIterable implements ClosableIterable { private final IndexHits indexHits; public FilteringClosableIterable(IndexHits indexHits) { - super(new IterableWrapper(indexHits) { + super(new IterableWrapper(indexHits) { @Override @SuppressWarnings("unchecked") - protected ENTITY underlyingObjectToObject(Node node) { - Class javaType = (Class) getJavaType(node); + protected Object underlyingObjectToObject(Node node) { + Class javaType = (Class) getJavaType(node); if (javaType == null) return null; return graphEntityInstantiator.createEntityFromState(node, javaType); } - }, new Predicate() { + }, new Predicate() { @Override - public boolean accept(ENTITY item) { + public boolean accept(Object item) { return item != null; } }); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingRelationshipTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingRelationshipTypeRepresentationStrategy.java index 16f652351..44580df3a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingRelationshipTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/IndexingRelationshipTypeRepresentationStrategy.java @@ -17,8 +17,6 @@ package org.springframework.data.neo4j.support.typerepresentation; import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexHits; @@ -27,27 +25,22 @@ import org.neo4j.helpers.collection.ClosableIterable; import org.neo4j.helpers.collection.FilteringIterable; import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.data.neo4j.annotation.RelationshipEntity; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.RelationshipBacked; -import org.springframework.data.neo4j.core.RelationshipTypeRepresentationStrategy; -import org.springframework.data.persistence.EntityInstantiator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.WeakHashMap; + +import org.springframework.data.neo4j.core.RelationshipTypeRepresentationStrategy; +import org.springframework.data.neo4j.support.EntityInstantiator; public class IndexingRelationshipTypeRepresentationStrategy implements RelationshipTypeRepresentationStrategy { public static final String INDEX_NAME = "__types__"; public static final String TYPE_PROPERTY_NAME = "__type__"; public static final String INDEX_KEY = "className"; - private EntityInstantiator relationshipEntityInstantiator; + private EntityInstantiator relationshipEntityInstantiator; private GraphDatabaseService graphDb; private final EntityTypeCache typeCache; public IndexingRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDb, - EntityInstantiator relationshipEntityInstantiator) { + EntityInstantiator relationshipEntityInstantiator) { this.graphDb = graphDb; this.relationshipEntityInstantiator = relationshipEntityInstantiator; typeCache = new EntityTypeCache(); @@ -58,12 +51,12 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations } @Override - public void postEntityCreation(Relationship state, Class type) { + public void postEntityCreation(Relationship state, Class type) { addToTypesIndex(state, type); state.setProperty(TYPE_PROPERTY_NAME, type.getName()); } - private void addToTypesIndex(Relationship node, Class entityClass) { + private void addToTypesIndex(Relationship node, Class entityClass) { Class klass = entityClass; while (klass.getAnnotation(RelationshipEntity.class) != null) { getRelTypesIndex().add(node, INDEX_KEY, klass.getName()); @@ -72,19 +65,19 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations } @Override - public ClosableIterable findAll(Class clazz) { + public ClosableIterable findAll(Class clazz) { return findAllRelBacked(clazz); } - private ClosableIterable findAllRelBacked(Class clazz) { + private ClosableIterable findAllRelBacked(Class clazz) { final IndexHits allEntitiesOfType = getRelTypesIndex().get(INDEX_KEY, clazz.getName()); - return new FilteringClosableEntityIterable(allEntitiesOfType); + return new FilteringClosableEntityIterable(allEntitiesOfType); } @Override - public long count(Class entityClass) { + public long count(Class entityClass) { long count = 0; final IndexHits hits = getRelTypesIndex().get(INDEX_KEY, entityClass.getName()); while (hits.hasNext()) { @@ -96,7 +89,7 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations @Override @SuppressWarnings("unchecked") - public Class getJavaType(Relationship relationship) { + public Class getJavaType(Relationship relationship) { if (relationship == null) throw new IllegalArgumentException("Relationship is null"); String className = (String) relationship.getProperty(TYPE_PROPERTY_NAME); return typeCache.getClassForName(className); @@ -110,8 +103,8 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations @Override @SuppressWarnings("unchecked") - public U createEntity(Relationship state) { - Class javaType = getJavaType(state); + public U createEntity(Relationship state) { + Class javaType = getJavaType(state); if (javaType == null) { throw new IllegalStateException("No type stored on relationship."); } @@ -120,8 +113,8 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations @Override @SuppressWarnings("unchecked") - public U createEntity(Relationship state, Class type) { - Class javaType = getJavaType(state); + public U createEntity(Relationship state, Class type) { + Class javaType = getJavaType(state); if (javaType == null) { throw new IllegalStateException("No type stored on relationship."); } @@ -132,25 +125,25 @@ public class IndexingRelationshipTypeRepresentationStrategy implements Relations } @Override - public U projectEntity(Relationship state, Class type) { + public U projectEntity(Relationship state, Class type) { return relationshipEntityInstantiator.createEntityFromState(state, type); } - private class FilteringClosableEntityIterable extends FilteringIterable implements ClosableIterable { + private class FilteringClosableEntityIterable extends FilteringIterable implements ClosableIterable { private final IndexHits indexHits; public FilteringClosableEntityIterable(IndexHits indexHits) { - super(new IterableWrapper(indexHits) { + super(new IterableWrapper(indexHits) { @Override @SuppressWarnings("unchecked") - protected ENTITY underlyingObjectToObject(Relationship rel) { - Class javaType = (Class) IndexingRelationshipTypeRepresentationStrategy.this.getJavaType(rel); + protected Object underlyingObjectToObject(Relationship rel) { + Class javaType = (Class) IndexingRelationshipTypeRepresentationStrategy.this.getJavaType(rel); if (javaType == null) return null; return IndexingRelationshipTypeRepresentationStrategy.this.relationshipEntityInstantiator.createEntityFromState(rel, javaType); } - }, new Predicate() { + }, new Predicate() { @Override - public boolean accept(ENTITY item) { + public boolean accept(Object item) { return item != null; } }); diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopNodeTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopNodeTypeRepresentationStrategy.java index c09b62860..034efdb10 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopNodeTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopNodeTypeRepresentationStrategy.java @@ -18,22 +18,30 @@ package org.springframework.data.neo4j.support.typerepresentation; import org.neo4j.graphdb.Node; import org.neo4j.helpers.collection.ClosableIterable; -import org.springframework.data.neo4j.core.NodeBacked; + import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy; +import org.springframework.data.neo4j.support.EntityInstantiator; +import org.springframework.data.neo4j.support.node.NodeEntityInstantiator; public class NoopNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy { - @Override - public void postEntityCreation(Node state, Class type) { + private final EntityInstantiator nodeEntiyInstantiator; + + public NoopNodeTypeRepresentationStrategy(EntityInstantiator nodeEntiyInstantiator) { + this.nodeEntiyInstantiator = nodeEntiyInstantiator; } @Override - public ClosableIterable findAll(Class clazz) { + public void postEntityCreation(Node state, Class type) { + } + + @Override + public ClosableIterable findAll(Class clazz) { throw new UnsupportedOperationException("findAll not supported."); } @Override - public long count(Class entityClass) { + public long count(Class entityClass) { throw new UnsupportedOperationException("count not supported."); } @@ -42,22 +50,22 @@ public class NoopNodeTypeRepresentationStrategy implements NodeTypeRepresentatio } @Override - public Class getJavaType(Node state) { + public Class getJavaType(Node state) { throw new UnsupportedOperationException("getJavaType not supported."); } @Override - public U createEntity(Node state) { + public U createEntity(Node state) { throw new UnsupportedOperationException("Creation with stored type not supported."); } @Override - public U createEntity(Node state, Class type) { + public U createEntity(Node state, Class type) { return projectEntity(state, type); } @Override - public U projectEntity(Node state, Class type) { - return null; + public U projectEntity(Node state, Class type) { + return nodeEntiyInstantiator.createEntityFromState(state,type); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopRelationshipTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopRelationshipTypeRepresentationStrategy.java index 2c293ab09..5ddd0e8da 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopRelationshipTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/NoopRelationshipTypeRepresentationStrategy.java @@ -18,22 +18,29 @@ package org.springframework.data.neo4j.support.typerepresentation; import org.neo4j.graphdb.Relationship; import org.neo4j.helpers.collection.ClosableIterable; -import org.springframework.data.neo4j.core.RelationshipBacked; + import org.springframework.data.neo4j.core.RelationshipTypeRepresentationStrategy; +import org.springframework.data.neo4j.support.EntityInstantiator; public class NoopRelationshipTypeRepresentationStrategy implements RelationshipTypeRepresentationStrategy { - @Override - public void postEntityCreation(Relationship state, Class type) { + private final EntityInstantiator relationshipEntityInstantiator; + + public NoopRelationshipTypeRepresentationStrategy(EntityInstantiator relationshipEntityInstantiator) { + this.relationshipEntityInstantiator = relationshipEntityInstantiator; } @Override - public ClosableIterable findAll(Class clazz) { + public void postEntityCreation(Relationship state, Class type) { + } + + @Override + public ClosableIterable findAll(Class clazz) { throw new UnsupportedOperationException("findAll not supported."); } @Override - public long count(Class entityClass) { + public long count(Class entityClass) { throw new UnsupportedOperationException("count not supported."); } @@ -42,22 +49,22 @@ public class NoopRelationshipTypeRepresentationStrategy implements RelationshipT } @Override - public Class getJavaType(Relationship state) { + public Class getJavaType(Relationship state) { throw new UnsupportedOperationException("getJavaType not supported."); } @Override - public U createEntity(Relationship state) { + public U createEntity(Relationship state) { throw new UnsupportedOperationException("Creation with stored type not supported."); } @Override - public U createEntity(Relationship state, Class type) { + public U createEntity(Relationship state, Class type) { return projectEntity(state, type); } @Override - public U projectEntity(Relationship state, Class type) { - return null; + public U projectEntity(Relationship state, Class type) { + return relationshipEntityInstantiator.createEntityFromState(state, type); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategy.java index f77d4aa6f..9f09957cb 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/SubReferenceNodeTypeRepresentationStrategy.java @@ -24,10 +24,10 @@ import org.neo4j.helpers.collection.ClosableIterable; import org.neo4j.helpers.collection.CombiningIterable; import org.neo4j.helpers.collection.IterableWrapper; import org.neo4j.kernel.Traversal; -import org.springframework.data.neo4j.core.GraphBacked; -import org.springframework.data.neo4j.core.NodeBacked; + + import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy; -import org.springframework.data.persistence.EntityInstantiator; +import org.springframework.data.neo4j.support.EntityInstantiator; import java.util.Collection; import java.util.Collections; @@ -54,10 +54,10 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre public static final String SUBREF_CLASS_KEY = "class"; private GraphDatabaseService graphDatabaseService; - private EntityInstantiator entityInstantiator; + private EntityInstantiator entityInstantiator; private final EntityTypeCache typeCache; - public SubReferenceNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator entityInstantiator) { + public SubReferenceNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator entityInstantiator) { this.graphDatabaseService = graphDatabaseService; this.entityInstantiator = entityInstantiator; typeCache = new EntityTypeCache(); @@ -93,7 +93,7 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre } @Override - public void postEntityCreation(Node state, Class type) { + public void postEntityCreation(Node state, Class type) { final Node subReference = obtainSubreferenceNode(type); state.createRelationshipTo(subReference, INSTANCE_OF_RELATIONSHIP_TYPE); subReference.setProperty(SUBREF_CLASS_KEY, type.getName()); @@ -119,7 +119,7 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre } @Override - public long count(final Class entityClass) { + public long count(final Class entityClass) { final Node subrefNode = findSubreferenceNode(entityClass); if (subrefNode == null) return 0; return (Integer) subrefNode.getProperty(SUBREFERENCE_NODE_COUNTER_KEY, 0); @@ -127,7 +127,7 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre @Override @SuppressWarnings("unchecked") - public Class getJavaType(Node node) { + public Class getJavaType(Node node) { if (node == null) throw new IllegalArgumentException("Node is null"); Relationship instanceOfRelationship = node.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING); if (instanceOfRelationship == null) @@ -139,17 +139,17 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre return clazz; } - private Class resolveType(Node node, String typeName) { - final Class> type = typeCache.getClassForName(typeName); + private Class resolveType(Node node, String typeName) { + final Class type = typeCache.getClassForName(typeName); if (type == null) { throw new IllegalStateException("Unable to get type for node: " + node); } - return (Class) type.asSubclass(NodeBacked.class); + return (Class) type; } @Override public void preEntityRemoval(Node state) { - Class clazz = getJavaType(state); + Class clazz = getJavaType(state); if (clazz == null) return; final Node subReference = obtainSubreferenceNode(clazz); Relationship instanceOf = state.getSingleRelationship(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.OUTGOING); @@ -165,18 +165,19 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre } @Override - public ClosableIterable findAll(final Class clazz) { + public ClosableIterable findAll(final Class clazz) { final Node subrefNode = findSubreferenceNode(clazz); if (log.isDebugEnabled()) log.debug("Subref: " + subrefNode); Iterable> relIterables = findEntityIterables(subrefNode); return new ClosableCombiningIterable(relIterables); } - private List> findEntityIterables(Node subrefNode) { + private List> findEntityIterables(Node subrefNode) { if (subrefNode == null) return Collections.emptyList(); List> result = new LinkedList>(); for (Relationship relationship : subrefNode.getRelationships(SUBCLASS_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) { - result.addAll((Collection>) findEntityIterables(relationship.getStartNode())); + final List> entityIterables = this.findEntityIterables(relationship.getStartNode()); + result.addAll(entityIterables); } Iterable t = new IterableWrapper(subrefNode.getRelationships(INSTANCE_OF_RELATIONSHIP_TYPE, Direction.INCOMING)) { @Override @@ -196,7 +197,7 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre return getOrCreateSubReferenceNode(subRefRelationshipType(entityClass)); } - public Node findSubreferenceNode(final Class entityClass) { + public Node findSubreferenceNode(final Class entityClass) { final Relationship subrefRelationship = graphDatabaseService.getReferenceNode().getSingleRelationship(subRefRelationshipType(entityClass), Direction.OUTGOING); return subrefRelationship != null ? subrefRelationship.getEndNode() : null; } @@ -224,8 +225,8 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre @Override - public U createEntity(Node state) { - Class javaType = getJavaType(state); + public U createEntity(Node state) { + Class javaType = getJavaType(state); if (javaType == null) { throw new IllegalStateException("No type stored on node."); } @@ -233,8 +234,8 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre } @Override - public U createEntity(Node state, Class type) { - Class javaType = getJavaType(state); + public U createEntity(Node state, Class type) { + Class javaType = getJavaType(state); if (javaType == null) { throw new IllegalStateException("No type stored on node."); } @@ -245,11 +246,11 @@ public class SubReferenceNodeTypeRepresentationStrategy implements NodeTypeRepre } @Override - public U projectEntity(Node state, Class type) { + public U projectEntity(Node state, Class type) { return entityInstantiator.createEntityFromState(state, type); } - private static class ClosableCombiningIterable extends CombiningIterable implements ClosableIterable { + private static class ClosableCombiningIterable extends CombiningIterable implements ClosableIterable { private final Iterable> relIterables; public ClosableCombiningIterable(Iterable> relIterables) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/TypeRepresentationStrategyFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/TypeRepresentationStrategyFactory.java index cede57cda..da10e22ee 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/TypeRepresentationStrategyFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/typerepresentation/TypeRepresentationStrategyFactory.java @@ -20,17 +20,17 @@ import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.springframework.data.neo4j.core.*; -import org.springframework.data.persistence.EntityInstantiator; +import org.springframework.data.neo4j.support.EntityInstantiator; public class TypeRepresentationStrategyFactory { private GraphDatabaseService graphDatabaseService; - private EntityInstantiator graphEntityInstantiator; - private EntityInstantiator relationshipEntityInstantiator; + private EntityInstantiator graphEntityInstantiator; + private EntityInstantiator relationshipEntityInstantiator; private Strategy strategy; public TypeRepresentationStrategyFactory(GraphDatabaseService graphDatabaseService, - EntityInstantiator graphEntityInstantiator, - EntityInstantiator relationshipEntityInstantiator) { + EntityInstantiator graphEntityInstantiator, + EntityInstantiator relationshipEntityInstantiator) { this.graphDatabaseService = graphDatabaseService; this.graphEntityInstantiator = graphEntityInstantiator; this.relationshipEntityInstantiator = relationshipEntityInstantiator; @@ -67,40 +67,40 @@ public class TypeRepresentationStrategyFactory { private enum Strategy { SubRef { @Override - public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { return new SubReferenceNodeTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator); } @Override - public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator) { - return new NoopRelationshipTypeRepresentationStrategy(); + public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator) { + return new NoopRelationshipTypeRepresentationStrategy(relationshipEntityInstantiator); } }, Indexed { @Override - public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { return new IndexingNodeTypeRepresentationStrategy(graphDatabaseService, graphEntityInstantiator); } @Override - public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator) { + public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator) { return new IndexingRelationshipTypeRepresentationStrategy(graphDatabaseService, relationshipEntityInstantiator); } }, Noop { @Override - public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { - return new NoopNodeTypeRepresentationStrategy(); + public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + return new NoopNodeTypeRepresentationStrategy(graphEntityInstantiator); } @Override - public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator) { - return new NoopRelationshipTypeRepresentationStrategy(); + public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator) { + return new NoopRelationshipTypeRepresentationStrategy(relationshipEntityInstantiator); } }; - public abstract NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator); + public abstract NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator); - public abstract RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator); + public abstract RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator relationshipEntityInstantiator); } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Car.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Car.java index 505742632..1377024ab 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Car.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Car.java @@ -17,14 +17,17 @@ package org.springframework.data.neo4j; import org.neo4j.graphdb.Node; +import org.springframework.data.neo4j.annotation.GraphId; import org.springframework.data.neo4j.annotation.NodeEntity; @NodeEntity public abstract class Car { - public Car() { + @GraphId + long id; + public Car() { } public Car(Node n) { - setPersistentState(n); + this.id = n.getId(); } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Friendship.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Friendship.java index c77f3bd12..bb1dda7f0 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Friendship.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Friendship.java @@ -17,14 +17,11 @@ package org.springframework.data.neo4j; -import java.util.Date; - -import org.springframework.data.neo4j.annotation.EndNode; -import org.springframework.data.neo4j.annotation.Indexed; -import org.springframework.data.neo4j.annotation.RelationshipEntity; -import org.springframework.data.neo4j.annotation.StartNode; +import org.springframework.data.neo4j.annotation.*; import org.springframework.data.neo4j.fieldaccess.DynamicProperties; +import java.util.Date; + @RelationshipEntity(useShortNames = false) public class Friendship { @@ -48,6 +45,9 @@ public class Friendship { @EndNode private Person p2; + @RelationshipType + private String type; + @Indexed private int years; @@ -56,8 +56,14 @@ public class Friendship { private DynamicProperties personalProperties; private transient String latestLocation; - - public void setPerson1(Person p) { + + public Friendship(Person start, Person end, String type) { + this.p1 = start; + this.p2 = end; + this.type = type; + } + + public void setPerson1(Person p) { p1 = p; } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Group.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Group.java index 0fbbba916..f5b55b0e0 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Group.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Group.java @@ -23,8 +23,7 @@ import org.neo4j.kernel.Traversal; import org.neo4j.kernel.impl.traversal.TraversalDescriptionImpl; import org.springframework.data.neo4j.annotation.*; import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.mapping.Neo4JPersistentProperty; +import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import java.util.Collection; @@ -118,7 +117,7 @@ public class Group { private static class PeopleTraversalBuilder implements FieldTraversalDescriptionBuilder { @Override - public TraversalDescription build(NodeBacked start, Neo4JPersistentProperty property, String...params) { + public TraversalDescription build(Object start, Neo4jPersistentProperty property, String...params) { return new TraversalDescriptionImpl() .relationships(DynamicRelationshipType.withName(params[0])) .filter(Traversal.returnAllButStartNode()); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/GroupRepository.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/GroupRepository.java index ca58ecde9..a3bf51640 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/GroupRepository.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/GroupRepository.java @@ -16,8 +16,8 @@ package org.springframework.data.neo4j; -import org.springframework.data.neo4j.repository.NamedIndexRepository; import org.springframework.data.neo4j.repository.GraphRepository; +import org.springframework.data.neo4j.repository.NamedIndexRepository; /** * @author mh diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Person.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Person.java index 59620ee19..fd71c27b0 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Person.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Person.java @@ -16,21 +16,16 @@ package org.springframework.data.neo4j; -import java.util.Date; -import java.util.Map; +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.springframework.data.neo4j.annotation.*; +import org.springframework.data.neo4j.fieldaccess.DynamicProperties; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.Size; - -import org.neo4j.graphdb.Direction; -import org.springframework.data.neo4j.annotation.GraphId; -import org.springframework.data.neo4j.annotation.Indexed; -import org.springframework.data.neo4j.annotation.NodeEntity; -import org.springframework.data.neo4j.annotation.Query; -import org.springframework.data.neo4j.annotation.RelatedTo; -import org.springframework.data.neo4j.annotation.RelatedToVia; -import org.springframework.data.neo4j.fieldaccess.DynamicProperties; +import java.util.Date; +import java.util.Map; @NodeEntity @@ -87,6 +82,10 @@ public class Person { @Query(value = "start person=(%start) match (person)<-[:persons]-(team)-[:persons]->(member) return member.name, member.age") private Iterable> otherTeamMemberData; + public Person(Node n) { + this.graphId = n.getId(); + } + public String getBossName() { return bossName; } @@ -174,7 +173,7 @@ public class Person { } public Friendship knows(Person p) { - return (Friendship)relateTo(p, Friendship.class,"knows"); + return new Friendship(this, p, "knows"); } public void setPersonality(Personality personality) { @@ -232,8 +231,4 @@ public class Person { public void setNickname(String nickname) { this.nickname = nickname; } - - public static Person persistedPerson(String name, int age) { - return new Person(name,age).persist(); - } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/PersonCreator.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/PersonCreator.java index dc0233e2e..9e3a214b8 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/PersonCreator.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/PersonCreator.java @@ -26,8 +26,6 @@ import org.springframework.data.persistence.StateBackedCreator; public class PersonCreator implements StateBackedCreator { @Override public Person create(Node n, Class c) throws Exception { - Person person = new Person(); - person.setPersistentState(n); - return person; + return new Person(n); } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/SubGroup.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/SubGroup.java index 2675382d9..99793f3a1 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/SubGroup.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/SubGroup.java @@ -16,7 +16,7 @@ package org.springframework.data.neo4j; -import org.springframework.data.neo4j.annotation.*; +import org.springframework.data.neo4j.annotation.NodeEntity; @NodeEntity public class SubGroup extends Group { diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Volvo.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Volvo.java index 81ed1912f..cd0946f84 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Volvo.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/Volvo.java @@ -16,8 +16,6 @@ package org.springframework.data.neo4j; -import org.springframework.data.neo4j.annotation.NodeEntity; - public class Volvo extends Car { public Volvo() { } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/DataGraphNamespaceHandlerTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/DataGraphNamespaceHandlerTest.java index 4965c4346..52304a1a7 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/DataGraphNamespaceHandlerTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/config/DataGraphNamespaceHandlerTest.java @@ -23,7 +23,6 @@ import org.neo4j.kernel.EmbeddedGraphDatabase; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.neo4j.PersonRepository; -import org.springframework.data.neo4j.PersonRepository; import org.springframework.data.neo4j.repository.DirectGraphRepositoryFactory; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.transaction.PlatformTransactionManager; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicPropertyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicPropertyTest.java index 80d11902c..a2a54d6b0 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicPropertyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/fieldaccess/PrefixedDynamicPropertyTest.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import org.junit.Test; +import org.neo4j.helpers.collection.IteratorUtil; public class PrefixedDynamicPropertyTest { private static final String VALUE = "value"; @@ -40,12 +41,7 @@ public class PrefixedDynamicPropertyTest { PrefixedDynamicProperties props = new PrefixedDynamicProperties(PREFIX); private List toList(Iterable it) { - List result = new ArrayList(); - for (T t : it) { - result.add(t); - } - - return result; + return IteratorUtil.addToCollection(it, new ArrayList()); } @Test diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4JMappingContextTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4JMappingContextTest.java index ea208ceb4..4d096911d 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4JMappingContextTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4JMappingContextTest.java @@ -19,33 +19,31 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.neo4j.Person; -import java.util.Collections; - import static org.junit.Assert.assertEquals; /** * @author mh * @since 19.09.11 */ -public class Neo4JMappingContextTest { +public class Neo4jMappingContextTest { - private Neo4JMappingContext mappingContext; - private Neo4JPersistentEntityImpl personType; + private Neo4jMappingContext mappingContext; + private Neo4jPersistentEntityImpl personType; @Before public void setUp() throws Exception { - mappingContext = new Neo4JMappingContext(); + mappingContext = new Neo4jMappingContext(); personType = mappingContext.getPersistentEntity(Person.class); } @Test public void checkGraphIdProperty() { - final Neo4JPersistentProperty idProperty = personType.getIdProperty(); + final Neo4jPersistentProperty idProperty = personType.getIdProperty(); assertEquals("graphId", idProperty.getName()); } @Test public void checkNameProperty() { - final Neo4JPersistentProperty nameProperty = personType.getPersistentProperty("name"); + final Neo4jPersistentProperty nameProperty = personType.getPersistentProperty("name"); assertEquals("name",nameProperty.getName()); assertEquals(String.class,nameProperty.getType()); assertEquals(true,nameProperty.isIndexed()); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverterTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverterTest.java new file mode 100644 index 000000000..3cab8bf03 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/mapping/Neo4jNodeConverterTest.java @@ -0,0 +1,180 @@ +/** + * 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.junit.Test; +import org.neo4j.graphdb.*; +import org.neo4j.graphdb.index.Index; +import org.neo4j.graphdb.index.IndexHits; +import org.neo4j.helpers.collection.IteratorUtil; +import org.neo4j.test.ImpermanentGraphDatabase; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.neo4j.Person; +import org.springframework.data.neo4j.Personality; +import org.springframework.data.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean; +import org.springframework.data.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory; +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.typerepresentation.NoopNodeTypeRepresentationStrategy; + +import java.io.IOException; +import java.util.Date; + +import static org.junit.Assert.assertEquals; + +/** + * @author mh + * @since 19.09.11 + */ +public class Neo4jNodeConverterTest { + + private Neo4jNodeConverterImpl converter; + private Transaction tx; + private GraphDatabaseContext gdc; + + @Before + public void setUp() throws Exception { + final Neo4jMappingContext mappingContext = new Neo4jMappingContext(); + gdc = createContext(mappingContext); + tx = gdc.beginTx(); + final NodeEntityStateFactory nodeEntityStateFactory = new NodeEntityStateFactory(); + nodeEntityStateFactory.setMappingContext(mappingContext); + nodeEntityStateFactory.setGraphDatabaseContext(gdc); + nodeEntityStateFactory.setNodeDelegatingFieldAccessorFactory(new NodeDelegatingFieldAccessorFactory(gdc)); + converter = new Neo4jNodeConverterImpl(nodeEntityStateFactory); + gdc.setConverter(converter); + } + + private GraphDatabaseContext createContext(Neo4jMappingContext mappingContext) throws Exception { + GraphDatabaseContext gdc = new GraphDatabaseContext(); + gdc.setGraphDatabaseService(new ImpermanentGraphDatabase()); + gdc.setMappingContext(mappingContext); + gdc.setNodeTypeRepresentationStrategy(new NoopNodeTypeRepresentationStrategy(new NodeEntityInstantiator(mappingContext))); + gdc.setConversionService(new Neo4jConversionServiceFactoryBean().getObject()); + return gdc; + } + + @After + public void tearDown() throws Exception { + tx.failure(); + tx.finish(); + } + + @Test + public void testWriteEntityToNewNode() { + final Person p = new Person("Michael",37); + converter.write(p,null); + final Node createdNode = gdc.getNodeById(p.getId()); + assertEquals("Michael", createdNode.getProperty("name")); + } + @Test + public void testFindNewlyWrittenNodeInIndex() { + final Person p = new Person("Michael",37); + converter.write(p,null); + final Node createdNode = gdc.getNodeById(p.getId()); + final Index index = gdc.getIndex(Person.class,Person.NAME_INDEX); + final Node found = index.get("name", "Michael").getSingle(); + assertEquals("node found in index", createdNode, found); + } + @Test + public void testWriteEntityToExistingNode() { + final Node existingNode = gdc.createNode(); + final Person p = new Person("Michael",37); + converter.write(p, existingNode); + assertEquals("Entity uses provided node", existingNode.getId(), p.getId()); + assertEquals("Michael", existingNode.getProperty("name")); + } + + @Test + public void testUpdateExistingNode() { + final Node existingNode = gdc.createNode(); + existingNode.setProperty("name","Test"); + assertEquals("Test", existingNode.getProperty("name")); + final Person p = new Person("Michael",37); + converter.write(p, existingNode); + assertEquals("Michael", existingNode.getProperty("name")); + p.setName("Emil"); + converter.write(p, existingNode); + assertEquals("Emil", existingNode.getProperty("name")); + } + + @Test + public void testWriteConvertedPropertiesToExistingNode() { + final Node existingNode = gdc.createNode(); + final Person p = new Person("Michael",37); + p.setBirthdate(new Date(100)); + p.setPersonality(Personality.EXTROVERT); + converter.write(p, existingNode); + assertEquals("100", existingNode.getProperty("birthdate")); + assertEquals("EXTROVERT", existingNode.getProperty("personality")); + } + @Test + public void testReadConvertedPropertiesToExistingNode() { + final Node existingNode = gdc.createNode(); + existingNode.setProperty("name","Michael"); + existingNode.setProperty("age",36); + existingNode.setProperty("personality","EXTROVERT"); + existingNode.setProperty("birthdate","100"); + final Person p = converter.read(Person.class,existingNode); + assertEquals("Michael", p.getName()); + assertEquals(36, p.getAge()); + assertEquals(new Date(100),p.getBirthdate()); + assertEquals(Personality.EXTROVERT,p.getPersonality()); + } + + @Test + public void testDeleteProperty() { + final Node existingNode = gdc.createNode(); + final Person p = new Person("Michael",37); + converter.write(p, existingNode); + p.setName(null); + converter.write(p, existingNode); + assertEquals(false, existingNode.hasProperty("name")); + } + + @Test + public void testReadEntityFromExistingNode() { + final Node node = gdc.createNode(); + node.setProperty("name","Emil"); + final Person p = converter.read(Person.class, node); + assertEquals("Emil", p.getName()); + } + + @Test + public void testSetRelationshipWithPreExistingNode() { + final Person p = new Person("Michael",37); + final Person emil = new Person("Emil", 30); + converter.write(emil, null); + p.setBoss(emil); + converter.write(p, null); + final Node node = gdc.getNodeById(p.getId()); + final Node boss = node.getRelationships(DynamicRelationshipType.withName("boss"), Direction.INCOMING).iterator().next().getStartNode(); + assertEquals("added additional relationship end node",emil.getId(), boss.getId()); + } + @Test + public void testSetRelationshipWithNonExistingNode() { + final Person p = new Person("Michael",37); + final Person emil = new Person("Emil", 30); + p.setBoss(emil); + converter.write(p, null); + final Node node = gdc.getNodeById(p.getId()); + final Node boss = node.getRelationships(DynamicRelationshipType.withName("boss"), Direction.INCOMING).iterator().next().getStartNode(); + assertEquals("added additional relationship end node",emil.getId(), boss.getId()); + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/MatchClauseUnitTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/MatchClauseUnitTest.java index d09c22ce3..0f495c763 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/MatchClauseUnitTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/query/MatchClauseUnitTest.java @@ -28,7 +28,7 @@ import org.neo4j.graphdb.Direction; import org.springframework.data.neo4j.annotation.Indexed; import org.springframework.data.neo4j.annotation.NodeEntity; import org.springframework.data.neo4j.annotation.RelatedTo; -import org.springframework.data.neo4j.mapping.Neo4JMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.repository.query.parser.Property; /** @@ -37,11 +37,11 @@ import org.springframework.data.repository.query.parser.Property; */ public class MatchClauseUnitTest { - Neo4JMappingContext context; + Neo4jMappingContext context; @Before public void setUp() { - context = new Neo4JMappingContext(); + context = new Neo4jMappingContext(); context.setInitialEntitySet(Collections.singleton(Person.class)); context.afterPropertiesSet(); }