diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java index 76be46e32..135d44090 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/DataGraphBeanDefinitionParser.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.context.annotation.ConfigurationClassPostProcessor; +import org.springframework.util.ClassUtils; import org.w3c.dom.Element; import static org.springframework.util.StringUtils.hasText; @@ -32,22 +33,40 @@ import static org.springframework.util.StringUtils.hasText; public class DataGraphBeanDefinitionParser extends AbstractBeanDefinitionParser { private static final String GRAPH_DATABASE_SERVICE = "graphDatabaseService"; + public static final String ASPECTJ_CONFIG = "org.springframework.data.neo4j.config.Neo4jAspectConfiguration"; + public static final String CROSS_STORE_CONFIG = "org.springframework.data.neo4j.config.CrossStoreNeo4jConfiguration"; @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext context) { - BeanDefinitionBuilder configBuilder = createConfigurationBeanDefinition(); + BeanDefinitionBuilder configBuilder = createConfigurationBeanDefinition(element); setupGraphDatabase(element, context, configBuilder); setupEntityManagerFactory(element, configBuilder); setupConfigurationClassPostProcessor(context); return getSourcedBeanDefinition(configBuilder, element, context); } - private BeanDefinitionBuilder createConfigurationBeanDefinition() { - BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder.rootBeanDefinition(Neo4jConfiguration.class); + + private BeanDefinitionBuilder createConfigurationBeanDefinition(Element element) { + BeanDefinitionBuilder configBuilder = createConfigBuilderByMode(element); configBuilder.setAutowireMode(Autowire.BY_TYPE.value()); return configBuilder; } + // todo cross-store + private BeanDefinitionBuilder createConfigBuilderByMode(Element element) { + if (isModeCrossStore(element)) return BeanDefinitionBuilder.rootBeanDefinition(CROSS_STORE_CONFIG); + if (isModeAspectJ()) return BeanDefinitionBuilder.rootBeanDefinition(ASPECTJ_CONFIG); + return BeanDefinitionBuilder.rootBeanDefinition(Neo4jConfiguration.class); + } + + private boolean isModeCrossStore(Element element) { + return isModeAspectJ() && isEntityManagerFactoryConfigured(element); + } + + private boolean isModeAspectJ() { + return ClassUtils.isPresent(ASPECTJ_CONFIG, getClass().getClassLoader()); + } + private void setupConfigurationClassPostProcessor(final ParserContext parserContext) { BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry(); @@ -72,12 +91,17 @@ public class DataGraphBeanDefinitionParser extends AbstractBeanDefinitionParser } private void setupEntityManagerFactory(Element element, BeanDefinitionBuilder configBuilder) { - String entityManagerFactory = element.getAttribute("entityManagerFactory"); - if (hasText(entityManagerFactory)) { + if (isEntityManagerFactoryConfigured(element)) { + String entityManagerFactory = element.getAttribute("entityManagerFactory"); configBuilder.addPropertyReference("entityManagerFactory", entityManagerFactory); } } + private boolean isEntityManagerFactoryConfigured(Element element) { + String entityManagerFactory = element.getAttribute("entityManagerFactory"); + return hasText(entityManagerFactory); + } + private String handleStoreDir(Element element, ParserContext context, BeanDefinitionBuilder configBuilder) { String storeDir = element.getAttribute("storeDirectory"); if (!hasText(storeDir)) return null; 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 ca8340574..242765d19 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,9 +16,6 @@ package org.springframework.data.neo4j.config; -import javax.annotation.PostConstruct; -import javax.validation.Validator; - import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; @@ -30,8 +27,6 @@ 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.fieldaccess.DelegatingFieldAccessorFactory; import org.springframework.data.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean; import org.springframework.data.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory; @@ -40,6 +35,7 @@ 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.EntityStateHandler; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.node.NodeEntityInstantiator; import org.springframework.data.neo4j.support.node.NodeEntityStateFactory; @@ -51,6 +47,9 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.transaction.jta.UserTransactionAdapter; +import javax.annotation.PostConstruct; +import javax.validation.Validator; + /** * Abstract base class for code based configuration of Spring managed Neo4j infrastructure. *

Subclasses are required to provide an implementation of graphDbService .... @@ -87,6 +86,7 @@ public abstract class Neo4jConfiguration { gdc.setConversionService(conversionService()); gdc.setMappingContext(mappingContext()); gdc.setConverter(neo4jConverter()); + gdc.setEntityStateHandler(entityStateHandler()); gdc.setNodeTypeRepresentationStrategy(typeRepresentationStrategyFactory.getNodeTypeRepresentationStrategy()); gdc.setRelationshipTypeRepresentationStrategy(typeRepresentationStrategyFactory.getRelationshipTypeRepresentationStrategy()); if (validator!=null) { @@ -95,6 +95,11 @@ public abstract class Neo4jConfiguration { return gdc; } + @Bean + public EntityStateHandler entityStateHandler() { + return new EntityStateHandler(mappingContext(),graphDatabaseService); + } + @Bean public Neo4jNodeConverterImpl neo4jConverter() throws Exception { return new Neo4jNodeConverterImpl(); @@ -106,13 +111,13 @@ public abstract class Neo4jConfiguration { } @Bean - protected RelationshipEntityInstantiator graphRelationshipInstantiator() { - return new RelationshipEntityInstantiator(mappingContext()); + protected RelationshipEntityInstantiator graphRelationshipInstantiator() throws Exception { + return new RelationshipEntityInstantiator(entityStateHandler()); } @Bean - protected EntityInstantiator graphEntityInstantiator() { - return new NodeEntityInstantiator(mappingContext()); + protected EntityInstantiator graphEntityInstantiator() throws Exception { + return new NodeEntityInstantiator(entityStateHandler()); } @Bean diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/GraphBackedEntityIterableWrapper.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/GraphBackedEntityIterableWrapper.java new file mode 100644 index 000000000..cee856e77 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/GraphBackedEntityIterableWrapper.java @@ -0,0 +1,45 @@ +/** + * 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.fieldaccess; + +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.helpers.collection.IterableWrapper; +import org.springframework.data.neo4j.support.GraphDatabaseContext; + +/** + * Simple wrapper to create an Iterable over @NodeEntities or @RelationshipEntities from an iterable over Nodes or Relationships. + * Creates NodeEntities on the fly while iterating the Iterator from original iterable. + */ +public class GraphBackedEntityIterableWrapper extends IterableWrapper { + private final Class targetType; + private final GraphDatabaseContext graphDatabaseContext; + + public GraphBackedEntityIterableWrapper(Iterable iterable, Class targetType, final GraphDatabaseContext graphDatabaseContext) { + super(iterable); + this.targetType = targetType; + this.graphDatabaseContext = graphDatabaseContext; + } + + @Override + protected ENTITY underlyingObjectToObject(STATE s) { + return graphDatabaseContext.createEntityFromState(s, targetType); + } + + public static GraphBackedEntityIterableWrapper create( + Iterable iterable, Class targetType, final GraphDatabaseContext graphDatabaseContext) { + return new GraphBackedEntityIterableWrapper(iterable, targetType, graphDatabaseContext); + } +} \ No newline at end of file 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 52ce0041f..8ccabc766 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 @@ -16,10 +16,11 @@ package org.springframework.data.neo4j.fieldaccess; -import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity; +import org.springframework.data.neo4j.core.EntityState; import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.DoReturn; import org.springframework.data.neo4j.support.GraphDatabaseContext; +import org.springframework.data.neo4j.support.ManagedEntity; import java.util.AbstractSet; import java.util.Collection; @@ -68,15 +69,20 @@ public class ManagedFieldAccessorSet extends AbstractSet { } private void update() { - final Neo4jPersistentEntity persistentEntity = property.getOwner(); - if (persistentEntity.isNodeEntity()) { - updateValue(); - } - if (persistentEntity.isRelationshipEntity()) { + if (ctx.isManaged(entity)) { + updateValueWithState(((ManagedEntity)entity).getEntityState()); + } else { updateValue(); } } + private Object updateValueWithState(EntityState entityState) { + final Object newValue = entityState.setValue(property, delegate); + if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue); + property.setValue(entity, newValue); + return newValue; + } + private Object updateValue() { final Object newValue = fieldAccessor.setValue(entity,delegate); if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue); 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 7eae2fb26..eba9695a3 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 @@ -15,18 +15,13 @@ */ package org.springframework.data.neo4j.fieldaccess; -import java.lang.reflect.Field; -import java.util.Map; - -import org.neo4j.graphdb.Node; import org.springframework.data.neo4j.core.EntityState; - - - 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; +import org.springframework.data.neo4j.support.ManagedEntity; + +import java.util.Map; /** * Updates the entity containing such a ManagedPrefixedDynamicProperties when some property is added, changed or @@ -82,10 +77,6 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties return d; } - private void update() { - updateValue(); - } - private Object updateValue() { final Object newValue = fieldAccessor.setValue(entity, this); if (newValue instanceof DoReturn) @@ -93,4 +84,19 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties property.setValue(entity, newValue); return newValue; } + + private void update() { + if (graphDatabaseContext.isManaged(entity)) { + updateValueWithState(((ManagedEntity)entity).getEntityState()); + } else { + updateValue(); + } + } + + private Object updateValueWithState(EntityState entityState) { + final Object newValue = entityState.setValue(property, 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/NodeToNodesRelationshipFieldAccessor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/NodeToNodesRelationshipFieldAccessor.java index 82f9ab8b6..d1503e7bc 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,11 +20,8 @@ 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.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import org.springframework.util.Assert; import java.util.HashSet; import java.util.Set; @@ -59,7 +56,7 @@ public abstract class NodeToNodesRelationshipFieldAccessor extends AbstractNodeR @Override protected Node getState(final Object entity) { - return property.getOwner().getPersistentState(entity, graphDatabaseContext); + return graphDatabaseContext.getPersistentState(entity); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java index fe55d3613..c5b6b32b5 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java @@ -16,17 +16,15 @@ package org.springframework.data.neo4j.fieldaccess; -import org.neo4j.graphdb.*; +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; import org.springframework.dao.InvalidDataAccessApiUsageException; - - import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty; import org.springframework.data.neo4j.mapping.RelationshipInfo; import org.springframework.data.neo4j.support.GraphDatabaseContext; -import java.util.HashSet; -import java.util.Set; - import static org.springframework.data.neo4j.support.DoReturn.doReturn; public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccessorFactory { @@ -68,22 +66,16 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess @Override public Object getValue(final Object entity) { checkUnderlyingNode(entity); - final Set result = createEntitySetFromRelationships(entity); - return doReturn(new ManagedFieldAccessorSet(entity, result, property,graphDatabaseContext, this)); - } + return doReturn(iterableFrom(entity)); + } - private Set createEntitySetFromRelationships(final Object entity) { - final Set result = new HashSet(); - for (final Relationship rel : getStatesFromEntity(entity)) { - final Object relationshipEntity = graphDatabaseContext.createEntityFromState(rel, relatedType); - result.add(relationshipEntity); - } - return result; - } + private GraphBackedEntityIterableWrapper iterableFrom(final Object entity) { + return GraphBackedEntityIterableWrapper.create(getStatesFromEntity(entity), relatedType, graphDatabaseContext); + } @Override protected Iterable getStatesFromEntity(final Object entity) { - final Node persistentState = property.getOwner().getPersistentState(entity, graphDatabaseContext); + final Node persistentState = getState(entity); return persistentState.getRelationships(type, direction); } @@ -94,7 +86,7 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess @Override protected Node getState(final Object entity) { - return property.getOwner().getPersistentState(entity, graphDatabaseContext); + return graphDatabaseContext.getPersistentState(entity); } } 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 bc5f716da..adc5cee86 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 @@ -24,6 +24,7 @@ import org.springframework.data.neo4j.mapping.RelationshipInfo; import org.springframework.data.neo4j.support.GraphDatabaseContext; import java.util.Collections; +import java.util.HashSet; import java.util.Set; import static org.springframework.data.neo4j.support.DoReturn.doReturn; @@ -73,5 +74,9 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel return doReturn(createManagedSet(entity, result)); } - } + @Override + public Object getDefaultImplementation() { + return new HashSet(); + } + } } 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 3323861b9..3b9e2a21b 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 @@ -35,5 +35,5 @@ public interface Neo4jPersistentEntity extends PersistentEntity T getPersistentState(Object entity, GraphDatabaseContext service); + Object getPersistentId(Object entity); } 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 5510d0e10..eef283cb9 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 @@ -23,7 +23,6 @@ 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; @@ -97,19 +96,10 @@ public class Neo4jPersistentEntityImpl extends BasicPersistentEntity T getPersistentState(Object entity, GraphDatabaseContext service) { + public Object getPersistentId(Object entity) { 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"); + return idProperty.getValue(entity); } public String getEntityName() { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/EntityStateHandler.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/EntityStateHandler.java new file mode 100644 index 000000000..a8fb0cefe --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/EntityStateHandler.java @@ -0,0 +1,85 @@ +/** + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.neo4j.support; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.PropertyContainer; +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.mapping.Neo4jPersistentEntityImpl; + +/** + * @author mh + * @since 02.10.11 + */ +public class EntityStateHandler { + + private Neo4jMappingContext mappingContext; + private final GraphDatabaseService service; + + public EntityStateHandler(Neo4jMappingContext mappingContext, GraphDatabaseService service) { + this.mappingContext = mappingContext; + this.service = service; + } + + 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 boolean isManaged(Object entity) { + return entity instanceof ManagedEntity; + } + + @SuppressWarnings("unchecked") + 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); + final Object id = persistentEntity.getPersistentId(entity); + if (id == null) return null; // todo create new node? + if (!(id instanceof Number)) + throw new IllegalArgumentException("The id of " + persistentEntity.getEntityName() + " " + persistentEntity.getIdProperty() + " is not a number"); + long graphId = ((Number) id).longValue(); + if (isNodeEntity(type)) { + return (S) service.getNodeById(graphId); + } + if (isRelationshipEntity(type)) { + return (S) service.getRelationshipById(graphId); + } + throw new IllegalArgumentException("The entity " + persistentEntity.getEntityName() + " has to be either annotated with @NodeEntity or @RelationshipEntity"); + } + + public boolean isNodeEntity(Class targetType) { + return mappingContext.isNodeEntity(targetType); + } + + public boolean isRelationshipEntity(Class targetType) { + return mappingContext.isRelationshipEntity(targetType); + } +} 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 163f036cf..cc0cf6724 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 @@ -32,7 +32,6 @@ 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; @@ -67,7 +66,8 @@ public class GraphDatabaseContext { private RelationshipTypeRepresentationStrategy relationshipTypeRepresentationStrategy; private Neo4jMappingContext mappingContext; - private final CypherQueryExecutor executor = new CypherQueryExecutor(this); + private CypherQueryExecutor cypherQueryExecutor; + private EntityStateHandler entityStateHandler; public Index getIndex(Class type) { @@ -128,33 +128,16 @@ public class GraphDatabaseContext { S state = getPersistentState(entity); return getTypeRepresentationStrategy(state, targetType).projectEntity(state, targetType); } - + @SuppressWarnings("unchecked") 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); + return entityStateHandler.getPersistentState(entity); } // todo depending on type of mapping @SuppressWarnings("unchecked") 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); + entityStateHandler.setPersistentState(entity, state); } public void postEntityCreation(S node, Class entityClass) { @@ -163,8 +146,7 @@ public class GraphDatabaseContext { @SuppressWarnings("unchecked") public Iterable findAllByTraversal(Object entity, Class targetType, TraversalDescription traversalDescription) { - final Neo4jPersistentEntityImpl persistentEntity = mappingContext.getPersistentEntity(entity.getClass()); - final PropertyContainer state = persistentEntity.getPersistentState(entity, this); + final PropertyContainer state = entityStateHandler.getPersistentState(entity); if (state == null) throw new IllegalStateException("No node attached to " + this); final Traverser traverser = traversalDescription.traverse((Node) state); if (Node.class.isAssignableFrom(targetType)) return (Iterable) traverser.nodes(); @@ -317,6 +299,7 @@ public class GraphDatabaseContext { public void setGraphDatabaseService(GraphDatabaseService graphDatabaseService) { this.graphDatabaseService = graphDatabaseService; + this.cypherQueryExecutor = new CypherQueryExecutor(this); } public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy() { @@ -373,8 +356,8 @@ public class GraphDatabaseContext { } } - private boolean isManaged(Object entity) { - return entity instanceof ManagedEntity; + public boolean isManaged(Object entity) { + return entityStateHandler.isManaged(entity); } public void setConverter(Neo4jNodeConverter converter) { @@ -383,14 +366,19 @@ public class GraphDatabaseContext { public Object executeQuery(Object entity, String queryString, Map params, Neo4jPersistentProperty property) { final TypeInformation typeInformation = property.getTypeInformation(); - final Class targetType = typeInformation.getActualType().getType(); + final TypeInformation actualType = typeInformation.getActualType(); + final Class targetType = actualType.getType(); + if (actualType.isMap()) { + return cypherQueryExecutor.queryForList(queryString, params); + } if (typeInformation.isCollectionLike()) { - return executor.query(queryString, targetType, params); + return cypherQueryExecutor.query(queryString, targetType, params); } - if (typeInformation.isMap()) { - return executor.queryForList(queryString, params); - } - return executor.queryForObject(queryString, targetType, params); + return cypherQueryExecutor.queryForObject(queryString, targetType, params); + } + + public void setEntityStateHandler(EntityStateHandler entityStateHandler) { + this.entityStateHandler = entityStateHandler; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java index 84d238695..73ba5512f 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/ManagedEntity.java @@ -15,16 +15,18 @@ */ package org.springframework.data.neo4j.support; -import org.neo4j.graphdb.PropertyContainer; +import org.springframework.data.neo4j.core.EntityState; /** * @author mh * @since 02.10.11 */ -public interface ManagedEntity { - T persist(); +public interface ManagedEntity { + U persist(); S getPersistentState(); + EntityState getEntityState(); + void setPersistentState(S state); } 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 27be3cea1..0f2c155c5 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,9 +17,8 @@ package org.springframework.data.neo4j.support.node; import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; -import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.support.AbstractConstructorEntityInstantiator; +import org.springframework.data.neo4j.support.EntityStateHandler; /** @@ -31,10 +30,10 @@ import org.springframework.data.neo4j.support.AbstractConstructorEntityInstantia */ public class NodeEntityInstantiator extends AbstractConstructorEntityInstantiator { - private final Neo4jMappingContext mappingContext; + private final EntityStateHandler entityStateHandler; - public NodeEntityInstantiator(Neo4jMappingContext mappingContext) { - this.mappingContext = mappingContext; + public NodeEntityInstantiator(EntityStateHandler entityStateHandler) { + this.entityStateHandler = entityStateHandler; } @@ -44,7 +43,7 @@ public class NodeEntityInstantiator extends AbstractConstructorEntityInstantiato } protected void setPersistentState(Object entity, Node node) { - this.mappingContext.setPersistentState(entity,node); + this.entityStateHandler.setPersistentState(entity,node); } 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 bf286904d..4f45bb26c 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 @@ -24,6 +24,7 @@ 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.support.GraphDatabaseContext; +import org.springframework.data.neo4j.support.ManagedEntity; /** * @author Michael Hunger @@ -65,7 +66,9 @@ public class NodeEntityState extends DefaultEntityState { @Override public void setPersistentState(Node node) { - graphDatabaseContext.setPersistentState(entity,node); + if (!(entity instanceof ManagedEntity)) { + graphDatabaseContext.setPersistentState(entity, node); + } super.setPersistentState(node); } 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 1e2972a5e..a4b10cbdf 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,9 +17,8 @@ package org.springframework.data.neo4j.support.relationship; import org.neo4j.graphdb.Relationship; - -import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.support.AbstractConstructorEntityInstantiator; +import org.springframework.data.neo4j.support.EntityStateHandler; import sun.reflect.ReflectionFactory; /** @@ -30,15 +29,15 @@ import sun.reflect.ReflectionFactory; public class RelationshipEntityInstantiator extends AbstractConstructorEntityInstantiator { - private final Neo4jMappingContext mappingContext; + private final EntityStateHandler entityStateHandler; - public RelationshipEntityInstantiator(Neo4jMappingContext mappingContext) { - this.mappingContext = mappingContext; + public RelationshipEntityInstantiator(EntityStateHandler entityStateHandler) { + this.entityStateHandler = entityStateHandler; } @Override protected void setState(Object entity, Relationship relationship) { - this.mappingContext.setPersistentState(entity,relationship); + this.entityStateHandler.setPersistentState(entity,relationship); } @Override 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 index a48a64e6d..d1c3e93de 100644 --- 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 @@ -28,6 +28,7 @@ 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.EntityStateHandler; import org.springframework.data.neo4j.support.GraphDatabaseContext; import org.springframework.data.neo4j.support.node.NodeEntityInstantiator; import org.springframework.data.neo4j.support.node.NodeEntityStateFactory; @@ -63,10 +64,13 @@ public class Neo4jNodeConverterTest { private GraphDatabaseContext createContext(Neo4jMappingContext mappingContext) throws Exception { GraphDatabaseContext gdc = new GraphDatabaseContext(); - gdc.setGraphDatabaseService(new ImpermanentGraphDatabase()); + final ImpermanentGraphDatabase gdb = new ImpermanentGraphDatabase(); + gdc.setGraphDatabaseService(gdb); gdc.setMappingContext(mappingContext); - gdc.setNodeTypeRepresentationStrategy(new NoopNodeTypeRepresentationStrategy(new NodeEntityInstantiator(mappingContext))); + final EntityStateHandler entityStateHandler = new EntityStateHandler(mappingContext, gdb); + gdc.setNodeTypeRepresentationStrategy(new NoopNodeTypeRepresentationStrategy(new NodeEntityInstantiator(entityStateHandler))); gdc.setConversionService(new Neo4jConversionServiceFactoryBean().getObject()); + gdc.setEntityStateHandler(entityStateHandler); return gdc; }