diff --git a/src/main/java/org/springframework/data/neo4j/config/AbstractNeo4jConfig.java b/src/main/java/org/springframework/data/neo4j/config/AbstractNeo4jConfig.java index e25089a00..fa795c4ac 100644 --- a/src/main/java/org/springframework/data/neo4j/config/AbstractNeo4jConfig.java +++ b/src/main/java/org/springframework/data/neo4j/config/AbstractNeo4jConfig.java @@ -19,7 +19,6 @@ import org.apiguardian.api.API; import org.neo4j.driver.Driver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.data.neo4j.core.DatabaseSelectionProvider; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.data.neo4j.core.Neo4jOperations; @@ -39,7 +38,6 @@ import org.springframework.transaction.PlatformTransactionManager; */ @Configuration @API(status = API.Status.STABLE, since = "6.0") -@Import(Neo4jDefaultCallbacksRegistrar.class) public abstract class AbstractNeo4jConfig extends Neo4jConfigurationSupport { /** diff --git a/src/main/java/org/springframework/data/neo4j/config/AbstractReactiveNeo4jConfig.java b/src/main/java/org/springframework/data/neo4j/config/AbstractReactiveNeo4jConfig.java index a77072fcd..726c6e94a 100644 --- a/src/main/java/org/springframework/data/neo4j/config/AbstractReactiveNeo4jConfig.java +++ b/src/main/java/org/springframework/data/neo4j/config/AbstractReactiveNeo4jConfig.java @@ -19,7 +19,6 @@ import org.apiguardian.api.API; import org.neo4j.driver.Driver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.data.neo4j.core.ReactiveDatabaseSelectionProvider; import org.springframework.data.neo4j.core.ReactiveNeo4jClient; import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate; @@ -39,7 +38,6 @@ import org.springframework.transaction.ReactiveTransactionManager; */ @Configuration @API(status = API.Status.STABLE, since = "6.0") -@Import(Neo4jDefaultReactiveCallbacksRegistrar.class) public abstract class AbstractReactiveNeo4jConfig extends Neo4jConfigurationSupport { /** diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java index 8b20b669e..0c66fc292 100644 --- a/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jAuditingRegistrar.java @@ -25,7 +25,7 @@ import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarS import org.springframework.data.auditing.config.AuditingConfiguration; import org.springframework.data.config.ParsingUtils; import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.neo4j.repository.event.AuditingBeforeBindCallback; +import org.springframework.data.neo4j.core.mapping.callback.AuditingBeforeBindCallback; import org.springframework.util.Assert; /** diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jCdiConfigurationSupport.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jCdiConfigurationSupport.java index 51fd1d1ef..8489e3271 100644 --- a/src/main/java/org/springframework/data/neo4j/config/Neo4jCdiConfigurationSupport.java +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jCdiConfigurationSupport.java @@ -23,8 +23,6 @@ import javax.inject.Singleton; import org.apiguardian.api.API; import org.neo4j.driver.Driver; -import org.springframework.data.mapping.callback.EntityCallback; -import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.neo4j.core.DatabaseSelectionProvider; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.data.neo4j.core.Neo4jOperations; @@ -32,9 +30,6 @@ import org.springframework.data.neo4j.core.Neo4jTemplate; import org.springframework.data.neo4j.core.convert.Neo4jConversions; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; -import org.springframework.data.neo4j.repository.event.BeforeBindCallback; -import org.springframework.data.neo4j.repository.event.IdGeneratingBeforeBindCallback; -import org.springframework.data.neo4j.repository.event.OptimisticLockingBeforeBindCallback; import org.springframework.transaction.PlatformTransactionManager; /** @@ -70,15 +65,11 @@ class Neo4jCdiConfigurationSupport { @Produces @Builtin @Singleton public Neo4jOperations neo4jOperations( - final @Any Instance neo4jClient, - final @Any Instance mappingContext, - final @Any Instance databaseNameProvider, - final Instance services + @Any Instance neo4jClient, + @Any Instance mappingContext, + @Any Instance databaseNameProvider ) { - - EntityCallbacks entityCallbacks = EntityCallbacks.create(services.stream().toArray(EntityCallback[]::new)); - return new Neo4jTemplate(resolve(neo4jClient), resolve(mappingContext), resolve(databaseNameProvider), - entityCallbacks); + return new Neo4jTemplate(resolve(neo4jClient), resolve(mappingContext), resolve(databaseNameProvider)); } @Produces @Singleton @@ -87,24 +78,9 @@ class Neo4jCdiConfigurationSupport { } @Produces @Singleton - public Neo4jMappingContext neo4jMappingContext(final Driver driver, final @Any Instance neo4JConversions) { + public Neo4jMappingContext neo4jMappingContext(Driver driver, @Any Instance neo4JConversions) { - Neo4jMappingContext neo4jMappingContext = new Neo4jMappingContext(resolve(neo4JConversions), driver.defaultTypeSystem()); - return neo4jMappingContext; - } - - @Produces @Singleton - public BeforeBindCallback idGeneratingBeforeBindCallback( - final @Any Instance mappingContext) { - - return new IdGeneratingBeforeBindCallback(resolve(mappingContext)); - } - - @Produces @Singleton - public BeforeBindCallback optimisticLockingBeforeBindCallback( - final @Any Instance mappingContext) { - - return new OptimisticLockingBeforeBindCallback(resolve(mappingContext)); + return new Neo4jMappingContext(resolve(neo4JConversions), driver.defaultTypeSystem()); } @Produces @Singleton diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jConfigurationSupport.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jConfigurationSupport.java index c2926c61c..013896180 100644 --- a/src/main/java/org/springframework/data/neo4j/config/Neo4jConfigurationSupport.java +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jConfigurationSupport.java @@ -17,21 +17,13 @@ package org.springframework.data.neo4j.config; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.Set; import org.apiguardian.api.API; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.data.annotation.Persistent; import org.springframework.data.neo4j.core.convert.Neo4jConversions; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.core.schema.Node; -import org.springframework.data.neo4j.core.schema.RelationshipProperties; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Internal support class for basic configuration. The support infrastructure here is basically all around finding out @@ -90,13 +82,7 @@ abstract class Neo4jConfigurationSupport { */ protected final Set> getInitialEntitySet() throws ClassNotFoundException { - Set> initialEntitySet = new HashSet>(); - - for (String basePackage : getMappingBasePackages()) { - initialEntitySet.addAll(scanForEntities(basePackage)); - } - - return initialEntitySet; + return Neo4jEntityScanner.get().scan(getMappingBasePackages()); } /** @@ -105,26 +91,11 @@ abstract class Neo4jConfigurationSupport { * @param basePackage must not be {@literal null}. * @return found entities in the package to scan. * @throws ClassNotFoundException if the given class cannot be loaded by the class loader. + * @deprecated since 6.0.2 Use {@link Neo4jEntityScanner} instead. */ + @Deprecated protected final Set> scanForEntities(String basePackage) throws ClassNotFoundException { - if (!StringUtils.hasText(basePackage)) { - return Collections.emptySet(); - } - - Set> initialEntitySet = new HashSet>(); - - ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( - false); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Node.class)); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); - componentProvider.addIncludeFilter(new AnnotationTypeFilter(RelationshipProperties.class)); - - ClassLoader classLoader = Neo4jConfigurationSupport.class.getClassLoader(); - for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) { - initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), classLoader)); - } - - return initialEntitySet; + return Neo4jEntityScanner.get().scan(basePackage); } } diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultCallbacksRegistrar.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultCallbacksRegistrar.java index f10e1c13b..915ad348e 100644 --- a/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultCallbacksRegistrar.java +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultCallbacksRegistrar.java @@ -16,15 +16,11 @@ package org.springframework.data.neo4j.config; import org.apiguardian.api.API; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.neo4j.core.Neo4jOperations; -import org.springframework.data.neo4j.repository.event.IdGeneratingBeforeBindCallback; -import org.springframework.data.neo4j.repository.event.OptimisticLockingBeforeBindCallback; /** * This brings in the default callbacks required for the default implementation of {@link Neo4jOperations} to work. The @@ -34,23 +30,15 @@ import org.springframework.data.neo4j.repository.event.OptimisticLockingBeforeBi * @author Michael J. Simons * @soundtrack AC/DC - High Voltage * @since 6.0 + * @deprecated since 6.0.2, now an empty implementation, not needed anymore and our default callbacks will be added directly via our + * infrastructure. */ -@API(status = API.Status.STABLE, since = "6.0") +@API(status = API.Status.DEPRECATED, since = "6.0") +@Deprecated public final class Neo4jDefaultCallbacksRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) { - // Id Generator - RootBeanDefinition beanDefinition = new RootBeanDefinition(IdGeneratingBeforeBindCallback.class); - beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); - registry.registerBeanDefinition(beanName, beanDefinition); - - // Optimistic locking support - beanDefinition = new RootBeanDefinition(OptimisticLockingBeforeBindCallback.class); - beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); - registry.registerBeanDefinition(beanName, beanDefinition); } } diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultReactiveCallbacksRegistrar.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultReactiveCallbacksRegistrar.java index 7854fdfad..45a919ff5 100644 --- a/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultReactiveCallbacksRegistrar.java +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jDefaultReactiveCallbacksRegistrar.java @@ -16,15 +16,11 @@ package org.springframework.data.neo4j.config; import org.apiguardian.api.API; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.neo4j.core.Neo4jOperations; -import org.springframework.data.neo4j.repository.event.ReactiveIdGeneratingBeforeBindCallback; -import org.springframework.data.neo4j.repository.event.ReactiveOptimisticLockingBeforeBindCallback; /** * This brings in the default callbacks required for the default implementation of {@link Neo4jOperations} to work. The @@ -34,23 +30,15 @@ import org.springframework.data.neo4j.repository.event.ReactiveOptimisticLocking * @author Michael J. Simons * @soundtrack AC/DC - High Voltage * @since 6.0 + * @deprecated since 6.0.2, now an empty implementation, not needed anymore and our default callbacks will be added directly via our + * infrastructure. */ -@API(status = API.Status.STABLE, since = "6.0") +@API(status = API.Status.DEPRECATED, since = "6.0") +@Deprecated public final class Neo4jDefaultReactiveCallbacksRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) { - // Id Generator - RootBeanDefinition beanDefinition = new RootBeanDefinition(ReactiveIdGeneratingBeforeBindCallback.class); - beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); - registry.registerBeanDefinition(beanName, beanDefinition); - - // Optimistic locking support - beanDefinition = new RootBeanDefinition(ReactiveOptimisticLockingBeforeBindCallback.class); - beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - beanName = beanNameGenerator.generateBeanName(beanDefinition, registry); - registry.registerBeanDefinition(beanName, beanDefinition); } } diff --git a/src/main/java/org/springframework/data/neo4j/config/Neo4jEntityScanner.java b/src/main/java/org/springframework/data/neo4j/config/Neo4jEntityScanner.java new file mode 100644 index 000000000..34fca7f5f --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/config/Neo4jEntityScanner.java @@ -0,0 +1,136 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.config; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apiguardian.api.API; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.RelationshipProperties; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * A utility class providing a way to discover an initial entity set for a {@link org.springframework.data.neo4j.core.mapping.Neo4jMappingContext}. + * + * @author Michael J. Simons + * @soundtrack Kelis - Tasty + * @since 6.0.2 + */ +@API(status = API.Status.STABLE, since = "6.0.2") +public final class Neo4jEntityScanner { + + public static Neo4jEntityScanner get() { + + return new Neo4jEntityScanner(null); + } + + public static Neo4jEntityScanner get(@Nullable ResourceLoader resourceLoader) { + + return new Neo4jEntityScanner(resourceLoader); + } + + private @Nullable final ResourceLoader resourceLoader; + + /** + * Create a new {@link Neo4jEntityScanner} instance. + * + * @param resourceLoader an optional resource loader used for class scanning. + */ + private Neo4jEntityScanner(@Nullable ResourceLoader resourceLoader) { + + this.resourceLoader = resourceLoader; + } + + /** + * Scan for entities with the specified annotations. + * + * @param basePackages the list of base packages to scan. + * @return a set of entity classes + * @throws ClassNotFoundException if an entity class cannot be loaded + */ + @SafeVarargs + public final Set> scan(String... basePackages) throws ClassNotFoundException { + return scan(Arrays.stream(basePackages).collect(Collectors.toList())); + } + + /** + * Scan for entities with the specified annotations. + * + * @param packages the list of base packages to scan. + * @return a set of entity classes + * @throws ClassNotFoundException if an entity class cannot be loaded + * @see #scan(String...) + */ + public Set> scan(Collection packages) throws ClassNotFoundException { + + if (packages.isEmpty()) { + return Collections.emptySet(); + } + + ClassPathScanningCandidateComponentProvider scanner = + createClassPathScanningCandidateComponentProvider(this.resourceLoader); + + ClassLoader classLoader = + this.resourceLoader == null ? + Neo4jConfigurationSupport.class.getClassLoader() : + this.resourceLoader.getClassLoader(); + + Set> entitySet = new HashSet<>(); + for (String basePackage : packages) { + if (StringUtils.hasText(basePackage)) { + for (BeanDefinition candidate : scanner.findCandidateComponents(basePackage)) { + entitySet.add(ClassUtils.forName(candidate.getBeanClassName(), classLoader)); + } + } + } + return entitySet; + } + + /** + * Create a {@link ClassPathScanningCandidateComponentProvider} to scan entities based + * on the specified {@link ApplicationContext}. + * + * @param resourceLoader an optional {@link ResourceLoader} to use + * @return a {@link ClassPathScanningCandidateComponentProvider} suitable to scan for Neo4j entities + */ + private static ClassPathScanningCandidateComponentProvider createClassPathScanningCandidateComponentProvider( + ResourceLoader resourceLoader) { + + ClassPathScanningCandidateComponentProvider delegate = new ClassPathScanningCandidateComponentProvider(false); + if (resourceLoader != null) { + delegate.setResourceLoader(resourceLoader); + } + + delegate.addIncludeFilter(new AnnotationTypeFilter(Node.class)); + delegate.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); + delegate.addIncludeFilter(new AnnotationTypeFilter(RelationshipProperties.class)); + + return delegate; + } +} diff --git a/src/main/java/org/springframework/data/neo4j/config/ReactiveNeo4jAuditingRegistrar.java b/src/main/java/org/springframework/data/neo4j/config/ReactiveNeo4jAuditingRegistrar.java index 840969cd8..e62c05642 100644 --- a/src/main/java/org/springframework/data/neo4j/config/ReactiveNeo4jAuditingRegistrar.java +++ b/src/main/java/org/springframework/data/neo4j/config/ReactiveNeo4jAuditingRegistrar.java @@ -25,7 +25,7 @@ import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarS import org.springframework.data.auditing.config.AuditingConfiguration; import org.springframework.data.config.ParsingUtils; import org.springframework.data.mapping.context.PersistentEntities; -import org.springframework.data.neo4j.repository.event.ReactiveAuditingBeforeBindCallback; +import org.springframework.data.neo4j.core.mapping.callback.ReactiveAuditingBeforeBindCallback; import org.springframework.util.Assert; /** 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 dce8b32fb..886249728 100644 --- a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java @@ -59,8 +59,8 @@ import org.springframework.data.neo4j.core.mapping.NestedRelationshipProcessingS import org.springframework.data.neo4j.core.mapping.NestedRelationshipProcessingStateMachine.ProcessState; import org.springframework.data.neo4j.core.mapping.NodeDescription; import org.springframework.data.neo4j.core.mapping.RelationshipDescription; +import org.springframework.data.neo4j.core.mapping.callback.EventSupport; import org.springframework.data.neo4j.repository.NoResultException; -import org.springframework.data.neo4j.repository.event.BeforeBindCallback; import org.springframework.data.util.ClassTypeInformation; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -87,7 +87,7 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware { private final CypherGenerator cypherGenerator; - private Neo4jEvents eventSupport; + private EventSupport eventSupport; private final DatabaseSelectionProvider databaseSelectionProvider; @@ -111,7 +111,7 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware { this.neo4jClient = neo4jClient; this.neo4jMappingContext = neo4jMappingContext; this.cypherGenerator = CypherGenerator.INSTANCE; - this.eventSupport = new Neo4jEvents(entityCallbacks); + this.eventSupport = EventSupport.useExistingCallbacks(neo4jMappingContext, entityCallbacks); this.databaseSelectionProvider = databaseSelectionProvider; } @@ -548,7 +548,7 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware { @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.eventSupport = new Neo4jEvents(EntityCallbacks.create(beanFactory)); + this.eventSupport = EventSupport.discoverCallbacks(neo4jMappingContext, beanFactory); } @Override @@ -596,21 +596,4 @@ public final class Neo4jTemplate implements Neo4jOperations, BeanFactoryAware { return fetchSpec.one().orElseThrow(() -> new NoResultException(1, preparedQuery.getCypherQuery())); } } - - /** - * Utility class that orchestrates {@link EntityCallbacks}. All the methods provided here check for their availability - * and do nothing when an event cannot be published. - */ - final class Neo4jEvents { - - private final EntityCallbacks entityCallbacks; - - Neo4jEvents(EntityCallbacks entityCallbacks) { - this.entityCallbacks = entityCallbacks; - } - - public T maybeCallBeforeBind(T object) { - return entityCallbacks.callback(BeforeBindCallback.class, object); - } - } } 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 923005ceb..8065e96da 100644 --- a/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jTemplate.java @@ -63,7 +63,7 @@ import org.springframework.data.neo4j.core.mapping.NestedRelationshipProcessingS import org.springframework.data.neo4j.core.mapping.NestedRelationshipProcessingStateMachine.ProcessState; import org.springframework.data.neo4j.core.mapping.NodeDescription; import org.springframework.data.neo4j.core.mapping.RelationshipDescription; -import org.springframework.data.neo4j.repository.event.ReactiveBeforeBindCallback; +import org.springframework.data.neo4j.core.mapping.callback.ReactiveEventSupport; import org.springframework.data.util.ClassTypeInformation; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -90,7 +90,7 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea private final CypherGenerator cypherGenerator; - private ReactiveNeo4jEvents eventSupport; + private ReactiveEventSupport eventSupport; private final ReactiveDatabaseSelectionProvider databaseSelectionProvider; @@ -104,7 +104,7 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea this.neo4jClient = neo4jClient; this.neo4jMappingContext = neo4jMappingContext; this.cypherGenerator = CypherGenerator.INSTANCE; - this.eventSupport = new ReactiveNeo4jEvents(ReactiveEntityCallbacks.create()); + this.eventSupport = ReactiveEventSupport.useExistingCallbacks(neo4jMappingContext, ReactiveEntityCallbacks.create()); this.databaseSelectionProvider = databaseSelectionProvider; } @@ -577,7 +577,7 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.eventSupport = new ReactiveNeo4jEvents(ReactiveEntityCallbacks.create(beanFactory)); + this.eventSupport = ReactiveEventSupport.discoverCallbacks(neo4jMappingContext, beanFactory); } final class DefaultReactiveExecutableQuery implements ExecutableQuery { @@ -618,21 +618,4 @@ public final class ReactiveNeo4jTemplate implements ReactiveNeo4jOperations, Bea } } } - - /** - * Utility class that orchestrates {@link ReactiveEntityCallbacks}. All the methods provided here check for their - * availability and do nothing when an event cannot be published. - */ - final class ReactiveNeo4jEvents { - - private final ReactiveEntityCallbacks entityCallbacks; - - ReactiveNeo4jEvents(ReactiveEntityCallbacks entityCallbacks) { - this.entityCallbacks = entityCallbacks; - } - - Mono maybeCallBeforeBind(T object) { - return entityCallbacks.callback(ReactiveBeforeBindCallback.class, object); - } - } } diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java b/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java index aaf39130b..854df78f9 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/Neo4jMappingContext.java @@ -27,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apiguardian.api.API; import org.neo4j.cypherdsl.core.Statement; -import org.neo4j.driver.Driver; +import org.neo4j.driver.internal.types.InternalTypeSystem; import org.neo4j.driver.types.TypeSystem; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; @@ -105,22 +105,21 @@ public final class Neo4jMappingContext extends AbstractMappingContext neo4jPersistentEntity, NestedRelationshipContext relationshipContext, diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/AuditingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/AuditingBeforeBindCallback.java new file mode 100644 index 000000000..79cb40201 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/AuditingBeforeBindCallback.java @@ -0,0 +1,71 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import static org.apiguardian.api.API.Status.STABLE; + +import org.apiguardian.api.API; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.core.Ordered; +import org.springframework.data.auditing.AuditingHandler; +import org.springframework.data.auditing.IsNewAwareAuditingHandler; +import org.springframework.data.mapping.callback.EntityCallback; +import org.springframework.util.Assert; + +/** + * {@link EntityCallback} to populate auditing related fields on an entity about to be bound to a record. + * + * @author Michael J. Simons + * @soundtrack Iron Maiden - Iron Maiden + * @since 6.0.2 + */ +@API(status = STABLE, since = "6.0.2") +public final class AuditingBeforeBindCallback implements BeforeBindCallback, Ordered { + + public static final int NEO4J_AUDITING_ORDER = 100; + + private final ObjectFactory auditingHandlerFactory; + + /** + * Creates a new {@link AuditingBeforeBindCallback} using the given {@link AuditingHandler} provided by the given + * {@link ObjectFactory}. + * + * @param auditingHandlerFactory must not be {@literal null}. + */ + public AuditingBeforeBindCallback(ObjectFactory auditingHandlerFactory) { + + Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!"); + this.auditingHandlerFactory = auditingHandlerFactory; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.neo4j.repository.event.BeforeBindCallback#onBeforeBind(java.lang.Object) + */ + @Override + public Object onBeforeBind(Object entity) { + return auditingHandlerFactory.getObject().markAudited(entity); + } + + /* + * (non-Javadoc) + * @see org.springframework.core.Ordered#getOrder() + */ + @Override + public int getOrder() { + return NEO4J_AUDITING_ORDER; + } +} diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/BeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/BeforeBindCallback.java new file mode 100644 index 000000000..983810ac3 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/BeforeBindCallback.java @@ -0,0 +1,45 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import static org.apiguardian.api.API.Status.STABLE; + +import org.apiguardian.api.API; +import org.springframework.data.mapping.callback.EntityCallback; + +/** + * Entity callback triggered before an Entity is bound to a record (represented by a {@link java.util.Map + * java.util.Map<String, Object>}). + * + * @author Michael J. Simons + * @param The type of the entity. + * @since 6.0.2 + * @soundtrack Bon Jovi - Slippery When Wet + */ +@FunctionalInterface +@API(status = STABLE, since = "6.0.2") +public interface BeforeBindCallback extends EntityCallback { + + /** + * Entity callback method invoked before a domain object is saved. Can return either the same or a modified instance + * of the domain object. This method is called before converting the {@code entity} to a {@link java.util.Map}, so the + * outcome of this callback is used to create the record for the domain object. + * + * @param entity the domain object to save. + * @return the domain object to be persisted. + */ + T onBeforeBind(T entity); +} diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/EventSupport.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/EventSupport.java new file mode 100644 index 000000000..0e3883433 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/EventSupport.java @@ -0,0 +1,84 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import static org.apiguardian.api.API.Status.INTERNAL; + +import org.apiguardian.api.API; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.data.mapping.callback.EntityCallbacks; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; + +/** + * Utility class that orchestrates {@link EntityCallbacks}. Not to be used outside the framework. + * + * @author Michael J. Simons + * @since 6.0.2 + */ +@API(status = INTERNAL, since = "6.0.2") +public final class EventSupport { + + /** + * Creates event support containing the required default events plus all entity callbacks discoverable through + * the {@link BeanFactory}. + * + * @param context The mapping context that is used in some of the callbacks. + * @param beanFactory The bean factory used to discover additional callbacks. + * @return A new instance of the event support + */ + public static EventSupport discoverCallbacks(Neo4jMappingContext context, BeanFactory beanFactory) { + + EntityCallbacks entityCallbacks = EntityCallbacks.create(beanFactory); + addDefaultEntityCallbacks(context, entityCallbacks); + return new EventSupport(entityCallbacks); + } + + /** + * Creates event support containing the required default events plus all explicitly defined events. + * + * @param context The mapping context that is used in some of the callbacks. + * @param entityCallbacks predefined callbacks. + * @return A new instance of the event support + */ + public static EventSupport useExistingCallbacks(Neo4jMappingContext context, EntityCallbacks entityCallbacks) { + + addDefaultEntityCallbacks(context, entityCallbacks); + return new EventSupport(entityCallbacks); + } + + private static void addDefaultEntityCallbacks(Neo4jMappingContext context, EntityCallbacks entityCallbacks) { + + entityCallbacks.addEntityCallback(new IdGeneratingBeforeBindCallback(context)); + entityCallbacks.addEntityCallback(new OptimisticLockingBeforeBindCallback(context)); + } + + private final EntityCallbacks entityCallbacks; + + private EventSupport(EntityCallbacks entityCallbacks) { + this.entityCallbacks = entityCallbacks; + } + + @SuppressWarnings("deprecation") + public T maybeCallBeforeBind(T object) { + + if (object == null) { + return object; + } + T o = entityCallbacks + .callback(org.springframework.data.neo4j.repository.event.BeforeBindCallback.class, object); + return entityCallbacks.callback(BeforeBindCallback.class, o); + } +} diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/IdGeneratingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/IdGeneratingBeforeBindCallback.java similarity index 78% rename from src/main/java/org/springframework/data/neo4j/repository/event/IdGeneratingBeforeBindCallback.java rename to src/main/java/org/springframework/data/neo4j/core/mapping/callback/IdGeneratingBeforeBindCallback.java index 28cfb18d5..1a19620d8 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/IdGeneratingBeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/IdGeneratingBeforeBindCallback.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; -import org.apiguardian.api.API; import org.springframework.core.Ordered; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; @@ -24,14 +23,13 @@ import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; * * @author Michael J. Simons * @soundtrack Various - Kung Fury (Original Motion Picture Soundtrack) - * @since 6.0 + * @since 6.0.2 */ -@API(status = API.Status.INTERNAL, since = "6.0") -public final class IdGeneratingBeforeBindCallback implements BeforeBindCallback, Ordered { +final class IdGeneratingBeforeBindCallback implements BeforeBindCallback, Ordered { private final IdPopulator idPopulator; - public IdGeneratingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { + IdGeneratingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { this.idPopulator = new IdPopulator(neo4jMappingContext); } diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/IdPopulator.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/IdPopulator.java similarity index 98% rename from src/main/java/org/springframework/data/neo4j/repository/event/IdPopulator.java rename to src/main/java/org/springframework/data/neo4j/core/mapping/callback/IdPopulator.java index bd19a21c4..4ade92343 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/IdPopulator.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/IdPopulator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import java.util.Optional; diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/OptimisticLockingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/OptimisticLockingBeforeBindCallback.java new file mode 100644 index 000000000..8ea2be159 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/OptimisticLockingBeforeBindCallback.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import org.springframework.core.Ordered; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; + +/** + * Callback to increment the value of the version property for a given entity. + * + * @author Gerrit Meier + * @since 6.0.2 + */ +final class OptimisticLockingBeforeBindCallback implements BeforeBindCallback, Ordered { + + private final OptimisticLockingSupport optimisticLocking; + + OptimisticLockingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { + this.optimisticLocking = new OptimisticLockingSupport(neo4jMappingContext); + } + + @Override + public Object onBeforeBind(Object entity) { + return optimisticLocking.getAndIncrementVersionPropertyIfNecessary(entity); + } + + @Override + public int getOrder() { + return AuditingBeforeBindCallback.NEO4J_AUDITING_ORDER + 11; + } +} diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/OptimisticLockingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/OptimisticLockingSupport.java similarity index 68% rename from src/main/java/org/springframework/data/neo4j/repository/event/OptimisticLockingBeforeBindCallback.java rename to src/main/java/org/springframework/data/neo4j/core/mapping/callback/OptimisticLockingSupport.java index 1a4cddabd..8c2ed9a2d 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/OptimisticLockingBeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/OptimisticLockingSupport.java @@ -13,33 +13,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; -import org.apiguardian.api.API; -import org.springframework.core.Ordered; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity; import org.springframework.data.neo4j.core.mapping.Neo4jPersistentProperty; /** - * Callback to increment the value of the version property for a given entity. + * Common logic for retrieving entity metadata from the context and incrementing the version property if necessary. * - * @author Gerrit Meier - * @since 6.0 + * @author Michael J. Simons + * @soundtrack Body Count - Violent Demise: The Last Days */ -@API(status = API.Status.INTERNAL, since = "6.0") -public final class OptimisticLockingBeforeBindCallback implements BeforeBindCallback, Ordered { +final class OptimisticLockingSupport { - private final Neo4jMappingContext neo4jMappingContext; + private final Neo4jMappingContext mappingContext; - public OptimisticLockingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { - this.neo4jMappingContext = neo4jMappingContext; + OptimisticLockingSupport(Neo4jMappingContext mappingContext) { + this.mappingContext = mappingContext; } - @Override - public Object onBeforeBind(Object entity) { - Neo4jPersistentEntity neo4jPersistentEntity = (Neo4jPersistentEntity) neo4jMappingContext + Object getAndIncrementVersionPropertyIfNecessary(Object entity) { + + Neo4jPersistentEntity neo4jPersistentEntity = (Neo4jPersistentEntity) mappingContext .getRequiredNodeDescription(entity.getClass()); if (neo4jPersistentEntity.hasVersionProperty()) { @@ -61,9 +58,4 @@ public final class OptimisticLockingBeforeBindCallback implements BeforeBindCall } return entity; } - - @Override - public int getOrder() { - return AuditingBeforeBindCallback.NEO4J_AUDITING_ORDER + 11; - } } diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveAuditingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveAuditingBeforeBindCallback.java new file mode 100644 index 000000000..1978ac1ed --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveAuditingBeforeBindCallback.java @@ -0,0 +1,73 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import static org.apiguardian.api.API.Status.STABLE; + +import org.apiguardian.api.API; +import org.reactivestreams.Publisher; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.core.Ordered; +import org.springframework.data.auditing.AuditingHandler; +import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler; +import org.springframework.data.mapping.callback.EntityCallback; +import org.springframework.util.Assert; + +/** + * Reactive {@link EntityCallback} to populate auditing related fields on an entity about to be bound to a record. + * + * @author Michael J. Simons + * @soundtrack Iron Maiden - The Number Of The Beast + * @since 6.0.2 + */ +@API(status = STABLE, since = "6.0.2") +public final class ReactiveAuditingBeforeBindCallback implements ReactiveBeforeBindCallback, Ordered { + + public static final int NEO4J_REACTIVE_AUDITING_ORDER = 100; + + private final ObjectFactory auditingHandlerFactory; + + /** + * Creates a new {@link ReactiveAuditingBeforeBindCallback} using the {@link AuditingHandler} provided by the given + * {@link ObjectFactory}. + * + * @param auditingHandlerFactory must not be {@literal null}. + */ + public ReactiveAuditingBeforeBindCallback(ObjectFactory auditingHandlerFactory) { + + Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!"); + this.auditingHandlerFactory = auditingHandlerFactory; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.neo4j.repository.event.ReactiveBeforeBindCallback#onBeforeBind(java.lang.Object) + */ + @Override + public Publisher onBeforeBind(Object entity) { + + return auditingHandlerFactory.getObject().markAudited(entity); + } + + /* + * (non-Javadoc) + * @see org.springframework.core.Ordered#getOrder() + */ + @Override + public int getOrder() { + return NEO4J_REACTIVE_AUDITING_ORDER; + } +} diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveBeforeBindCallback.java new file mode 100644 index 000000000..57b029ec0 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveBeforeBindCallback.java @@ -0,0 +1,48 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import static org.apiguardian.api.API.Status.STABLE; + +import org.apiguardian.api.API; +import org.reactivestreams.Publisher; +import org.springframework.data.mapping.callback.EntityCallback; +import org.springframework.data.mapping.callback.ReactiveEntityCallbacks; + +/** + * Entity callback triggered before an Entity is bound to a record (represented by a {@link java.util.Map + * java.util.Map<String, Object>}). + * + * @param The type of the entity. + * @author Michael J. Simons + * @soundtrack Iron Maiden - Killers + * @see ReactiveEntityCallbacks + * @since 6.0.2 + */ +@FunctionalInterface +@API(status = STABLE, since = "6.0.2") +public interface ReactiveBeforeBindCallback extends EntityCallback { + + /** + * Entity callback method invoked before a domain object is saved. Can return either the same or a modified instance + * of the domain object. This method is called before converting the {@code entity} to a {@link java.util.Map}, so the + * outcome of this callback is used to create the record for the domain object. + * + * @param entity the domain object to save. + * @return the domain object to be persisted. + */ + Publisher onBeforeBind(T entity); +} diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveEventSupport.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveEventSupport.java new file mode 100644 index 000000000..ed6334309 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveEventSupport.java @@ -0,0 +1,84 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import static org.apiguardian.api.API.Status.INTERNAL; + +import reactor.core.publisher.Mono; + +import org.apiguardian.api.API; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.data.mapping.callback.EntityCallbacks; +import org.springframework.data.mapping.callback.ReactiveEntityCallbacks; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; + +/** + * Utility class that orchestrates {@link EntityCallbacks}. Not to be used outside the framework. + * + * @author Michael J. Simons + * @since 6.0.2 + */ +@API(status = INTERNAL, since = "6.0.2") +public final class ReactiveEventSupport { + + /** + * Creates event support containing the required default events plus all entity callbacks discoverable through + * the {@link BeanFactory}. + * + * @param context The mapping context that is used in some of the callbacks. + * @param beanFactory The bean factory used to discover additional callbacks. + * @return A new instance of the event support + */ + public static ReactiveEventSupport discoverCallbacks(Neo4jMappingContext context, BeanFactory beanFactory) { + + ReactiveEntityCallbacks entityCallbacks = ReactiveEntityCallbacks.create(beanFactory); + addDefaultEntityCallbacks(context, entityCallbacks); + return new ReactiveEventSupport(entityCallbacks); + } + + /** + * Creates event support containing the required default events plus all explicitly defined events. + * + * @param context The mapping context that is used in some of the callbacks. + * @param entityCallbacks predefined callbacks. + * @return A new instance of the event support + */ + public static ReactiveEventSupport useExistingCallbacks(Neo4jMappingContext context, ReactiveEntityCallbacks entityCallbacks) { + + addDefaultEntityCallbacks(context, entityCallbacks); + return new ReactiveEventSupport(entityCallbacks); + } + + private static void addDefaultEntityCallbacks(Neo4jMappingContext context, + ReactiveEntityCallbacks entityCallbacks) { + + entityCallbacks.addEntityCallback(new ReactiveIdGeneratingBeforeBindCallback(context)); + entityCallbacks.addEntityCallback(new ReactiveOptimisticLockingBeforeBindCallback(context)); + } + + private final ReactiveEntityCallbacks entityCallbacks; + + private ReactiveEventSupport(ReactiveEntityCallbacks entityCallbacks) { + this.entityCallbacks = entityCallbacks; + } + + @SuppressWarnings("deprecation") + public Mono maybeCallBeforeBind(T object) { + return entityCallbacks + .callback(org.springframework.data.neo4j.repository.event.ReactiveBeforeBindCallback.class, object) + .flatMap(o -> entityCallbacks.callback(ReactiveBeforeBindCallback.class, o)); + } +} diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveIdGeneratingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveIdGeneratingBeforeBindCallback.java similarity index 79% rename from src/main/java/org/springframework/data/neo4j/repository/event/ReactiveIdGeneratingBeforeBindCallback.java rename to src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveIdGeneratingBeforeBindCallback.java index 49985eff8..1720811ba 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveIdGeneratingBeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveIdGeneratingBeforeBindCallback.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import reactor.core.publisher.Mono; -import org.apiguardian.api.API; import org.reactivestreams.Publisher; import org.springframework.core.Ordered; import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; @@ -27,14 +26,13 @@ import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; * * @author Michael J. Simons * @soundtrack Various - Kung Fury (Original Motion Picture Soundtrack) - * @since 6.0 + * @since 6.0.2 */ -@API(status = API.Status.INTERNAL, since = "6.0") -public final class ReactiveIdGeneratingBeforeBindCallback implements ReactiveBeforeBindCallback, Ordered { +final class ReactiveIdGeneratingBeforeBindCallback implements ReactiveBeforeBindCallback, Ordered { private final IdPopulator idPopulator; - public ReactiveIdGeneratingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { + ReactiveIdGeneratingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { this.idPopulator = new IdPopulator(neo4jMappingContext); } diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveOptimisticLockingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveOptimisticLockingBeforeBindCallback.java new file mode 100644 index 000000000..7baa9e34b --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveOptimisticLockingBeforeBindCallback.java @@ -0,0 +1,48 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.core.mapping.callback; + +import reactor.core.publisher.Mono; + +import org.reactivestreams.Publisher; +import org.springframework.core.Ordered; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; + +/** + * Callback to increment the value of the version property for a given entity. + * + * @author Gerrit Meier + * @since 6.0.2 + */ +final class ReactiveOptimisticLockingBeforeBindCallback implements ReactiveBeforeBindCallback, Ordered { + + private final OptimisticLockingSupport optimisticLocking; + + ReactiveOptimisticLockingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { + this.optimisticLocking = new OptimisticLockingSupport(neo4jMappingContext); + } + + @Override + public Publisher onBeforeBind(Object entity) { + + return Mono.just(entity).map(optimisticLocking::getAndIncrementVersionPropertyIfNecessary); + } + + @Override + public int getOrder() { + return ReactiveAuditingBeforeBindCallback.NEO4J_REACTIVE_AUDITING_ORDER + 11; + } +} diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/callback/package-info.java b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/package-info.java new file mode 100644 index 000000000..ee233fe29 --- /dev/null +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/callback/package-info.java @@ -0,0 +1,21 @@ +/** + * + This package contains the callback API. There are both imperative and reactive callbacks available that get invoked + just before an entity is bound to a statement. These can be implemented by client code. For further convinience, + both imperative and reactive auditing callbacks are available. The config package contains a registrar and an + annotation to enable those without having to provide the beans manually. + + The event system comes in two flavours: Events that are based on Spring's application event system and callbacks that + are based on Spring Data's callback system. Application events can be configured to run asynchronously, which make + them a bad fit in transactional workloads. + + As a rule of thumb, use Entity callbacks for modifying entities before persisting and application events otherwise. + The best option however to react in a transactional way to changes of an entity is to implement + {@link org.springframework.data.domain.DomainEvents} on an aggregate root. + * + * @author Michael J. Simons + */ +@NonNullApi +package org.springframework.data.neo4j.core.mapping.callback; + +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/neo4j/core/mapping/package-info.java b/src/main/java/org/springframework/data/neo4j/core/mapping/package-info.java index a477bbdf6..ac8083eda 100644 --- a/src/main/java/org/springframework/data/neo4j/core/mapping/package-info.java +++ b/src/main/java/org/springframework/data/neo4j/core/mapping/package-info.java @@ -1,7 +1,9 @@ /** - * The main mapping framework. This package orchestrates the reading and writing of entities and all tasks related to it. - * The package has to be considered as internal api as a whole and we don't give any guarantees of API stability. - * + * + The main mapping framework. This package orchestrates the reading and writing of entities and all tasks related to it. + The only public API of this package is the subpackage {@literal callback}, containing the event support. + The core package itself has to be considered an internal api and we don't give any guarantees of API stability. + * * @author Michael J. Simons */ @NonNullApi diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallback.java index e8ab37ffe..23d3fa5c0 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallback.java @@ -18,10 +18,8 @@ package org.springframework.data.neo4j.repository.event; import org.apiguardian.api.API; import org.springframework.beans.factory.ObjectFactory; import org.springframework.core.Ordered; -import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.auditing.IsNewAwareAuditingHandler; import org.springframework.data.mapping.callback.EntityCallback; -import org.springframework.util.Assert; /** * {@link EntityCallback} to populate auditing related fields on an entity about to be bound to a record. @@ -29,41 +27,27 @@ import org.springframework.util.Assert; * @author Michael J. Simons * @soundtrack Iron Maiden - Iron Maiden * @since 6.0 + * @deprecated since 6.0.2, please use {@link org.springframework.data.neo4j.core.mapping.callback.AuditingBeforeBindCallback}. */ -@API(status = API.Status.INTERNAL, since = "6.0") +@API(status = API.Status.DEPRECATED, since = "6.0") +@Deprecated public final class AuditingBeforeBindCallback implements BeforeBindCallback, Ordered { - public static final int NEO4J_AUDITING_ORDER = 100; + private final org.springframework.data.neo4j.core.mapping.callback.AuditingBeforeBindCallback delegate; - private final ObjectFactory auditingHandlerFactory; - - /** - * Creates a new {@link AuditingBeforeBindCallback} using the given {@link AuditingHandler} provided by the given - * {@link ObjectFactory}. - * - * @param auditingHandlerFactory must not be {@literal null}. - */ public AuditingBeforeBindCallback(ObjectFactory auditingHandlerFactory) { - Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!"); - this.auditingHandlerFactory = auditingHandlerFactory; + this.delegate = new org.springframework.data.neo4j.core.mapping.callback.AuditingBeforeBindCallback( + auditingHandlerFactory); } - /* - * (non-Javadoc) - * @see org.springframework.data.neo4j.repository.event.BeforeBindCallback#onBeforeBind(java.lang.Object) - */ @Override public Object onBeforeBind(Object entity) { - return auditingHandlerFactory.getObject().markAudited(entity); + return delegate.onBeforeBind(entity); } - /* - * (non-Javadoc) - * @see org.springframework.core.Ordered#getOrder() - */ @Override public int getOrder() { - return NEO4J_AUDITING_ORDER; + return delegate.getOrder(); } } diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/BeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/repository/event/BeforeBindCallback.java index ca44193cc..c673a2597 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/BeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/repository/event/BeforeBindCallback.java @@ -26,9 +26,11 @@ import org.springframework.data.mapping.callback.EntityCallback; * @param The type of the entity. * @since 6.0 * @soundtrack Bon Jovi - Slippery When Wet + * @deprecated since 6.0.2, please use {@link org.springframework.data.neo4j.core.mapping.callback.BeforeBindCallback}. */ @FunctionalInterface -@API(status = API.Status.STABLE, since = "6.0") +@API(status = API.Status.DEPRECATED, since = "6.0") +@Deprecated public interface BeforeBindCallback extends EntityCallback { /** diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallback.java index 2ed3f9c01..015b32b43 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallback.java @@ -19,53 +19,35 @@ import org.apiguardian.api.API; import org.reactivestreams.Publisher; import org.springframework.beans.factory.ObjectFactory; import org.springframework.core.Ordered; -import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.auditing.ReactiveIsNewAwareAuditingHandler; import org.springframework.data.mapping.callback.EntityCallback; -import org.springframework.util.Assert; /** * Reactive {@link EntityCallback} to populate auditing related fields on an entity about to be bound to a record. * * @author Michael J. Simons * @soundtrack Iron Maiden - The Number Of The Beast - * @since 6.0 + * @deprecated since 6.0.2, please use {@link org.springframework.data.neo4j.core.mapping.callback.AuditingBeforeBindCallback}. */ -@API(status = API.Status.INTERNAL, since = "6.0") +@API(status = API.Status.DEPRECATED, since = "6.0") +@Deprecated public final class ReactiveAuditingBeforeBindCallback implements ReactiveBeforeBindCallback, Ordered { - public static final int NEO4J_REACTIVE_AUDITING_ORDER = 100; + private final org.springframework.data.neo4j.core.mapping.callback.ReactiveAuditingBeforeBindCallback delegate; - private final ObjectFactory auditingHandlerFactory; - - /** - * Creates a new {@link ReactiveAuditingBeforeBindCallback} using the {@link AuditingHandler} provided by the given - * {@link ObjectFactory}. - * - * @param auditingHandlerFactory must not be {@literal null}. - */ public ReactiveAuditingBeforeBindCallback(ObjectFactory auditingHandlerFactory) { - Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!"); - this.auditingHandlerFactory = auditingHandlerFactory; + this.delegate = new org.springframework.data.neo4j.core.mapping.callback.ReactiveAuditingBeforeBindCallback( + auditingHandlerFactory); } - /* - * (non-Javadoc) - * @see org.springframework.data.neo4j.repository.event.ReactiveBeforeBindCallback#onBeforeBind(java.lang.Object) - */ @Override public Publisher onBeforeBind(Object entity) { - - return auditingHandlerFactory.getObject().markAudited(entity); + return delegate.onBeforeBind(entity); } - /* - * (non-Javadoc) - * @see org.springframework.core.Ordered#getOrder() - */ @Override public int getOrder() { - return NEO4J_REACTIVE_AUDITING_ORDER; + return delegate.getOrder(); } } diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveBeforeBindCallback.java index 3a79132c1..ff0f4bb7a 100644 --- a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveBeforeBindCallback.java +++ b/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveBeforeBindCallback.java @@ -29,9 +29,11 @@ import org.springframework.data.mapping.callback.ReactiveEntityCallbacks; * @soundtrack Iron Maiden - Killers * @see ReactiveEntityCallbacks * @since 6.0 + * @deprecated since 6.0.2, please use {@link org.springframework.data.neo4j.core.mapping.callback.ReactiveAuditingBeforeBindCallback}. */ @FunctionalInterface -@API(status = API.Status.STABLE, since = "6.0") +@API(status = API.Status.DEPRECATED, since = "6.0") +@Deprecated public interface ReactiveBeforeBindCallback extends EntityCallback { /** diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveOptimisticLockingBeforeBindCallback.java b/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveOptimisticLockingBeforeBindCallback.java deleted file mode 100644 index 96bccf1cd..000000000 --- a/src/main/java/org/springframework/data/neo4j/repository/event/ReactiveOptimisticLockingBeforeBindCallback.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2011-2020 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 - * - * https://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.repository.event; - -import reactor.core.publisher.Mono; - -import org.apiguardian.api.API; -import org.reactivestreams.Publisher; -import org.springframework.core.Ordered; -import org.springframework.data.mapping.PersistentPropertyAccessor; -import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; -import org.springframework.data.neo4j.core.mapping.Neo4jPersistentEntity; -import org.springframework.data.neo4j.core.mapping.Neo4jPersistentProperty; - -/** - * Callback to increment the value of the version property for a given entity. - * - * @author Gerrit Meier - * @since 6.0 - */ -@API(status = API.Status.INTERNAL, since = "6.0") -public final class ReactiveOptimisticLockingBeforeBindCallback implements ReactiveBeforeBindCallback, Ordered { - - private final Neo4jMappingContext neo4jMappingContext; - - public ReactiveOptimisticLockingBeforeBindCallback(Neo4jMappingContext neo4jMappingContext) { - this.neo4jMappingContext = neo4jMappingContext; - } - - @Override - public Publisher onBeforeBind(Object entity) { - - return Mono.fromSupplier(() -> { - Neo4jPersistentEntity neo4jPersistentEntity = (Neo4jPersistentEntity) neo4jMappingContext - .getRequiredNodeDescription(entity.getClass()); - - if (neo4jPersistentEntity.hasVersionProperty()) { - PersistentPropertyAccessor propertyAccessor = neo4jPersistentEntity.getPropertyAccessor(entity); - Neo4jPersistentProperty versionProperty = neo4jPersistentEntity.getRequiredVersionProperty(); - - if (!Long.class.isAssignableFrom(versionProperty.getType())) { - return entity; - } - - Long versionPropertyValue = (Long) propertyAccessor.getProperty(versionProperty); - - long newVersionValue = 0; - if (versionPropertyValue != null) { - newVersionValue = versionPropertyValue + 1; - } - - propertyAccessor.setProperty(versionProperty, newVersionValue); - } - return entity; - }); - } - - @Override - public int getOrder() { - return ReactiveAuditingBeforeBindCallback.NEO4J_REACTIVE_AUDITING_ORDER + 11; - } -} diff --git a/src/main/java/org/springframework/data/neo4j/repository/event/package-info.java b/src/main/java/org/springframework/data/neo4j/repository/event/package-info.java deleted file mode 100644 index 9c202d5e3..000000000 --- a/src/main/java/org/springframework/data/neo4j/repository/event/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Contains the infrastructure for the event system. The event system comes in two flavours: Events that are based on - * Spring's application event system and callbacks that are based on Spring Data's callback system. Application events - * can be configured to run asynchronously, which make them a bad fit in transactional workloads. - *

- * As a rule of thumb, use Entity callbacks for modifying entities before persisting and application events otherwise. - * The best option however to react in a transactional way to changes of an entity is to implement - * {@link org.springframework.data.domain.DomainEvents} on an aggregate root. - * - * @author Michael J. Simons - * @since 6.0 - * @soundtrack Bon Jovi - Slippery When Wet - */ -@NonNullApi -package org.springframework.data.neo4j.repository.event; - -import org.springframework.lang.NonNullApi; diff --git a/src/test/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallbackTest.java b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/AuditingBeforeBindCallbackTest.java similarity index 98% rename from src/test/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallbackTest.java rename to src/test/java/org/springframework/data/neo4j/core/mapping/callback/AuditingBeforeBindCallbackTest.java index c54c3f531..2dddab439 100644 --- a/src/test/java/org/springframework/data/neo4j/repository/event/AuditingBeforeBindCallbackTest.java +++ b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/AuditingBeforeBindCallbackTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/src/test/java/org/springframework/data/neo4j/repository/event/IdPopulatorTest.java b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/IdPopulatorTest.java similarity index 98% rename from src/test/java/org/springframework/data/neo4j/repository/event/IdPopulatorTest.java rename to src/test/java/org/springframework/data/neo4j/core/mapping/callback/IdPopulatorTest.java index 817e9c2a8..7db6659e9 100644 --- a/src/test/java/org/springframework/data/neo4j/repository/event/IdPopulatorTest.java +++ b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/IdPopulatorTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; diff --git a/src/test/java/org/springframework/data/neo4j/repository/event/ImmutableSample.java b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/ImmutableSample.java similarity index 94% rename from src/test/java/org/springframework/data/neo4j/repository/event/ImmutableSample.java rename to src/test/java/org/springframework/data/neo4j/core/mapping/callback/ImmutableSample.java index a60b0ccef..0aec7359b 100644 --- a/src/test/java/org/springframework/data/neo4j/repository/event/ImmutableSample.java +++ b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/ImmutableSample.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; diff --git a/src/test/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallbackTest.java b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveAuditingBeforeBindCallbackTest.java similarity index 98% rename from src/test/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallbackTest.java rename to src/test/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveAuditingBeforeBindCallbackTest.java index d53952823..51c5157df 100644 --- a/src/test/java/org/springframework/data/neo4j/repository/event/ReactiveAuditingBeforeBindCallbackTest.java +++ b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/ReactiveAuditingBeforeBindCallbackTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; diff --git a/src/test/java/org/springframework/data/neo4j/repository/event/Sample.java b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/Sample.java similarity index 94% rename from src/test/java/org/springframework/data/neo4j/repository/event/Sample.java rename to src/test/java/org/springframework/data/neo4j/core/mapping/callback/Sample.java index fe13104e8..f48d8da70 100644 --- a/src/test/java/org/springframework/data/neo4j/repository/event/Sample.java +++ b/src/test/java/org/springframework/data/neo4j/core/mapping/callback/Sample.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.neo4j.repository.event; +package org.springframework.data.neo4j.core.mapping.callback; import java.util.Date; diff --git a/src/test/java/org/springframework/data/neo4j/integration/imperative/CallbacksIT.java b/src/test/java/org/springframework/data/neo4j/integration/imperative/CallbacksIT.java index ddd77629b..1384716d3 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/imperative/CallbacksIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/imperative/CallbacksIT.java @@ -26,11 +26,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.neo4j.config.AbstractNeo4jConfig; +import org.springframework.data.neo4j.core.mapping.callback.BeforeBindCallback; import org.springframework.data.neo4j.integration.imperative.repositories.ThingRepository; import org.springframework.data.neo4j.integration.shared.common.CallbacksITBase; import org.springframework.data.neo4j.integration.shared.common.ThingWithAssignedId; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; -import org.springframework.data.neo4j.repository.event.BeforeBindCallback; import org.springframework.transaction.annotation.EnableTransactionManagement; /** diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/MultipleContextsIT.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/MultipleContextsIT.java new file mode 100644 index 000000000..ac285faca --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/MultipleContextsIT.java @@ -0,0 +1,120 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; + +import org.junit.jupiter.api.Test; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Config; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Logging; +import org.neo4j.driver.Session; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.neo4j.integration.multiple_ctx_imperative.domain1.Domain1Config; +import org.springframework.data.neo4j.integration.multiple_ctx_imperative.domain1.Domain1Entity; +import org.springframework.data.neo4j.integration.multiple_ctx_imperative.domain1.Domain1Repository; +import org.springframework.data.neo4j.integration.multiple_ctx_imperative.domain2.Domain2Config; +import org.springframework.data.neo4j.integration.multiple_ctx_imperative.domain2.Domain2Entity; +import org.springframework.data.neo4j.integration.multiple_ctx_imperative.domain2.Domain2Repository; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.testcontainers.containers.Neo4jContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +/** + * Tests whether multiple context are truly separated. + * + * @author Michael J. Simons + */ +@SpringJUnitConfig(classes = { SharedConfig.class, Domain1Config.class, Domain2Config.class }) +@Testcontainers(disabledWithoutDocker = true) +public class MultipleContextsIT { + + @Container + private static Neo4jContainer container1 = new Neo4jContainer<>("neo4j:4.0") + .withAdminPassword("secret1"); + + @Container + private static Neo4jContainer container2 = new Neo4jContainer<>("neo4j:4.0") + .withAdminPassword("secret2"); + + @DynamicPropertySource + static void neo4jSettings(DynamicPropertyRegistry registry) { + + registry.add("database1.url", container1::getBoltUrl); + registry.add("database1.password", () -> "secret1"); + + registry.add("database2.url", container2::getBoltUrl); + registry.add("database2.password", () -> "secret2"); + } + + @Test // DATAGRAPH-1441 + void repositoriesShouldTargetTheCorrectDatabase( + @Autowired Domain1Repository repo1, + @Autowired Domain2Repository repo2 + ) { + + Domain1Entity newEntity1 = repo1.save(new Domain1Entity("For domain 1")); + newEntity1.setAnAttribute(newEntity1.getAnAttribute() + " updated"); + newEntity1 = repo1.save(newEntity1); + long id1 = newEntity1.getId(); + + Domain2Entity newEntity2 = new Domain2Entity("For domain 2"); + newEntity2.setAnAttribute(newEntity2.getAnAttribute() + " updated"); + newEntity2 = repo2.save(newEntity2); + long id2 = repo2.save(newEntity2).getId(); + + try (Driver driver = newDriver(container1.getBoltUrl(), container1.getAdminPassword()); + Session session = driver.session()) { + verifyExistenceAndVersion(id1, session); + } + + try (Driver driver = newDriver(container2.getBoltUrl(), container2.getAdminPassword()); + Session session = driver.session()) { + verifyExistenceAndVersion(id2, session); + } + } + + /** + * Create drivers independend from the setup under test. + * + * @param boltUrl Where to connect to + * @param password Which password + * @return Minimal driver instance. + */ + private static Driver newDriver(String boltUrl, String password) { + + Config driverConfig = Config.builder() + .withMaxConnectionPoolSize(1) + .withLogging(Logging.none()) + .withEventLoopThreads(1) + .build(); + return GraphDatabase.driver(boltUrl, AuthTokens.basic("neo4j", password), driverConfig); + } + + private static void verifyExistenceAndVersion(long id1, Session session) { + Long version = session + .readTransaction(tx -> tx.run("MATCH (n) WHERE id(n) = $id RETURN n.version", Collections + .singletonMap("id", id1)).single().get(0).asLong()); + assertThat(version).isOne(); + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/SharedConfig.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/SharedConfig.java new file mode 100644 index 000000000..e2a21c032 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/SharedConfig.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.core.convert.Neo4jConversions; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author Michael J. Simons + * @soundtrack Kelis - Tasty + */ +@EnableTransactionManagement +@Configuration(proxyBeanMethods = false) +public class SharedConfig { + + @Bean + public Neo4jConversions neo4jConversions() { + return new Neo4jConversions(); + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Config.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Config.java new file mode 100644 index 000000000..32ff80081 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Config.java @@ -0,0 +1,92 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative.domain1; + +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.Environment; +import org.springframework.data.neo4j.config.Neo4jEntityScanner; +import org.springframework.data.neo4j.core.DatabaseSelection; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jOperations; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.data.neo4j.core.convert.Neo4jConversions; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * @author Michael J. Simons + * @soundtrack Kelis - Tasty + */ +@Configuration(proxyBeanMethods = false) +@EnableNeo4jRepositories( + basePackageClasses = Domain1Config.class, + neo4jMappingContextRef = "domain1Context", + neo4jTemplateRef = "domain1Template", + transactionManagerRef = "domain1Manager" +) +public class Domain1Config { + + @Primary @Bean + public Driver domain1Driver(Environment env) { + + return GraphDatabase.driver(env.getRequiredProperty("database1.url"), + AuthTokens.basic("neo4j", env.getRequiredProperty("database1.password"))); + } + + @Primary @Bean + public Neo4jClient domain1Client(@Qualifier("domain1Driver") Driver driver) { + return Neo4jClient.create(driver); + } + + @Primary @Bean + public Neo4jOperations domain1Template( + @Qualifier("domain1Client") Neo4jClient domain1Client, + @Qualifier("domain1Context") Neo4jMappingContext domain1Context, + @Qualifier("domain1Selection") DatabaseSelectionProvider domain1Selection + ) { + return new Neo4jTemplate(domain1Client, domain1Context, domain1Selection); + } + + @Primary @Bean + public PlatformTransactionManager domain1Manager( + @Qualifier("domain1Driver") Driver driver, + @Qualifier("domain1Selection") DatabaseSelectionProvider domain1Selection + ) { + return new Neo4jTransactionManager(driver, domain1Selection); + } + + @Primary @Bean + public DatabaseSelectionProvider domain1Selection() { + return () -> DatabaseSelection.undecided(); + } + + @Primary @Bean + public Neo4jMappingContext domain1Context(Neo4jConversions neo4jConversions) throws ClassNotFoundException { + Neo4jMappingContext context = new Neo4jMappingContext(neo4jConversions); + context.setInitialEntitySet(Neo4jEntityScanner.get().scan(this.getClass().getPackage().getName())); + context.setStrict(true); + return context; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Entity.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Entity.java new file mode 100644 index 000000000..97cbee141 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Entity.java @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative.domain1; + +import org.springframework.data.annotation.Version; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; + +/** + * @author Michael J. Simons + * @soundtrack Kelis - Tasty + */ +@Node +public class Domain1Entity { + + @Id @GeneratedValue + private Long id; + + @Version + private Long version; + + private String anAttribute; + + public Domain1Entity(String anAttribute) { + this.anAttribute = anAttribute; + } + + public Long getId() { + return id; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + public String getAnAttribute() { + return anAttribute; + } + + public void setAnAttribute(String anAttribute) { + this.anAttribute = anAttribute; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Repository.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Repository.java new file mode 100644 index 000000000..878b90d70 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain1/Domain1Repository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative.domain1; + +import org.springframework.data.neo4j.repository.Neo4jRepository; + +/** + * @author Michael J. Simons + * @soundtrack Various - T2 Trainspotting + */ +public interface Domain1Repository extends Neo4jRepository { +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Config.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Config.java new file mode 100644 index 000000000..ebcc46751 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Config.java @@ -0,0 +1,91 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative.domain2; + +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.data.neo4j.config.Neo4jEntityScanner; +import org.springframework.data.neo4j.core.DatabaseSelection; +import org.springframework.data.neo4j.core.DatabaseSelectionProvider; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.data.neo4j.core.Neo4jOperations; +import org.springframework.data.neo4j.core.Neo4jTemplate; +import org.springframework.data.neo4j.core.convert.Neo4jConversions; +import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager; +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * @author Michael J. Simons + * @soundtrack Kelis - Tasty + */ +@Configuration(proxyBeanMethods = false) +@EnableNeo4jRepositories( + basePackageClasses = Domain2Config.class, + neo4jMappingContextRef = "domain2Context", + neo4jTemplateRef = "domain2Template", + transactionManagerRef = "domain2Manager" +) +public class Domain2Config { + + @Bean + public Driver domain2Driver(Environment env) { + + return GraphDatabase.driver(env.getRequiredProperty("database2.url"), + AuthTokens.basic("neo4j", env.getRequiredProperty("database2.password"))); + } + + @Bean + public Neo4jClient domain2Client(@Qualifier("domain2Driver") Driver driver) { + return Neo4jClient.create(driver); + } + + @Bean + public Neo4jOperations domain2Template( + @Qualifier("domain2Client") Neo4jClient domain2Client, + @Qualifier("domain2Context") Neo4jMappingContext domain2Context, + @Qualifier("domain2Selection") DatabaseSelectionProvider domain2Selection + ) { + return new Neo4jTemplate(domain2Client, domain2Context, domain2Selection); + } + + @Bean + public PlatformTransactionManager domain2Manager( + @Qualifier("domain2Driver") Driver driver, + @Qualifier("domain2Selection") DatabaseSelectionProvider domain2Selection + ) { + return new Neo4jTransactionManager(driver, domain2Selection); + } + + @Bean + public DatabaseSelectionProvider domain2Selection() { + return () -> DatabaseSelection.undecided(); + } + + @Bean + public Neo4jMappingContext domain2Context(Neo4jConversions neo4jConversions) throws ClassNotFoundException { + Neo4jMappingContext context = new Neo4jMappingContext(neo4jConversions); + context.setInitialEntitySet(Neo4jEntityScanner.get().scan(this.getClass().getPackage().getName())); + context.setStrict(true); + return context; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Entity.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Entity.java new file mode 100644 index 000000000..f5debb95d --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Entity.java @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative.domain2; + +import org.springframework.data.annotation.Version; +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; + +/** + * @author Michael J. Simons + * @soundtrack Kelis - Tasty + */ +@Node +public class Domain2Entity { + + @Id @GeneratedValue + private Long id; + + @Version + private Long version; + + private String anAttribute; + + public Domain2Entity(String anAttribute) { + this.anAttribute = anAttribute; + } + + public Long getId() { + return id; + } + + public Long getVersion() { + return version; + } + + public String getAnAttribute() { + return anAttribute; + } + + public void setAnAttribute(String anAttribute) { + this.anAttribute = anAttribute; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Repository.java b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Repository.java new file mode 100644 index 000000000..ddfebb67a --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/multiple_ctx_imperative/domain2/Domain2Repository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2020 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 + * + * https://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.integration.multiple_ctx_imperative.domain2; + +import org.springframework.data.neo4j.repository.Neo4jRepository; + +/** + * @author Michael J. Simons + * @soundtrack Various - T2 Trainspotting + */ +public interface Domain2Repository extends Neo4jRepository { +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveCallbacksIT.java b/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveCallbacksIT.java index c2593ff6b..81217d367 100644 --- a/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveCallbacksIT.java +++ b/src/test/java/org/springframework/data/neo4j/integration/reactive/ReactiveCallbacksIT.java @@ -30,11 +30,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.neo4j.config.AbstractReactiveNeo4jConfig; +import org.springframework.data.neo4j.core.mapping.callback.ReactiveBeforeBindCallback; import org.springframework.data.neo4j.integration.reactive.repositories.ReactiveThingRepository; import org.springframework.data.neo4j.integration.shared.common.CallbacksITBase; import org.springframework.data.neo4j.integration.shared.common.ThingWithAssignedId; import org.springframework.data.neo4j.repository.config.EnableReactiveNeo4jRepositories; -import org.springframework.data.neo4j.repository.event.ReactiveBeforeBindCallback; import org.springframework.data.neo4j.test.Neo4jExtension; import org.springframework.transaction.ReactiveTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement;