From 5b6cb19c53594e64659902eb8a09e2b7954d84df Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Thu, 23 Feb 2023 19:57:13 +0100 Subject: [PATCH] GH-2684 - Make Neo4j templates safer to use outside an application context. While still not recommended to use outside an application context, this allows basic usage. Closes #2684. --- .../data/neo4j/core/Neo4jTemplate.java | 31 ++++++++++++------- .../neo4j/core/ReactiveNeo4jTemplate.java | 28 +++++++++++------ 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java index a6eaaa743..4fc728fc7 100644 --- a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java @@ -124,7 +124,7 @@ public final class Neo4jTemplate implements private EventSupport eventSupport; - private ProjectionFactory projectionFactory; + private ProjectionFactory projectionFactoryf; private Renderer renderer; @@ -146,6 +146,11 @@ public final class Neo4jTemplate implements this.neo4jMappingContext = neo4jMappingContext; this.cypherGenerator = CypherGenerator.INSTANCE; this.eventSupport = EventSupport.useExistingCallbacks(neo4jMappingContext, entityCallbacks); + this.renderer = Renderer.getDefaultRenderer(); + } + + ProjectionFactory getProjectionFactory() { + return Objects.requireNonNull(this.projectionFactoryf, "Projection support for the Neo4j template is only available when the template is a proper and fully initialized Spring bean."); } @Override @@ -256,7 +261,7 @@ public final class Neo4jTemplate implements if (resultType.isInterface()) { return intermediaResults.stream() - .map(instance -> projectionFactory.createProjection(resultType, instance)) + .map(instance -> getProjectionFactory().createProjection(resultType, instance)) .collect(Collectors.toList()); } @@ -342,9 +347,10 @@ public final class Neo4jTemplate implements return resultType.cast(save(instance)); } - ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(resultType); + ProjectionFactory localProjectionFactory = getProjectionFactory(); + ProjectionInformation projectionInformation = localProjectionFactory.getProjectionInformation(resultType); Collection pps = PropertyFilterSupport.addPropertiesFrom(instance.getClass(), resultType, - projectionFactory, neo4jMappingContext); + localProjectionFactory, neo4jMappingContext); T savedInstance = saveImpl(instance, pps, null); if (!resultType.isInterface()) { @@ -352,13 +358,13 @@ public final class Neo4jTemplate implements return result; } if (projectionInformation.isClosed()) { - return projectionFactory.createProjection(resultType, savedInstance); + return localProjectionFactory.createProjection(resultType, savedInstance); } Neo4jPersistentEntity entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(savedInstance.getClass()); Neo4jPersistentProperty idProperty = entityMetaData.getIdProperty(); PersistentPropertyAccessor propertyAccessor = entityMetaData.getPropertyAccessor(savedInstance); - return projectionFactory.createProjection(resultType, + return localProjectionFactory.createProjection(resultType, this.findById(propertyAccessor.getProperty(idProperty), savedInstance.getClass()).get()); } @@ -542,15 +548,16 @@ public final class Neo4jTemplate implements return saveElements; } - ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(resultType); + ProjectionFactory localProjectionFactory = getProjectionFactory(); + ProjectionInformation projectionInformation = localProjectionFactory.getProjectionInformation(resultType); Collection pps = PropertyFilterSupport.addPropertiesFrom(commonElementType, resultType, - projectionFactory, neo4jMappingContext); + localProjectionFactory, neo4jMappingContext); List savedInstances = saveAllImpl(instances, pps, null); if (projectionInformation.isClosed()) { - return savedInstances.stream().map(instance -> projectionFactory.createProjection(resultType, instance)) + return savedInstances.stream().map(instance -> localProjectionFactory.createProjection(resultType, instance)) .collect(Collectors.toList()); } @@ -563,7 +570,7 @@ public final class Neo4jTemplate implements }).collect(Collectors.toList()); return findAllById(ids, commonElementType) - .stream().map(instance -> projectionFactory.createProjection(resultType, instance)) + .stream().map(instance -> localProjectionFactory.createProjection(resultType, instance)) .collect(Collectors.toList()); } @@ -1000,7 +1007,7 @@ public final class Neo4jTemplate implements SpelAwareProxyProjectionFactory spelAwareProxyProjectionFactory = new SpelAwareProxyProjectionFactory(); spelAwareProxyProjectionFactory.setBeanClassLoader(beanClassLoader); spelAwareProxyProjectionFactory.setBeanFactory(beanFactory); - this.projectionFactory = spelAwareProxyProjectionFactory; + this.projectionFactoryf = spelAwareProxyProjectionFactory; Configuration cypherDslConfiguration = beanFactory .getBeanProvider(Configuration.class) @@ -1054,7 +1061,7 @@ public final class Neo4jTemplate implements Class resultType = TemplateSupport.findCommonElementType(instances); Collection pps = PropertyFilterSupport.addPropertiesFrom(domainType, resultType, - projectionFactory, neo4jMappingContext); + getProjectionFactory(), neo4jMappingContext); NestedRelationshipProcessingStateMachine stateMachine = new NestedRelationshipProcessingStateMachine(neo4jMappingContext); List results = new ArrayList<>(); diff --git a/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java b/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java index 36ad8ed55..c33665f6d 100644 --- a/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; @@ -140,6 +141,11 @@ public final class ReactiveNeo4jTemplate implements this.neo4jMappingContext = neo4jMappingContext; this.cypherGenerator = CypherGenerator.INSTANCE; this.eventSupport = ReactiveEventSupport.useExistingCallbacks(neo4jMappingContext, ReactiveEntityCallbacks.create()); + this.renderer = Renderer.getDefaultRenderer(); + } + + ProjectionFactory getProjectionFactory() { + return Objects.requireNonNull(this.projectionFactory, "Projection support for the Neo4j template is only available when the template is a proper and fully initialized Spring bean."); } @Override @@ -250,7 +256,7 @@ public final class ReactiveNeo4jTemplate implements } if (resultType.isInterface()) { - return intermediaResults.map(instance -> projectionFactory.createProjection(resultType, instance)); + return intermediaResults.map(instance -> getProjectionFactory().createProjection(resultType, instance)); } DtoInstantiatingConverter converter = new DtoInstantiatingConverter(resultType, neo4jMappingContext); @@ -341,9 +347,10 @@ public final class ReactiveNeo4jTemplate implements return save(instance).map(resultType::cast); } - ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(resultType); + ProjectionFactory localProjectionFactory = getProjectionFactory(); + ProjectionInformation projectionInformation = localProjectionFactory.getProjectionInformation(resultType); Collection pps = PropertyFilterSupport.addPropertiesFrom(instance.getClass(), resultType, - projectionFactory, neo4jMappingContext); + localProjectionFactory, neo4jMappingContext); Mono savingPublisher = saveImpl(instance, pps, null); @@ -355,7 +362,7 @@ public final class ReactiveNeo4jTemplate implements }); } if (projectionInformation.isClosed()) { - return savingPublisher.map(savedInstance -> projectionFactory.createProjection(resultType, savedInstance)); + return savingPublisher.map(savedInstance -> localProjectionFactory.createProjection(resultType, savedInstance)); } return savingPublisher.flatMap(savedInstance -> { @@ -364,7 +371,7 @@ public final class ReactiveNeo4jTemplate implements Neo4jPersistentProperty idProperty = entityMetaData.getIdProperty(); PersistentPropertyAccessor propertyAccessor = entityMetaData.getPropertyAccessor(savedInstance); return this.findById(propertyAccessor.getProperty(idProperty), savedInstance.getClass()) - .map(loadedValue -> projectionFactory.createProjection(resultType, loadedValue)); + .map(loadedValue -> localProjectionFactory.createProjection(resultType, loadedValue)); }); } @@ -377,7 +384,7 @@ public final class ReactiveNeo4jTemplate implements Class resultType = TemplateSupport.findCommonElementType(instances); Collection pps = PropertyFilterSupport.addPropertiesFrom(domainType, resultType, - projectionFactory, neo4jMappingContext); + getProjectionFactory(), neo4jMappingContext); NestedRelationshipProcessingStateMachine stateMachine = new NestedRelationshipProcessingStateMachine(neo4jMappingContext); EntityFromDtoInstantiatingConverter converter = new EntityFromDtoInstantiatingConverter<>(domainType, neo4jMappingContext); @@ -499,13 +506,14 @@ public final class ReactiveNeo4jTemplate implements return saveAll(instances).map(resultType::cast); } - ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(resultType); + ProjectionFactory localProjectionFactory = getProjectionFactory(); + ProjectionInformation projectionInformation = localProjectionFactory.getProjectionInformation(resultType); Collection pps = PropertyFilterSupport.addPropertiesFrom(commonElementType, resultType, - projectionFactory, neo4jMappingContext); + localProjectionFactory, neo4jMappingContext); Flux savedInstances = saveAllImpl(instances, pps, null); if (projectionInformation.isClosed()) { - return savedInstances.map(instance -> projectionFactory.createProjection(resultType, instance)); + return savedInstances.map(instance -> localProjectionFactory.createProjection(resultType, instance)); } Neo4jPersistentEntity entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(commonElementType); @@ -514,7 +522,7 @@ public final class ReactiveNeo4jTemplate implements return savedInstances.flatMap(savedInstance -> { PersistentPropertyAccessor propertyAccessor = entityMetaData.getPropertyAccessor(savedInstance); return findById(propertyAccessor.getProperty(idProperty), commonElementType); - }).map(instance -> projectionFactory.createProjection(resultType, instance)); + }).map(instance -> localProjectionFactory.createProjection(resultType, instance)); } private Flux saveAllImpl(Iterable instances, @Nullable Collection includedProperties, @Nullable BiPredicate includeProperty) {