diff --git a/spring-data-graph-parent/pom.xml b/spring-data-graph-parent/pom.xml index 8b3070311..b306cbd6e 100644 --- a/spring-data-graph-parent/pom.xml +++ b/spring-data-graph-parent/pom.xml @@ -16,7 +16,7 @@ 1.5.10 3.0.5.RELEASE 1.0.0.BUILD-SNAPSHOT - 1.3.M04 + 1.3-SNAPSHOT 1.6.11.M2 @@ -229,6 +229,17 @@ tests test + + org.neo4j + server-api + ${neo4j.version} + provided + + + commons-configuration + commons-configuration + 1.6 + org.ow2.jotm diff --git a/spring-data-neo4j-roo/src/test/resources/org/springframework/data/graph/neo4j/jpa/Neo4jEntityManagerTest-context.xml b/spring-data-neo4j-roo/src/test/resources/org/springframework/data/graph/neo4j/jpa/Neo4jEntityManagerTest-context.xml index 7c42a9636..dddb3438c 100644 --- a/spring-data-neo4j-roo/src/test/resources/org/springframework/data/graph/neo4j/jpa/Neo4jEntityManagerTest-context.xml +++ b/spring-data-neo4j-roo/src/test/resources/org/springframework/data/graph/neo4j/jpa/Neo4jEntityManagerTest-context.xml @@ -88,25 +88,27 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-neo4j/pom.xml b/spring-data-neo4j/pom.xml index 1bb7d7048..c1876dc39 100644 --- a/spring-data-neo4j/pom.xml +++ b/spring-data-neo4j/pom.xml @@ -132,6 +132,17 @@ test + + org.neo4j + server-api + provided + + + + commons-configuration + commons-configuration + + org.hibernate.javax.persistence @@ -168,6 +179,14 @@ + + neo4j-public-repository + Neo4J Public Repository + http://m2.neo4j.org + + true + + jboss-repository JBoss Public Repository diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedTo.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedTo.java index 0069041f3..315a81ba7 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedTo.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedTo.java @@ -16,14 +16,14 @@ package org.springframework.data.graph.annotation; +import org.springframework.data.graph.core.Direction; +import org.springframework.data.graph.core.NodeBacked; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.data.graph.core.Direction; -import org.springframework.data.graph.core.NodeBacked; - /** * Annotation for {@link org.springframework.data.graph.annotation.NodeEntity} fields that relate to other entities via * relationships. Works for one-to-one and one-to-many relationships. It is optionally possible to define the relationship type, @@ -31,10 +31,11 @@ import org.springframework.data.graph.core.NodeBacked; * * Collection based one-to-many relationships return managed collections that reflect addition and removal to the underlying relationships. * + * Examples: *
- * @RelatedTo([type="friends"], elementClass=Person.class)
+ * @RelatedTo(elementClass=Person.class)
  * Collection<Person> friends;
- * @RelatedTo([type="spouse"], [elementClass=Person.class])
+ * @RelatedTo(type="partner")
  * Person spouse;
  * 
@@ -47,7 +48,7 @@ public @interface RelatedTo { /** * @return name of the relationship type, optional, can be inferred from the field name */ - String type(); + String type() default ""; /** * @return direction for the relationship, by default outgoing diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java index 318bc997d..1d73667f8 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/annotation/RelatedToVia.java @@ -52,5 +52,5 @@ public @interface RelatedToVia { /** * @return target relationship entity class */ - Class elementClass() default RelationshipBacked.class; + Class elementClass(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/ConfigurationCheck.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/ConfigurationCheck.java new file mode 100644 index 000000000..c7d0be8d3 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/ConfigurationCheck.java @@ -0,0 +1,67 @@ +package org.springframework.data.graph.neo4j.config; + +import org.neo4j.graphdb.Transaction; +import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; + +import javax.annotation.PostConstruct; + +/** + * Validates correct configuration of Neo4j and Spring, especially transaction-managers + */ +public class ConfigurationCheck { + GraphDatabaseContext graphDatabaseContext; + PlatformTransactionManager transactionManager; + + public ConfigurationCheck(GraphDatabaseContext graphDatabaseContext, PlatformTransactionManager transactionManager) { + this.graphDatabaseContext = graphDatabaseContext; + this.transactionManager = transactionManager; + } + + @PostConstruct + private void checkConfiguration() { + checkInjection(); + checkSpringTransactionManager(); + checkNeo4jTransactionManager(); + } + + private void checkInjection() { + assert graphDatabaseContext.getGraphDatabaseService()!=null : "graphDatabaseService not correctly configured, please refer to the manual, setup section"; + } + + private void checkSpringTransactionManager() { + try { + TransactionStatus transaction = transactionManager.getTransaction(null); + updateStartTime(); + transactionManager.commit(transaction); + } catch(Exception e) { + AssertionError error = new AssertionError("transactionManager not correctly configured, please refer to the manual, setup section"); + error.initCause(e); + throw error; + } + } + + private void checkNeo4jTransactionManager() { + Transaction tx = null; + try { + tx = graphDatabaseContext.beginTx(); + updateStartTime(); + tx.success(); + } catch (Exception e) { + AssertionError error = new AssertionError("transactionManager not correctly configured, please refer to the manual, setup section"); + error.initCause(e); + throw error; + } finally { + try { + if (tx != null) tx.finish(); + } catch(Exception e) { + // ignore + } + } + } + + private void updateStartTime() { + graphDatabaseContext.getReferenceNode().setProperty("startTime", System.currentTimeMillis()); + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/DataGraphBeanDefinitionParser.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/DataGraphBeanDefinitionParser.java index eebb1bbf2..614ba28c5 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/DataGraphBeanDefinitionParser.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/DataGraphBeanDefinitionParser.java @@ -1,10 +1,14 @@ package org.springframework.data.graph.neo4j.config; import org.neo4j.kernel.EmbeddedGraphDatabase; +import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +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.w3c.dom.Element; import static org.springframework.util.StringUtils.hasText; @@ -15,12 +19,29 @@ public class DataGraphBeanDefinitionParser extends AbstractBeanDefinitionParser @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext context) { - BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder.rootBeanDefinition(Neo4jConfiguration.class); + BeanDefinitionBuilder configBuilder = createConfigurationBeanDefinition(); setupGraphDatabase(element, context, configBuilder); setupEntityManagerFactory(element, configBuilder); + setupConfigurationClassPostProcessor(context); return getSourcedBeanDefinition(configBuilder, element, context); } + private BeanDefinitionBuilder createConfigurationBeanDefinition() { + BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder.rootBeanDefinition(Neo4jConfiguration.class); + configBuilder.setAutowireMode(Autowire.BY_TYPE.value()); + return configBuilder; + } + + private void setupConfigurationClassPostProcessor(final ParserContext parserContext) { + BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry(); + + BeanDefinitionBuilder configurationClassPostProcessor = BeanDefinitionBuilder.rootBeanDefinition(ConfigurationClassPostProcessor.class); + BeanNameGenerator beanNameGenerator = parserContext.getReaderContext().getReader().getBeanNameGenerator(); + AbstractBeanDefinition configurationClassPostProcessorBeanDefinition = configurationClassPostProcessor.getBeanDefinition(); + String beanName = beanNameGenerator.generateBeanName(configurationClassPostProcessorBeanDefinition, beanDefinitionRegistry); + beanDefinitionRegistry.registerBeanDefinition(beanName, configurationClassPostProcessorBeanDefinition); + } + @Override protected boolean shouldGenerateId() { return true; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/Neo4jConfiguration.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/Neo4jConfiguration.java index 331903284..0a7e33cb4 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/Neo4jConfiguration.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/config/Neo4jConfiguration.java @@ -17,32 +17,32 @@ package org.springframework.data.graph.neo4j.config; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; 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.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.fieldaccess.Neo4jConversionServiceFactoryBean; import org.springframework.data.graph.neo4j.fieldaccess.NodeDelegatingFieldAccessorFactory; import org.springframework.data.graph.neo4j.fieldaccess.NodeEntityStateFactory; import org.springframework.data.graph.neo4j.fieldaccess.RelationshipEntityStateFactory; import org.springframework.data.graph.neo4j.finder.FinderFactory; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; -import org.springframework.data.graph.neo4j.support.SubReferenceNodeTypeStrategy; +import org.springframework.data.graph.neo4j.support.NodeTypeStrategyFactoryBean; import org.springframework.data.graph.neo4j.support.node.Neo4jConstructorGraphEntityInstantiator; import org.springframework.data.graph.neo4j.support.node.Neo4jNodeBacking; import org.springframework.data.graph.neo4j.support.node.PartialNeo4jEntityInstantiator; import org.springframework.data.graph.neo4j.support.relationship.ConstructorBypassingGraphRelationshipInstantiator; import org.springframework.data.graph.neo4j.support.relationship.Neo4jRelationshipBacking; import org.springframework.data.graph.neo4j.transaction.ChainedTransactionManager; -import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.persistence.transaction.NaiveDoubleTransactionManager; +import org.springframework.data.persistence.EntityInstantiator; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.jta.JtaTransactionManager; -import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.validation.Validator; @@ -82,7 +82,7 @@ public class Neo4jConfiguration { } public boolean isUsingCrossStorePersistence() { - return entityManagerFactory!=null; + return entityManagerFactory != null; } @Bean @@ -90,20 +90,25 @@ public class Neo4jConfiguration { GraphDatabaseContext gdc = new GraphDatabaseContext(); gdc.setGraphDatabaseService(getGraphDatabaseService()); gdc.setRelationshipEntityInstantiator(new ConstructorBypassingGraphRelationshipInstantiator()); - if (isUsingCrossStorePersistence()) { - gdc.setGraphEntityInstantiator(new PartialNeo4jEntityInstantiator(new Neo4jConstructorGraphEntityInstantiator(), entityManagerFactory)); - } - else { - gdc.setGraphEntityInstantiator(new Neo4jConstructorGraphEntityInstantiator()); - } + EntityInstantiator graphEntityInstantiator = getGraphEntityInstantiator(); + gdc.setGraphEntityInstantiator(graphEntityInstantiator); gdc.setConversionService(new Neo4jConversionServiceFactoryBean().getObject()); - gdc.setNodeTypeStrategy(new SubReferenceNodeTypeStrategy(gdc)); + NodeTypeStrategyFactoryBean nodeTypeStrategyFactoryBean = new NodeTypeStrategyFactoryBean(graphDatabaseService, graphEntityInstantiator); + gdc.setNodeTypeStrategy(nodeTypeStrategyFactoryBean.getObject()); if (validator!=null) { gdc.setValidator(validator); } return gdc; } + private EntityInstantiator getGraphEntityInstantiator() { + if (isUsingCrossStorePersistence()) { + return new PartialNeo4jEntityInstantiator(new Neo4jConstructorGraphEntityInstantiator(), entityManagerFactory); + } else { + return new Neo4jConstructorGraphEntityInstantiator(); + } + } + @Bean public FinderFactory finderFactory(GraphDatabaseContext graphDatabaseContext) throws Exception { return new FinderFactory(graphDatabaseContext); @@ -149,4 +154,9 @@ public class Neo4jConfiguration { return transactionManager; } } + + @Bean + public ConfigurationCheck configurationCheck() throws Exception { + return new ConfigurationCheck(graphDatabaseContext(),transactionManager()); + } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java index ce2615b71..2c9e19766 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DefaultEntityState.java @@ -43,8 +43,8 @@ public abstract class DefaultEntityState, STAT this.entity = entity; this.type = type; fieldAccessorFactoryProviders = delegatingFieldAccessorFactory.accessorFactoriesFor(type); - this.fieldAccessors= fieldAccessorFactoryProviders.getFieldAccessors(); - this.fieldAccessorListeners= fieldAccessorFactoryProviders.getFieldAccessListeners(); + this.fieldAccessors = fieldAccessorFactoryProviders.getFieldAccessors(); + this.fieldAccessorListeners = fieldAccessorFactoryProviders.getFieldAccessListeners(); } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java index e2996d827..bc48e4bae 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/DelegatingFieldAccessorFactory.java @@ -119,11 +119,11 @@ public abstract class DelegatingFieldAccessorFactory implements FieldAccessor - private final Map, FieldAccessorFactoryProviders> acessorFactoryProviderCache = new HashMap, FieldAccessorFactoryProviders>(); + private final Map, FieldAccessorFactoryProviders> accessorFactoryProviderCache = new HashMap, FieldAccessorFactoryProviders>(); public FieldAccessorFactoryProviders accessorFactoriesFor(final Class type) { synchronized (this) { - final FieldAccessorFactoryProviders fieldAccessorFactoryProviders = acessorFactoryProviderCache.get(type); + final FieldAccessorFactoryProviders fieldAccessorFactoryProviders = accessorFactoryProviderCache.get(type); if (fieldAccessorFactoryProviders != null) return fieldAccessorFactoryProviders; final FieldAccessorFactoryProviders newFieldAccessorFactories = new FieldAccessorFactoryProviders(type); ReflectionUtils.doWithFields(type, new ReflectionUtils.FieldCallback() { @@ -133,7 +133,7 @@ public abstract class DelegatingFieldAccessorFactory implements FieldAccessor newFieldAccessorFactories.add(field, factory, listenerFactories); } }); - acessorFactoryProviderCache.put(type, newFieldAccessorFactories); + accessorFactoryProviderCache.put(type, newFieldAccessorFactories); return newFieldAccessorFactories; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java index 85e075b65..a13d689be 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/IndexingPropertyFieldAccessorListenerFactory.java @@ -20,7 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.index.Index; -import org.neo4j.index.impl.lucene.ValueContext; +import org.neo4j.index.lucene.ValueContext; import org.springframework.data.annotation.Indexed; import org.springframework.data.graph.annotation.NodeEntity; import org.springframework.data.graph.core.GraphBacked; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityState.java index 4f32951ac..bbd5ee7e5 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeEntityState.java @@ -21,7 +21,7 @@ import org.neo4j.graphdb.NotInTransactionException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; -import org.springframework.persistence.support.StateProvider; +import org.springframework.data.persistence.StateProvider; /** * @author Michael Hunger diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java index 825cc9ce8..6955d2f39 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/NodeRelationshipFieldAccessorFactory.java @@ -18,6 +18,7 @@ package org.springframework.data.graph.neo4j.fieldaccess; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.DynamicRelationshipType; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.graph.annotation.RelatedTo; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; @@ -58,12 +59,21 @@ abstract class NodeRelationshipFieldAccessorFactory implements FieldAccessorFact return DynamicRelationshipType.withName(relAnnotation.type()); } + protected DynamicRelationshipType typeFrom(Field field, RelatedTo relAnnotation) { + return "".equals(relAnnotation.type()) ? typeFrom(field) : typeFrom(relAnnotation); + } + protected RelatedTo getRelationshipAnnotation(Field field) { return field.getAnnotation(RelatedTo.class); } protected boolean hasValidRelationshipAnnotation(Field field) { final RelatedTo relAnnotation = getRelationshipAnnotation(field); - return relAnnotation != null && !relAnnotation.elementClass().equals(NodeBacked.class); + if (relAnnotation == null) return false; + boolean hasElementClass = !relAnnotation.elementClass().equals(NodeBacked.class); + if (!hasElementClass) throw new InvalidDataAccessApiUsageException(String.format( + "Missing mandatory attribute @RelatedTo.elementClass for one-to-N relationship field %s in class: %s", + field.getName(), field.getDeclaringClass().getName())); + return true; } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java index 1f6d6c50c..96552a528 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipEntityFieldAccessorFactory.java @@ -50,9 +50,15 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), graphDatabaseContext); } - private boolean hasValidRelationshipAnnotation(final Field f) { - final RelatedToVia relEntityAnnotation = getRelationshipAnnotation(f); - return relEntityAnnotation != null && !RelationshipBacked.class.equals(relEntityAnnotation.elementClass()); + private boolean hasValidRelationshipAnnotation(final Field field) { + final RelatedToVia relEntityAnnotation = getRelationshipAnnotation(field); + if (relEntityAnnotation == null) return false; + Class elementClass = relEntityAnnotation.elementClass(); + boolean hasElementClass = elementClass != null && !RelationshipBacked.class.equals(elementClass); + if (!hasElementClass) throw new InvalidDataAccessApiUsageException(String.format( + "Missing mandatory attribute @RelatedTo.elementClass for one-to-N relationship field %s in class: %s", + field.getName(), field.getDeclaringClass().getName())); + return hasElementClass; } private RelatedToVia getRelationshipAnnotation(final Field field) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java index 94b274c34..f085d7b19 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/OneToNRelationshipFieldAccessorFactory.java @@ -44,7 +44,7 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel @Override public FieldAccessor forField(final Field field) { final RelatedTo relAnnotation = getRelationshipAnnotation(field); - return new OneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphDatabaseContext); + return new OneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphDatabaseContext); } public static class OneToNRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityState.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityState.java index bb680595e..424f2400a 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityState.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/PartialNodeEntityState.java @@ -26,7 +26,7 @@ import org.springframework.data.graph.annotation.RelatedTo; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.finder.FinderFactory; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; -import org.springframework.persistence.support.StateProvider; +import org.springframework.data.persistence.StateProvider; import javax.persistence.Id; import java.lang.reflect.Field; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java index 70e3c1ace..8c4379108 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/ReadOnlyOneToNRelationshipFieldAccessorFactory.java @@ -39,7 +39,7 @@ public class ReadOnlyOneToNRelationshipFieldAccessorFactory extends NodeRelation @Override public FieldAccessor forField(final Field field) { final RelatedTo relAnnotation = getRelationshipAnnotation(field); - return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphDatabaseContext); + return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphDatabaseContext); } public static class ReadOnlyOneToNRelationshipFieldAccessor extends OneToNRelationshipFieldAccessorFactory.OneToNRelationshipFieldAccessor { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java index 54ae107bd..7d97b2565 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/fieldaccess/SingleRelationshipFieldAccessorFactory.java @@ -24,7 +24,6 @@ import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; import java.lang.reflect.Field; -import java.util.Collection; import java.util.Collections; import java.util.Set; @@ -46,7 +45,7 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel final RelatedTo relAnnotation = getRelationshipAnnotation(field); if (relAnnotation == null) return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field), graphDatabaseContext); - return new SingleRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphDatabaseContext); + return new SingleRelationshipFieldAccessor(typeFrom(field, relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphDatabaseContext); } public static class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/finder/AbstractFinder.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/finder/AbstractFinder.java index 924b351c7..90bd5fe9d 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/finder/AbstractFinder.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/finder/AbstractFinder.java @@ -6,7 +6,6 @@ import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexHits; import org.neo4j.helpers.collection.IterableWrapper; -import org.neo4j.index.impl.lucene.ValueContext; import org.springframework.data.graph.core.GraphBacked; import org.springframework.data.graph.neo4j.support.GraphDatabaseContext; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/server/ProvidedClassPathXmlApplicationContext.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/server/ProvidedClassPathXmlApplicationContext.java new file mode 100644 index 000000000..e44496e98 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/server/ProvidedClassPathXmlApplicationContext.java @@ -0,0 +1,29 @@ +package org.springframework.data.graph.neo4j.server; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * Context that merges the provided graph database service with the given context locations, + * so that spring beans that consume a graph database are populated properly. + */ +public class ProvidedClassPathXmlApplicationContext extends ClassPathXmlApplicationContext { + + private final GraphDatabaseService database; + + public ProvidedClassPathXmlApplicationContext(GraphDatabaseService database, final String[] locations) + throws org.springframework.beans.BeansException { + super(); + setConfigLocations(locations); + this.database = database; + refresh(); + } + + @Override + protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { + super.prepareBeanFactory(beanFactory); + beanFactory.registerResolvableDependency(GraphDatabaseService.class, database); + beanFactory.registerSingleton("graphDatabaseService", database); + } +} \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/server/SpringPluginInitializer.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/server/SpringPluginInitializer.java new file mode 100644 index 000000000..c67195855 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/server/SpringPluginInitializer.java @@ -0,0 +1,84 @@ +package org.springframework.data.graph.neo4j.server; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.beanutils.BeanFactory; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.server.plugins.PluginLifecycle; +import org.neo4j.server.plugins.Injectable; +import org.springframework.context.ApplicationContext; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Initializer to run Spring Data Graph based Server Plugins in a Neo4j REST-server. It takes the list of + * config locations and a number of spring beans from those contexts that should be exposed + * as injectable dependencies with a Jersey @Context.
+ * For example:
+ *
+ * class MyInitializer extends SpringPluginInitializer {
+ *     public MyInitializer() {
+ *         super(new String[]{"myContext.xml"},"finderFactory","myRepository");
+ *     }
+ * }
+ * 
+ */ +public abstract class SpringPluginInitializer implements PluginLifecycle { + private String[] contextLocations; + private String[] exposedBeans; + protected ProvidedClassPathXmlApplicationContext ctx; + + public SpringPluginInitializer(String[] contextLocations, String... exposedBeans) { + this.contextLocations = contextLocations; + this.exposedBeans = exposedBeans; + } + + /** + * Binds the provided graph database to the spring contexts so that spring beans that consume a + * graph database can be populated.
+ * @param graphDatabaseService of the Neo4j server + * @param config of the Neo4j Server + * @return Exposes the requested Spring beans as @{see Injectable}s + */ + @Override + public Collection> start(GraphDatabaseService graphDatabaseService, Configuration config) { + ctx = new ProvidedClassPathXmlApplicationContext(graphDatabaseService, contextLocations); + Collection> result = new ArrayList>(exposedBeans.length); + for (final String exposedBean : exposedBeans) { + result.add(new SpringBeanInjectable(SpringPluginInitializer.this.ctx, exposedBean)); + } + return result; + } + + /** + * closes the spring context + */ + public void stop() { + if (ctx!=null) { + ctx.close(); + } + } + + /** + * provides access to the Spring bean, proxying the @{see Injectable} + * @param optional type of the bean + */ + private static class SpringBeanInjectable implements Injectable { + private final String exposedBean; + protected ApplicationContext ctx; + + public SpringBeanInjectable(final ApplicationContext ctx, String exposedBean) { + this.exposedBean = exposedBean; + this.ctx = ctx; + } + + public T getValue() { + return (T) ctx.getBean(exposedBean); + + } + + public Class getType() { + return (Class) ctx.getType(exposedBean); + } + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/GraphDatabaseContext.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/GraphDatabaseContext.java index adfc42d67..ac24b6925 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/GraphDatabaseContext.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/GraphDatabaseContext.java @@ -29,7 +29,7 @@ import org.springframework.data.graph.core.GraphBacked; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.core.NodeTypeStrategy; import org.springframework.data.graph.core.RelationshipBacked; -import org.springframework.persistence.support.EntityInstantiator; +import org.springframework.data.persistence.EntityInstantiator; import javax.transaction.Status; import javax.transaction.SystemException; @@ -269,28 +269,6 @@ public class GraphDatabaseContext { } - /** - * @param relType - * @return - */ - public Node getOrCreateSubReferenceNode(final RelationshipType relType) { - return getOrCreateSingleOtherNode(graphDatabaseService.getReferenceNode(), relType, Direction.OUTGOING); - } - - private Node getOrCreateSingleOtherNode(Node fromNode, RelationshipType type, - Direction direction) { - Relationship singleRelationship = fromNode.getSingleRelationship(type, direction); - if (singleRelationship != null) { - return singleRelationship.getOtherNode(fromNode); - } - - Node otherNode = graphDatabaseService.createNode(); - fromNode.createRelationshipTo(otherNode, type); - return otherNode; - - } - - /** * @return Neo4j Transaction manager */ diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategy.java index 671d30bce..2978c0203 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategy.java @@ -11,11 +11,14 @@ import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.data.graph.annotation.NodeEntity; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.core.NodeTypeStrategy; -import org.springframework.persistence.support.EntityInstantiator; +import org.springframework.data.persistence.EntityInstantiator; public class IndexingNodeTypeStrategy implements NodeTypeStrategy { - private EntityInstantiator graphEntityInstantiator; + public static final String NODE_INDEX_NAME = "__types__"; + public static final String TYPE_PROPERTY_NAME = "__type__"; + public static final String INDEX_KEY = "className"; + private EntityInstantiator graphEntityInstantiator; private GraphDatabaseService graphDb; public IndexingNodeTypeStrategy(GraphDatabaseService graphDb, EntityInstantiator graphEntityInstantiator) { @@ -24,7 +27,7 @@ public class IndexingNodeTypeStrategy implements NodeTypeStrategy { } private Index getTypesIndex() { - return graphDb.index().forNodes("__types__"); + return graphDb.index().forNodes(NODE_INDEX_NAME); } @Override @@ -32,20 +35,20 @@ public class IndexingNodeTypeStrategy implements NodeTypeStrategy { Node node = entity.getPersistentState(); Class entityClass = entity.getClass(); addToTypesIndex(node, entityClass); - node.setProperty("__type__", entityClass.getName()); + node.setProperty(TYPE_PROPERTY_NAME, entityClass.getName()); } private void addToTypesIndex(Node node, Class entityClass) { Class klass = entityClass; while (klass.getAnnotation(NodeEntity.class) != null) { - getTypesIndex().add(node, "className", klass.getName()); + getTypesIndex().add(node, INDEX_KEY, klass.getName()); klass = klass.getSuperclass(); } } @Override public Iterable findAll(Class clazz) { - final IndexHits allEntitiesOfType = getTypesIndex().get("className", clazz.getName()); + final IndexHits allEntitiesOfType = getTypesIndex().get(INDEX_KEY, clazz.getName()); return new FilteringIterable(new IterableWrapper(allEntitiesOfType) { @Override @SuppressWarnings("unchecked") @@ -64,7 +67,11 @@ public class IndexingNodeTypeStrategy implements NodeTypeStrategy { @Override public long count(Class entityClass) { - return getTypesIndex().get("className", entityClass.getName()).size(); + long count = 0; + for (Node node : getTypesIndex().get(INDEX_KEY, entityClass.getName())) { + count += 1; + } + return count; } @Override @@ -72,7 +79,7 @@ public class IndexingNodeTypeStrategy implements NodeTypeStrategy { public Class getJavaType(Node node) { if (node == null) throw new IllegalArgumentException("Node is null"); try { - return (Class) Class.forName((String) node.getProperty("__type__")); + return (Class) Class.forName((String) node.getProperty(TYPE_PROPERTY_NAME)); } catch (NotFoundException e) { return null; } catch (ClassNotFoundException e) { @@ -82,7 +89,7 @@ public class IndexingNodeTypeStrategy implements NodeTypeStrategy { @Override public void preEntityRemoval(NodeBacked entity) { - getTypesIndex().remove(entity.getPersistentState(), "className", entity.getClass().getName()); + getTypesIndex().remove(entity.getPersistentState()); } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/NodeTypeStrategyFactoryBean.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/NodeTypeStrategyFactoryBean.java new file mode 100644 index 000000000..e3d97b7b0 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/NodeTypeStrategyFactoryBean.java @@ -0,0 +1,93 @@ +package org.springframework.data.graph.neo4j.support; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.data.graph.core.NodeBacked; +import org.springframework.data.graph.core.NodeTypeStrategy; +import org.springframework.data.persistence.EntityInstantiator; + +public class NodeTypeStrategyFactoryBean implements FactoryBean { + private GraphDatabaseService graphDatabaseService; + private EntityInstantiator graphEntityInstantiator; + private Strategy strategy; + + public NodeTypeStrategyFactoryBean(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + this.graphDatabaseService = graphDatabaseService; + this.graphEntityInstantiator = graphEntityInstantiator; + strategy = chooseStrategy(); + } + + private Strategy chooseStrategy() { + if (isAlreadyIndexed()) return Strategy.Indexed; + if (isAlreadySubRef()) return Strategy.SubRef; + return Strategy.Indexed; + } + + private boolean isAlreadyIndexed() { + return graphDatabaseService.index().existsForNodes(IndexingNodeTypeStrategy.NODE_INDEX_NAME); + } + + private boolean isAlreadySubRef() { + for (Relationship rel : graphDatabaseService.getReferenceNode().getRelationships()) { + if (rel.getType().name().startsWith(SubReferenceNodeTypeStrategy.SUBREF_PREFIX)) { + return true; + } + } + return false; + } + + @Override + public NodeTypeStrategy getObject() throws Exception { + return strategy.getObject(graphDatabaseService, graphEntityInstantiator); + } + + @Override + public Class getObjectType() { + return strategy.getObjectType(); + } + + @Override + public boolean isSingleton() { + return false; + } + + private enum Strategy { + SubRef { + @Override + NodeTypeStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + return new SubReferenceNodeTypeStrategy(graphDatabaseService, graphEntityInstantiator); + } + + @Override + Class getObjectType() { + return SubReferenceNodeTypeStrategy.class; + } + }, + Indexed { + @Override + NodeTypeStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + return new IndexingNodeTypeStrategy(graphDatabaseService, graphEntityInstantiator); + } + + @Override + Class getObjectType() { + return IndexingNodeTypeStrategy.class; + } + }, + Noop { + @Override + NodeTypeStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator) { + return new NoopNodeTypeStrategy(); + } + + @Override + Class getObjectType() { + return NoopNodeTypeStrategy.class; + } + }; + abstract NodeTypeStrategy getObject(GraphDatabaseService graphDatabaseService, EntityInstantiator graphEntityInstantiator); + abstract Class getObjectType(); + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategy.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategy.java index 6aa094180..9cfc3728b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategy.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategy.java @@ -25,6 +25,7 @@ import org.neo4j.helpers.collection.IterableWrapper; import org.neo4j.kernel.impl.traversal.TraversalDescriptionImpl; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.core.NodeTypeStrategy; +import org.springframework.data.persistence.EntityInstantiator; import java.util.*; @@ -47,11 +48,13 @@ public class SubReferenceNodeTypeStrategy implements NodeTypeStrategy { public static final String SUBREF_PREFIX = "SUBREF_"; public static final String SUBREF_CLASS_KEY = "class"; - private final GraphDatabaseContext graphDatabaseContext; + private GraphDatabaseService graphDatabaseService; + private EntityInstantiator entityInstantiator; - public SubReferenceNodeTypeStrategy(final GraphDatabaseContext graphDatabaseContext) { - this.graphDatabaseContext = graphDatabaseContext; - } + public SubReferenceNodeTypeStrategy(GraphDatabaseService graphDatabaseService, EntityInstantiator entityInstantiator) { + this.graphDatabaseService = graphDatabaseService; + this.entityInstantiator = entityInstantiator; + } public static Node getSingleOtherNode(Node node, RelationshipType type, Direction direction) { @@ -184,7 +187,7 @@ public class SubReferenceNodeTypeStrategy implements NodeTypeStrategy { @Override protected T underlyingObjectToObject(final Relationship rel) { final Node node = rel.getStartNode(); - T entity = (T) graphDatabaseContext.createEntityFromState(node, getJavaType(node)); + T entity = (T) entityInstantiator.createEntityFromState(node, getJavaType(node)); if (log.isDebugEnabled()) log.debug("Converting node: " + node + " to entity: " + entity); return entity; } @@ -195,15 +198,33 @@ public class SubReferenceNodeTypeStrategy implements NodeTypeStrategy { public Node obtainSubreferenceNode(final Class entityClass) { - return graphDatabaseContext.getOrCreateSubReferenceNode(subRefRelationshipType(entityClass)); + return getOrCreateSubReferenceNode(subRefRelationshipType(entityClass)); } public Node findSubreferenceNode(final Class entityClass) { - final Relationship subrefRelationship = graphDatabaseContext.getReferenceNode().getSingleRelationship(subRefRelationshipType(entityClass), Direction.OUTGOING); + final Relationship subrefRelationship = graphDatabaseService.getReferenceNode().getSingleRelationship(subRefRelationshipType(entityClass), Direction.OUTGOING); return subrefRelationship != null ? subrefRelationship.getEndNode() : null; } private DynamicRelationshipType subRefRelationshipType(Class clazz) { return DynamicRelationshipType.withName(SUBREF_PREFIX + clazz.getName()); } + + public Node getOrCreateSubReferenceNode(final RelationshipType relType) { + return getOrCreateSingleOtherNode(graphDatabaseService.getReferenceNode(), relType, Direction.OUTGOING); + } + + private Node getOrCreateSingleOtherNode(Node fromNode, RelationshipType type, + Direction direction) { + Relationship singleRelationship = fromNode.getSingleRelationship(type, direction); + if (singleRelationship != null) { + return singleRelationship.getOtherNode(fromNode); + } + + Node otherNode = graphDatabaseService.createNode(); + fromNode.createRelationshipTo(otherNode, type); + return otherNode; + + } + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jConstructorGraphEntityInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jConstructorGraphEntityInstantiator.java index ba3e4f421..8212d35a8 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jConstructorGraphEntityInstantiator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/Neo4jConstructorGraphEntityInstantiator.java @@ -18,7 +18,7 @@ package org.springframework.data.graph.neo4j.support.node; import org.neo4j.graphdb.Node; import org.springframework.data.graph.core.NodeBacked; -import org.springframework.persistence.support.AbstractConstructorEntityInstantiator; +import org.springframework.data.persistence.AbstractConstructorEntityInstantiator; /** * Implementation of an entity instantiator for neo4j graphdb nodes, binding the entity type to a NodeBacked and the diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNeo4jEntityInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNeo4jEntityInstantiator.java index 3a511de90..59f2409ab 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNeo4jEntityInstantiator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/node/PartialNeo4jEntityInstantiator.java @@ -20,7 +20,7 @@ import org.neo4j.graphdb.Node; import org.springframework.data.graph.core.NodeBacked; import org.springframework.data.graph.neo4j.fieldaccess.PartialNodeEntityState; import org.springframework.orm.jpa.EntityManagerFactoryUtils; -import org.springframework.persistence.support.EntityInstantiator; +import org.springframework.data.persistence.EntityInstantiator; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/relationship/ConstructorBypassingGraphRelationshipInstantiator.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/relationship/ConstructorBypassingGraphRelationshipInstantiator.java index 5ef171ee8..e471d4e7b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/relationship/ConstructorBypassingGraphRelationshipInstantiator.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/relationship/ConstructorBypassingGraphRelationshipInstantiator.java @@ -20,7 +20,7 @@ import java.lang.reflect.Constructor; import org.neo4j.graphdb.Relationship; import org.springframework.data.graph.core.RelationshipBacked; -import org.springframework.persistence.support.EntityInstantiator; +import org.springframework.data.persistence.EntityInstantiator; import sun.reflect.ReflectionFactory; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java index a5511d84b..db7ecadf3 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Group.java @@ -26,7 +26,7 @@ public class Group { public final static String OTHER_NAME_INDEX="other_name"; public static final String SEARCH_GROUPS_INDEX = "search-groups"; - @RelatedTo(type = "persons", direction = Direction.OUTGOING, elementClass = Person.class) + @RelatedTo(direction = Direction.OUTGOING, elementClass = Person.class) private Collection persons; @RelatedTo(type = "persons", elementClass = Person.class) diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidOneToNEntity.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidOneToNEntity.java new file mode 100644 index 000000000..5585d7181 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidOneToNEntity.java @@ -0,0 +1,12 @@ +package org.springframework.data.graph.neo4j; + +import org.springframework.data.graph.annotation.NodeEntity; +import org.springframework.data.graph.annotation.RelatedTo; + +import java.util.Collection; + +@NodeEntity +public class InvalidOneToNEntity { + @RelatedTo + private Collection others; +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidReadOnlyOneToNEntity.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidReadOnlyOneToNEntity.java new file mode 100644 index 000000000..e1b2993dc --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/InvalidReadOnlyOneToNEntity.java @@ -0,0 +1,10 @@ +package org.springframework.data.graph.neo4j; + +import org.springframework.data.graph.annotation.NodeEntity; +import org.springframework.data.graph.annotation.RelatedTo; + +@NodeEntity +public class InvalidReadOnlyOneToNEntity { + @RelatedTo + private Iterable others; +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java index b250a2ae2..c7c33ff62 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/Person.java @@ -44,7 +44,7 @@ public class Person { private Car car; - @RelatedTo(type = "mother", direction = Direction.OUTGOING) + @RelatedTo private Person mother; @RelatedTo(type = "boss", direction = Direction.INCOMING) diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/config/ConfigurationConfirmationTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/config/ConfigurationConfirmationTest.java new file mode 100644 index 000000000..99bc799f5 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/config/ConfigurationConfirmationTest.java @@ -0,0 +1,16 @@ +package org.springframework.data.graph.neo4j.config; + +import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @author mh + * @since 23.03.11 + */ +public class ConfigurationConfirmationTest { + @Test(expected = BeanCreationException.class) + public void testInvalidTransactionManagerFails() { + ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext("classpath:org/springframework/data/graph/neo4j/config/ConfigurationCofirmationTest-context.xml"); + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategyTest.java index 0353215e3..29b7764e4 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategyTest.java @@ -24,11 +24,11 @@ import java.util.HashSet; import java.util.Set; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml", "classpath:org/springframework/data/graph/neo4j/support/IndexingNodeTypeStrategyOverride-context.xml"}) -@Ignore public class IndexingNodeTypeStrategyTest { @Autowired @@ -57,45 +57,76 @@ public class IndexingNodeTypeStrategyTest { Index typesIndex = graphDatabaseService.index().forNodes("__types__"); IndexHits thingHits = typesIndex.get("className", thing.getClass().getName()); assertEquals(set(node(thing), node(subThing)), IteratorUtil.addToCollection((Iterable)thingHits, new HashSet())); + IndexHits subThingHits = typesIndex.get("className", subThing.getClass().getName()); + assertEquals(node(subThing), subThingHits.getSingle()); assertEquals(thing.getClass().getName(), node(thing).getProperty("__type__")); assertEquals(subThing.getClass().getName(), node(subThing).getProperty("__type__")); } @Test - public void testFindAll() throws Exception { - assertEquals("Did not find all things.", - Arrays.asList(thing, subThing), IteratorUtil.addToCollection(nodeTypeStrategy.findAll(Thing.class), new ArrayList())); + public void testPreEntityRemoval() throws Exception { + manualCleanDb(); + createThings(); + Index typesIndex = graphDatabaseService.index().forNodes("__types__"); + IndexHits thingHits; + IndexHits subThingHits; + + Transaction tx = graphDatabaseService.beginTx(); + try + { + nodeTypeStrategy.preEntityRemoval(thing); + tx.success(); + } + finally + { + tx.finish(); + } + + thingHits = typesIndex.get("className", thing.getClass().getName()); + assertEquals(node(subThing), thingHits.getSingle()); + subThingHits = typesIndex.get("className", subThing.getClass().getName()); + assertEquals(node(subThing), subThingHits.getSingle()); + + tx = graphDatabaseService.beginTx(); + try + { + nodeTypeStrategy.preEntityRemoval(subThing); + tx.success(); + } + finally + { + tx.finish(); + } + + thingHits = typesIndex.get("className", thing.getClass().getName()); + assertNull(thingHits.getSingle()); + subThingHits = typesIndex.get("className", subThing.getClass().getName()); + assertNull(subThingHits.getSingle()); } @Test + @Transactional + public void testFindAll() throws Exception { + assertEquals("Did not find all things.", + Arrays.asList(thing, subThing), + IteratorUtil.addToCollection(nodeTypeStrategy.findAll(Thing.class), new ArrayList())); + } + + @Test + @Transactional public void testCount() throws Exception { assertEquals(2, nodeTypeStrategy.count(Thing.class)); } @Test + @Transactional public void testGetJavaType() throws Exception { assertEquals(Thing.class, nodeTypeStrategy.getJavaType(node(thing))); assertEquals(SubThing.class, nodeTypeStrategy.getJavaType(node(subThing))); } @Test - public void testPreEntityRemoval() throws Exception { - manualCleanDb(); - Transaction tx; - tx = graphDatabaseService.beginTx(); - try { - nodeTypeStrategy.preEntityRemoval(thing); - nodeTypeStrategy.preEntityRemoval(subThing); - tx.success(); - } finally { - tx.finish(); - } - Index typesIndex = graphDatabaseService.index().forNodes("__types__"); - IndexHits thingHits = typesIndex.get("className", thing.getClass().getName()); - assertEquals(0, thingHits.size()); - } - - @Test + @Transactional public void testConfirmType() throws Exception { assertEquals(Thing.class, nodeTypeStrategy.confirmType(node(thing), Thing.class)); assertEquals(SubThing.class, nodeTypeStrategy.confirmType(node(subThing), Thing.class)); diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java index e1b022667..2f77f4bb4 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/NodeEntityRelationshipTest.java @@ -61,7 +61,7 @@ public class NodeEntityRelationshipTest { Person p = persistedPerson("Michael", 35); Person mother = persistedPerson("Gabi", 60); p.setMother(mother); - Node motherNode = p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("mother"), Direction.OUTGOING).getEndNode(); + Node motherNode = p.getPersistentState().getSingleRelationship(DynamicRelationshipType.withName("Person.mother"), Direction.OUTGOING).getEndNode(); assertEquals(mother.getPersistentState(), motherNode); assertEquals(mother, p.getMother()); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java index 6bad30933..917a732cc 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/PropertyTest.java @@ -2,19 +2,14 @@ package org.springframework.data.graph.neo4j.support; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.neo4j.graphdb.NotFoundException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.graph.neo4j.Friendship; -import org.springframework.data.graph.neo4j.Person; -import static org.springframework.data.graph.neo4j.Person.persistedPerson; -import org.springframework.data.graph.neo4j.Personality; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.graph.neo4j.*; import org.springframework.data.graph.neo4j.finder.FinderFactory; import org.springframework.data.graph.neo4j.support.node.Neo4jHelper; - import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -24,6 +19,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Date; import static org.junit.Assert.assertEquals; +import static org.springframework.data.graph.neo4j.Person.persistedPerson; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"}) @@ -131,4 +127,16 @@ public class PropertyTest { Friendship f = p.knows(p2); assertEquals("Wrong ID.", (Long)f.getPersistentState().getId(), f.getRelationshipId()); } + + @Test(expected = InvalidDataAccessApiUsageException.class) + @Transactional + public void testFailFastOnMisconfiguredOneToNProperty() { + new InvalidOneToNEntity(); + } + + @Test(expected = InvalidDataAccessApiUsageException.class) + @Transactional + public void testFailFastOnMisconfiguredReadOnlyOneToNProperty() { + new InvalidReadOnlyOneToNEntity(); + } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyTest.java index ad40b5fb0..f9f51585d 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyTest.java @@ -39,7 +39,8 @@ import static org.junit.Assert.assertEquals; * @since 20.01.11 */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml"}) +@ContextConfiguration(locations = {"classpath:org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml", + "classpath:org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyOverride-context.xml"}) public class SubReferenceNodeTypeStrategyTest { protected final Log log = LogFactory.getLog(getClass()); @@ -48,8 +49,8 @@ public class SubReferenceNodeTypeStrategyTest { GraphDatabaseContext graphDatabaseContext; @Autowired private FinderFactory finderFactory; - - private NodeTypeStrategy nodeTypeStrategy; + @Autowired + private SubReferenceNodeTypeStrategy nodeTypeStrategy; private Node thingNode; private Thing thing; @@ -61,7 +62,6 @@ public class SubReferenceNodeTypeStrategyTest { @Before public void setUp() { - nodeTypeStrategy = graphDatabaseContext.getNodeTypeStrategy(); thingNode = createThing(); } diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/config/ConfigurationCofirmationTest-context.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/config/ConfigurationCofirmationTest-context.xml new file mode 100644 index 000000000..149f0f9c0 --- /dev/null +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/config/ConfigurationCofirmationTest-context.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/partial/Neo4jGraphRecommendationTest-context.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/partial/Neo4jGraphRecommendationTest-context.xml index 3f0482541..2a444b96d 100644 --- a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/partial/Neo4jGraphRecommendationTest-context.xml +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/partial/Neo4jGraphRecommendationTest-context.xml @@ -86,28 +86,30 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml index 1e2ea1075..83e78ba9e 100644 --- a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/Neo4jGraphPersistenceTest-context.xml @@ -100,8 +100,9 @@ - - + + + diff --git a/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyOverride-context.xml b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyOverride-context.xml new file mode 100644 index 000000000..e2d8e83f7 --- /dev/null +++ b/spring-data-neo4j/src/test/resources/org/springframework/data/graph/neo4j/support/SubReferenceNodeTypeStrategyOverride-context.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/src/docbkx/reference/programming-model/annotations.xml b/src/docbkx/reference/programming-model/annotations.xml index 4f42f682c..7b1985e77 100644 --- a/src/docbkx/reference/programming-model/annotations.xml +++ b/src/docbkx/reference/programming-model/annotations.xml @@ -7,8 +7,9 @@ annotation.
- Entities with @NodeEntity - The @NodeEntity annotation is used to declare a POJO entity to be backed by a node in the + @NodeEntity: The basic building block + + The @NodeEntity annotation is used to declare a POJO entity to be backed by a node in the graph store. Simple fields on the entity are mapped by default to properties of the node. Object references to other NodeEntities (whether single or Collection) are mapped via relationships. If the annotation parameter useShortNames is set to false, the properties and relationship @@ -26,47 +27,20 @@ public class Movie { } ]]>
+
- RelationshipEntities with @RelationshipEntity - To access the rich data model of graph relationships, POJOs can also be annotated with - @RelationshipEntity. Relationship entities can't be instantiated directly but are rather accessed via - node entities, either by @RelatedToVia fields or by the relateTo or - getRelationshipTo methods. - Relationship entities may contain fields that are mapped to properties and two special fields that are - annotated with @StartNode and @EndNode which point to the start and end node entities respectively. These - fields are treated as read only fields. - - -
-
- Fields with @GraphProperty - It is not necessary to annotate fields as they are persisted by default; all fields that contain primitive - values are persisted directly to the graph. All fields - convertible to String using the Spring conversion services will be stored as a string. Transient fields are - not persisted. - This annotation is mainly used for cross-store persistence. - -
-
- Fields with @RelatedTo pointing to other NodeEntities + @RelatedTo: Connecting NodeEntities Relationships to other NodeEntities are mapped to graph relationships. Those can either be single - relationships (1:1) or multiple relationships (1:n). In most cases single relationships to other + relationships (1:1) or multiple relationships (1:N). In most cases single relationships to other node entities don't have to be annotated as Spring Data Graph can extract all necessary information from the field using reflection. In the case of multiple relationships, the elementClass parameter of @RelatedTo must be specified because of type erasure. The direction (default OUTGOING) and type (inferred from field name) parameters of the annotation are optional. - Relationships to single node entities are created when setting the field and deleted when setting it to + + Relationships to single node entities are created when setting the field and deleted when setting it to null. For multi-relationships the field provides a managed collection (Set) that handles addition and removal of node entities and reflects those in the graph relationships. @@ -87,9 +61,33 @@ public class Actor { } ]]>
+
- Fields with @RelatedToVia pointing to RelationshipEntities - To provide easy programmatic access to the richer relationship entities of the data model a different + @RelationshipEntity: Rich relationships + + To access the full data model of graph relationships, POJOs can also be annotated with + @RelationshipEntity. Relationship entities can't be instantiated directly but are rather accessed via + node entities, either by @RelatedToVia fields or by the relateTo or + getRelationshipTo methods. + Relationship entities may contain fields that are mapped to properties and two special fields that are + annotated with @StartNode and @EndNode which point to the start and end node entities respectively. These + fields are treated as read only fields. + + +
+ +
+ @RelatedToVia: Connecting NodeEntitites via RelationshipEntities + + To provide easy programmatic access to the richer relationship entities of the data model a different annotation @RelatedToVia can be declared on fields of Iterables of the relationship entity type. These Iterables then provide read only access to instances of the entity that backs the relationship of this relationship type. Those instances are initialized with the properties of the relationship and the start @@ -104,15 +102,16 @@ public class Actor { ]]>
- @StartNode + @StartNode: Starting NodeEntity of RelationshipEntity Annotation for the start node of a relationship entity, read only.
- @EndNode + @EndNode: Ending NodeEntity of RelationshipEntity Annotation for the end node of a relationship entity, read only.
+
- @Indexed + @Indexed: Making entities searchable by field value The @Indexed annotation can be declared on fields that are intended to be indexed by the Neo4j IndexManager, triggered by value modification. The resulting index can be used to later retrieve nodes or relationships that contain a certain property @@ -128,6 +127,7 @@ public class Actor { it defaults to the one configured with Neo4j ("node" and "relationship").
+
@GraphTraversal The @GraphTraversal annotation leverages the delegation infrastructure used by the Spring Data Graph @@ -138,4 +138,15 @@ public class Actor { elementClass attribute.
+ +
+ @GraphProperty: Cross-store persisted fields + It is not necessary to annotate fields as they are persisted by default; all fields that contain primitive + values are persisted directly to the graph. All fields + convertible to String using the Spring conversion services will be stored as a string. Transient fields are + not persisted. + This annotation is mainly used for cross-store persistence. + +
+ \ No newline at end of file diff --git a/src/docbkx/reference/programming-model/nodetypestrategy.xml b/src/docbkx/reference/programming-model/nodetypestrategy.xml index fb93dd6d6..796f5d763 100644 --- a/src/docbkx/reference/programming-model/nodetypestrategy.xml +++ b/src/docbkx/reference/programming-model/nodetypestrategy.xml @@ -4,20 +4,45 @@ Reified types for entities There are several ways to represent the Java type hierarchy of the data model in the graph. In general for all - node and relationship entities type information is needed to perform certain repository operations. That's - why the hierarchy up to java.lang.Object of all these classes will be persisted in the graph. - Implementations of NodeTypeStrategy take care of persisting this information on entity instance + node and relationship entities type information is needed to perform certain repository operations. Some of + this type information is saved in the graph database. + + + Implementations of NodeTypeStrategy take care of persisting this information on entity instance creation. They also provide the repository methods that use this type information to perform their operations - like findAll, count etc. + like findAll, count, etc. - The current implementation uses nodes to represent the Java type hierarchy which are connected via SUBCLASS_OF - relationships to their superclass nodes and via INSTANCE_OF relationships to the concrete node entity - instance node. + There are three available implementations to choose from. + + + IndexingNodeTypeStrategy + + Stores entity types in the integrated index. Each entity node gets indexed with its type and + any supertypes that are also @NodeEntity-annotated. The special index used for this + is called __types__. Additionally, in order to get the type of an entity node, each + node has a property __type__ with the type of that entity. + + + + SubReferenceNodeTypeStrategy + + Stores entity types in a tree in the graph representing the type hierarchy. Each entity + has a INSTANCE_OF relationship to a type node representing that entity's type. The type may or + may not have a SUBCLASS_OF relationship to another type node. + + + + NoopNodeTypeStrategy + + Does not store any type information, and does hence not support finding by type, counting by type, + or retrieving the type of any entity. + + + - An alternative approach could use indexing operations to perform the same functionality. Or one could skip the - NodeTypeStrategy altogether if no strict checks on type conformity are needed, which would allow for a much - more flexible data model. + The default implementation is IndexingNodeTypeStrategy for new graphs. If using an existing + graph, Spring Data Graph will default to the strategy first used when the graph was created. \ No newline at end of file diff --git a/src/docbkx/tutorial/annotations.xml b/src/docbkx/tutorial/annotations.xml index 3967d3520..59908fe3d 100644 --- a/src/docbkx/tutorial/annotations.xml +++ b/src/docbkx/tutorial/annotations.xml @@ -9,7 +9,7 @@ It's time to put this to a test. How can we be assured that a field is persisted to the graph store? There seemed to be two possibilities. First was to get a GraphDatabaseContext injected and use its getById() method. The other one was a Finder approach. But let's try to keep things simple. - How can we persist an entity and how to get its id? No idea, so time to hit the documentation again, revealing that there are a bunch of methods introduced to the + How can we persist an entity and how to get its id? Looking at the documentation revealed that there are a bunch of methods introduced to the entities by the aspects. That's not obvious, but we found the two that would help here - entity.persist() and entity.getNodeId(). So our test looked like this. diff --git a/src/docbkx/tutorial/domain.xml b/src/docbkx/tutorial/domain.xml index f875b49dc..c53eb01d6 100644 --- a/src/docbkx/tutorial/domain.xml +++ b/src/docbkx/tutorial/domain.xml @@ -3,7 +3,7 @@ Setting the Stage - Movies Domain - The domain model was the next thing we planned to work on. We wanted to sketch it out first before diving into library details. We also looked at the datamodel of core themoviedb data to + The domain model was the next thing we planned to work on. We wanted to sketch it out first before diving into library details. We also looked at the datamodel of core themoviedb data to confirm that it matched our expectations. @@ -84,8 +83,8 @@ With this setup we were ready for the first spike: creating a simple MovieController showing a static view. Check. Next was the setup for Spring Data Graph. - We looked at the README at github and then checked it with the manual. Quite a lot of maven setup for aspectj but otherwise not so much to add. - Time to add a few lines to our spring configuration. + We looked at the README at github and then checked it with the manual. Quite a lot of Maven setup for AspectJ but otherwise not so much to add. + Time to add a few lines to our Spring configuration. @@ -129,7 +128,7 @@ - We spun up jetty to see if there were any obvious issues with the config. Check. + We spun up Jetty to see if there were any obvious issues with the config. It all seemed to work just fine. Check. diff --git a/src/docbkx/tutorial/spring-data-graph.xml b/src/docbkx/tutorial/spring-data-graph.xml index 77c61f4cf..15a61e809 100644 --- a/src/docbkx/tutorial/spring-data-graph.xml +++ b/src/docbkx/tutorial/spring-data-graph.xml @@ -1,16 +1,17 @@ - +x Conjuring Magic - Spring Data Graph - But that was the pure graph database. Using this in our domain would pollute my classes with lots of graph + That was the pure graph database. Using this in our domain would pollute our classes with lots of graph database details. We don't want that. Spring Data Graph - promised to do the heavy lifting for us. So we checked that next. Obviously it heavily depended on aspectj magic. - So there would be certain behavior that was - just observable without being visible in our code, but we were going to give it a try. + promised to do the heavy lifting for us. So we checked that next. + + Spring Data Graph depends heavily on AspectJ magic. Some parts of our classes would behave differently, + but it would not be visible in our code. We were going to give it a try. - First step was lots of maven configuration. + First step was lots of Maven configuration. @@ -71,7 +72,7 @@ ]]> - The spring configuration was much easier, thanks to a provided namespace. + The Spring configuration was much easier, thanks to a provided namespace.