Create NodeManagerFactory, NodeManager and related infrastructure.
A NodeManager will be used to create, read, update, delete and query managed objects. It will use a short lived persistence context. This adds the basic skeletons for doing this but especially the infrastructure to allow the following usage: * User configures a driver bean * User provides a NodeManagerFactory using that driver and a given set of classes to be managed * The factory gets augmented to handle transactions and facilitate Spring Datas class scanning mechanism The Spring Boot starter will automatically provide a configured NodeManager instance.
This commit is contained in:
21
etc/adr/adr-003.adoc
Normal file
21
etc/adr/adr-003.adoc
Normal file
@@ -0,0 +1,21 @@
|
||||
== ADR 3: Public classes that are part of internal API only must be final
|
||||
|
||||
=== Status
|
||||
|
||||
accepted, open
|
||||
|
||||
=== Context
|
||||
|
||||
Due to the fact that we are not yet on the module path, we need to have some classes public defined that are not meant
|
||||
to be part of the public API.
|
||||
|
||||
=== Decision
|
||||
|
||||
Those classes should be marked `@API(status = API.Status.INTERNAL, since = "1.0")` as well as made final to at least
|
||||
prevent people from inheriting from them.
|
||||
We need to add architecture rules in jQAssistant.
|
||||
|
||||
=== Consequences
|
||||
|
||||
Potentially problems with some Spring proxies.
|
||||
Need to be solved on a case by case incident.
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.core;
|
||||
|
||||
import org.apiguardian.api.API;
|
||||
import org.neo4j.driver.v1.StatementRunner;
|
||||
import org.neo4j.driver.v1.Transaction;
|
||||
import org.springframework.data.neo4j.core.context.DefaultPersistenceContext;
|
||||
import org.springframework.data.neo4j.core.context.PersistenceContext;
|
||||
import org.springframework.data.neo4j.core.schema.Schema;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@API(status = API.Status.INTERNAL, since = "1.0")
|
||||
class DefaultNodeManager implements NodeManager {
|
||||
|
||||
private final PersistenceContext persistenceContext = new DefaultPersistenceContext();
|
||||
|
||||
private final Schema schema;
|
||||
|
||||
private final Neo4jTemplate neo4jTemplate;
|
||||
|
||||
private final Transaction transaction;
|
||||
|
||||
DefaultNodeManager(Schema schema, StatementRunner statementRunner) {
|
||||
this.schema = schema;
|
||||
this.neo4jTemplate = new Neo4jTemplate(() -> statementRunner);
|
||||
this.transaction = statementRunner instanceof Transaction ? (Transaction) statementRunner : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Transaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeQuery(String query) {
|
||||
|
||||
return neo4jTemplate.executeQuery(query);
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,12 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.core;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.neo4j.driver.v1.Driver;
|
||||
import org.neo4j.driver.v1.StatementResult;
|
||||
import org.neo4j.driver.v1.StatementRunner;
|
||||
import org.springframework.data.neo4j.core.transaction.DefaultNeo4jStatementRunnerSupplier;
|
||||
import org.springframework.data.neo4j.core.transaction.StatementRunnerSupplier;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link Neo4jOperations}. Uses the Neo4j Java driver to connect to and interact with the
|
||||
@@ -33,10 +34,14 @@ import org.springframework.data.neo4j.core.transaction.StatementRunnerSupplier;
|
||||
*/
|
||||
public class Neo4jTemplate implements Neo4jOperations {
|
||||
|
||||
private StatementRunnerSupplier<StatementRunner> statementRunnerSupplier;
|
||||
private Supplier<StatementRunner> statementRunnerSupplier;
|
||||
|
||||
public Neo4jTemplate(Driver driver) {
|
||||
this.statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(driver);
|
||||
this(() -> Neo4jTransactionUtils.retrieveTransactionalStatementRunner(driver));
|
||||
}
|
||||
|
||||
Neo4jTemplate(Supplier<StatementRunner> statementRunnerSupplier) {
|
||||
this.statementRunnerSupplier = statementRunnerSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.core;
|
||||
|
||||
import org.apiguardian.api.API;
|
||||
import org.neo4j.driver.v1.Transaction;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Entry point for creating queries that return managed Nodes. The node manager is not supposed to be kept around
|
||||
* for longer than necessary. Try to keep your transactions short to avoid memory pressure due to keeping track of
|
||||
* managed nodes.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@API(status = API.Status.STABLE, since = "1.0")
|
||||
public interface NodeManager {
|
||||
|
||||
/**
|
||||
* Clears all managed entities and flushes any open state to the underlying storage.
|
||||
*/
|
||||
default void flush() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Transaction getTransaction();
|
||||
|
||||
@API(status = API.Status.EXPERIMENTAL)
|
||||
Object executeQuery(String query);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.core;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apiguardian.api.API;
|
||||
import org.neo4j.driver.v1.AccessMode;
|
||||
import org.neo4j.driver.v1.Driver;
|
||||
import org.neo4j.driver.v1.StatementRunner;
|
||||
import org.springframework.data.neo4j.core.schema.Scanner;
|
||||
import org.springframework.data.neo4j.core.schema.Schema;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Creates ready to use instances of {@link NodeManager}.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@API(status = API.Status.STABLE, since = "1.0")
|
||||
@Slf4j
|
||||
public final class NodeManagerFactory {
|
||||
|
||||
/**
|
||||
* Driver that is used to create new sessions, either by directly invoking it or through Springs transactional utils.
|
||||
*/
|
||||
private final Driver driver;
|
||||
|
||||
/**
|
||||
* The initial set of classes that will be scanned in {@link #initialize()} to build the schema for node managers
|
||||
* belonging to this factory.
|
||||
*/
|
||||
private final Set<Class<?>> initialPersistentClasses;
|
||||
|
||||
/** The scanner used to scan the initial set of persistent classes for creating a schema. */
|
||||
private Scanner scanner = new NoopScanner();
|
||||
|
||||
@Nullable
|
||||
private Schema schema;
|
||||
|
||||
private Function<Driver, StatementRunner> statementRunnerProvider = sourceDriver -> sourceDriver
|
||||
.session(AccessMode.WRITE, Collections.emptyList()).beginTransaction();
|
||||
|
||||
/**
|
||||
* Creates a new instance of a factory producing {@link NodeManager node managers}. When used in a transactional setup,
|
||||
* i.e. with the {@link org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager}, make sure to use
|
||||
* the same {@link Driver driver instance} for both the node and the transaction manager.
|
||||
* <p>
|
||||
* Spring Boots autoconfiguration for SDN RX will make sure that the same driver is used for both concerns.
|
||||
*
|
||||
* @param driver The driver used to obtain statement runners from when creating instances of node managers.
|
||||
* @param initialPersistentClasses The set of classes that should be initially scanned
|
||||
*/
|
||||
public NodeManagerFactory(Driver driver, Class<?>... initialPersistentClasses) {
|
||||
this.driver = driver;
|
||||
|
||||
this.initialPersistentClasses = new HashSet<>();
|
||||
Arrays.stream(initialPersistentClasses).forEach(this.initialPersistentClasses::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new node manager. The returned manager is supposed to have a short lifetime. When used in a Spring setup,
|
||||
* this method should not called directly by client code. Instead the client code should use an injected instance of
|
||||
* {@link NodeManager}, which will participate in Springs application based transaction or in JTA transactions.
|
||||
*
|
||||
* @return A new node manager
|
||||
*/
|
||||
public NodeManager createNodeManager() {
|
||||
|
||||
Objects.requireNonNull(schema, "A schema is required. Did you call #initialize() before using this factory?");
|
||||
return new DefaultNodeManager(schema, statementRunnerProvider.apply(driver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a provider for extracting sessions/transactions from a Neo4j driver. This method is not to be called
|
||||
* from application code and only used by internal API.
|
||||
*
|
||||
* @param statementRunnerProvider A required provider of statement runners
|
||||
*/
|
||||
@API(status = API.Status.INTERNAL, since = "1.0")
|
||||
public void setStatementRunnerProvider(Function<Driver, StatementRunner> statementRunnerProvider) {
|
||||
|
||||
Objects.requireNonNull(statementRunnerProvider, "A node manager factory requires a provider of statement runners.");
|
||||
this.statementRunnerProvider = statementRunnerProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the scanner used to build a schema for domain objects. This method is not to be called from application
|
||||
* code and only used by internal API.
|
||||
*
|
||||
* @param scanner
|
||||
*/
|
||||
@API(status = API.Status.INTERNAL, since = "1.0")
|
||||
public void setScanner(Scanner scanner) {
|
||||
|
||||
Objects.requireNonNull(scanner, "A node manager factory requires a scanner.");
|
||||
this.scanner = scanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* This initializes this factory and is usually called by Springs infrastructure and only useful as standalone call
|
||||
* when the node manager factory is used without Spring.
|
||||
*/
|
||||
public void initialize() {
|
||||
|
||||
log.info("Initializing schema with {} persistent classes", this.initialPersistentClasses.size());
|
||||
this.schema = scanner.scan(Collections.unmodifiableSet(this.initialPersistentClasses));
|
||||
}
|
||||
|
||||
/**
|
||||
* A noop implementation of a Schema scanner, only provided to create instances of a {@link NodeManagerFactory} that
|
||||
* are in a valid state without booting up a whole context.
|
||||
*/
|
||||
private static class NoopScanner implements Scanner {
|
||||
|
||||
@Override
|
||||
public Schema scan(Collection<Class<?>> persistentClasses) {
|
||||
return new Schema();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,5 +21,5 @@ package org.springframework.data.neo4j.core.context;
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class PersistenceContextImpl implements PersistenceContext {
|
||||
public class DefaultPersistenceContext implements PersistenceContext {
|
||||
}
|
||||
@@ -25,12 +25,12 @@ import org.springframework.data.util.TypeInformation;
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class Neo4jPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4jPersistentProperty>
|
||||
class DefaultNeo4jPersistentEntity<T> extends BasicPersistentEntity<T, Neo4jPersistentProperty>
|
||||
implements Neo4jPersistentEntity<T> {
|
||||
|
||||
private final String primaryLabel;
|
||||
|
||||
Neo4jPersistentEntityImpl(TypeInformation<T> information) {
|
||||
DefaultNeo4jPersistentEntity(TypeInformation<T> information) {
|
||||
super(information);
|
||||
|
||||
Node nodeAnnotation = this.findAnnotation(Node.class);
|
||||
@@ -27,7 +27,7 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class Neo4jPersistentPropertyImpl extends AnnotationBasedPersistentProperty<Neo4jPersistentProperty>
|
||||
class DefaultNeo4jPersistentProperty extends AnnotationBasedPersistentProperty<Neo4jPersistentProperty>
|
||||
implements Neo4jPersistentProperty {
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ class Neo4jPersistentPropertyImpl extends AnnotationBasedPersistentProperty<Neo4
|
||||
* @param owner must not be {@literal null}.
|
||||
* @param simpleTypeHolder
|
||||
*/
|
||||
Neo4jPersistentPropertyImpl(Property property,
|
||||
DefaultNeo4jPersistentProperty(Property property,
|
||||
PersistentEntity<?, Neo4jPersistentProperty> owner,
|
||||
SimpleTypeHolder simpleTypeHolder) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.springframework.data.neo4j.core.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -50,12 +51,14 @@ public class MappingContextBasedScannerImpl implements Scanner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Schema scan() {
|
||||
public Schema scan(Collection<Class<?>> persistentClasses) {
|
||||
|
||||
final Schema schema = new Schema();
|
||||
neo4jMappingContext.getPersistentEntities().forEach(m ->
|
||||
schema.registerNodeDescription(describeAsNode(m))
|
||||
);
|
||||
persistentClasses.forEach(clazz -> {
|
||||
Neo4jPersistentEntity<?> m = neo4jMappingContext.getPersistentEntity(clazz);
|
||||
schema.registerNodeDescription(describeAsNode(m));
|
||||
});
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersistentE
|
||||
@Override
|
||||
protected <T> Neo4jPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
|
||||
return new Neo4jPersistentEntityImpl<>(typeInformation);
|
||||
return new DefaultNeo4jPersistentEntity<>(typeInformation);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -48,6 +48,6 @@ public class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersistentE
|
||||
protected Neo4jPersistentProperty createPersistentProperty(Property property,
|
||||
Neo4jPersistentEntity<?> neo4jPersistentProperties, SimpleTypeHolder simpleTypeHolder) {
|
||||
|
||||
return new Neo4jPersistentPropertyImpl(property, neo4jPersistentProperties, simpleTypeHolder);
|
||||
return new DefaultNeo4jPersistentProperty(property, neo4jPersistentProperties, simpleTypeHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.core.schema;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apiguardian.api.API;
|
||||
|
||||
/**
|
||||
@@ -32,7 +34,8 @@ public interface Scanner {
|
||||
/**
|
||||
* Scans the relevant classes and creates a schema.
|
||||
*
|
||||
* @param persistentClasses The classes to scan
|
||||
* @return The new schema.
|
||||
*/
|
||||
Schema scan();
|
||||
Schema scan(Collection<Class<?>> persistentClasses);
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.core.transaction;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.neo4j.driver.v1.AccessMode;
|
||||
import org.neo4j.driver.v1.Driver;
|
||||
import org.neo4j.driver.v1.StatementRunner;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultNeo4jStatementRunnerSupplier implements StatementRunnerSupplier<StatementRunner> {
|
||||
|
||||
private final Driver driver;
|
||||
|
||||
@Override
|
||||
public StatementRunner get() {
|
||||
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
return driver.session();
|
||||
}
|
||||
|
||||
// Try existing transaction
|
||||
Neo4jResourceHolder resourceHolder = (Neo4jResourceHolder) TransactionSynchronizationManager
|
||||
.getResource(driver);
|
||||
|
||||
if (resourceHolder != null) {
|
||||
|
||||
return resourceHolder.getTransaction();
|
||||
}
|
||||
|
||||
// Manually create a new synchronization
|
||||
resourceHolder = new Neo4jResourceHolder(driver.session(AccessMode.WRITE, Collections.emptyList()));
|
||||
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new Neo4jSessionSynchronization(resourceHolder, driver));
|
||||
resourceHolder.setSynchronizedWithTransaction(true);
|
||||
TransactionSynchronizationManager.bindResource(driver, resourceHolder);
|
||||
|
||||
return resourceHolder.getTransaction();
|
||||
}
|
||||
}
|
||||
@@ -32,17 +32,17 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jResourceHolder extends ResourceHolderSupport {
|
||||
public class Neo4jConnectionHolder extends ResourceHolderSupport {
|
||||
|
||||
private final Session session;
|
||||
|
||||
private final Transaction transaction;
|
||||
|
||||
Neo4jResourceHolder(Session session) {
|
||||
Neo4jConnectionHolder(Session session) {
|
||||
this(session, TransactionConfig.empty());
|
||||
}
|
||||
|
||||
Neo4jResourceHolder(Session session, TransactionConfig transactionConfig) {
|
||||
Neo4jConnectionHolder(Session session, TransactionConfig transactionConfig) {
|
||||
this.session = session;
|
||||
|
||||
this.transaction = this.session.beginTransaction(transactionConfig);
|
||||
@@ -24,17 +24,17 @@ import org.springframework.transaction.support.TransactionSynchronization;
|
||||
|
||||
/**
|
||||
* Neo4j specific {@link ResourceHolderSynchronization} for resource cleanup at the end of a transaction when
|
||||
* participating in a non-native Neoj4 transaction, such as a Jta transaction.
|
||||
* participating in a non-native Neo4j transaction, such as a Jta transaction.
|
||||
*/
|
||||
class Neo4jSessionSynchronization
|
||||
extends ResourceHolderSynchronization<Neo4jResourceHolder, Object> {
|
||||
extends ResourceHolderSynchronization<Neo4jConnectionHolder, Object> {
|
||||
|
||||
private final Neo4jResourceHolder localResourceHolder;
|
||||
private final Neo4jConnectionHolder localConnectionHolder;
|
||||
|
||||
Neo4jSessionSynchronization(Neo4jResourceHolder resourceHolder, Driver driver) {
|
||||
Neo4jSessionSynchronization(Neo4jConnectionHolder connectionHolder, Driver driver) {
|
||||
|
||||
super(resourceHolder, driver);
|
||||
this.localResourceHolder = resourceHolder;
|
||||
super(connectionHolder, driver);
|
||||
this.localConnectionHolder = connectionHolder;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -51,7 +51,7 @@ class Neo4jSessionSynchronization
|
||||
* @see org.springframework.transaction.support.ResourceHolderSynchronization#processResourceAfterCommit(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected void processResourceAfterCommit(Neo4jResourceHolder resourceHolder) {
|
||||
protected void processResourceAfterCommit(Neo4jConnectionHolder resourceHolder) {
|
||||
|
||||
super.processResourceAfterCommit(resourceHolder);
|
||||
|
||||
@@ -67,8 +67,8 @@ class Neo4jSessionSynchronization
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
|
||||
if (status == TransactionSynchronization.STATUS_ROLLED_BACK && localResourceHolder.hasActiveTransaction()) {
|
||||
localResourceHolder.rollback();
|
||||
if (status == TransactionSynchronization.STATUS_ROLLED_BACK && localConnectionHolder.hasActiveTransaction()) {
|
||||
localConnectionHolder.rollback();
|
||||
}
|
||||
|
||||
super.afterCompletion(status);
|
||||
@@ -79,7 +79,7 @@ class Neo4jSessionSynchronization
|
||||
* @see org.springframework.transaction.support.ResourceHolderSynchronization#releaseResource(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected void releaseResource(Neo4jResourceHolder resourceHolder, Object resourceKey) {
|
||||
protected void releaseResource(Neo4jConnectionHolder resourceHolder, Object resourceKey) {
|
||||
|
||||
if (resourceHolder.hasActiveSession()) {
|
||||
resourceHolder.close();
|
||||
|
||||
@@ -18,15 +18,23 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.core.transaction;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apiguardian.api.API;
|
||||
import org.neo4j.driver.v1.AccessMode;
|
||||
import org.neo4j.driver.v1.Driver;
|
||||
import org.neo4j.driver.v1.Session;
|
||||
import org.neo4j.driver.v1.TransactionConfig;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.data.neo4j.core.NodeManagerFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.InvalidIsolationLevelException;
|
||||
@@ -41,23 +49,44 @@ import org.springframework.transaction.support.TransactionSynchronizationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Dedicated {@link org.springframework.transaction.PlatformTransactionManager} for native Neo4j transactions.
|
||||
* Dedicated {@link org.springframework.transaction.PlatformTransactionManager} for native Neo4j transactions. This
|
||||
* transaction manager will synchronize a pair of a native Neo4j session/transaction and one instance of a
|
||||
* {@link org.springframework.data.neo4j.core.NodeManager} with the transaction.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jTransactionManager extends AbstractPlatformTransactionManager {
|
||||
@Slf4j
|
||||
public class Neo4jTransactionManager extends AbstractPlatformTransactionManager implements BeanFactoryAware {
|
||||
|
||||
private final Driver driver;
|
||||
|
||||
@Nullable
|
||||
private NodeManagerFactory nodeManagerFactory;
|
||||
|
||||
@API(status = API.Status.STABLE, since = "1.0")
|
||||
public Neo4jTransactionManager(Driver driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
try {
|
||||
this.setNodeManagerFactory(beanFactory.getBean(NodeManagerFactory.class));
|
||||
} catch (NoSuchBeanDefinitionException ex) {
|
||||
log.warn("Found no instance of {}", NodeManagerFactory.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNodeManagerFactory(@Nullable NodeManagerFactory nodeManagerFactory) {
|
||||
|
||||
// TODO Check if the NodeManager uses the same datasource aka driver as we do
|
||||
this.nodeManagerFactory = nodeManagerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction() throws TransactionException {
|
||||
|
||||
Neo4jResourceHolder resourceHolder = (Neo4jResourceHolder) TransactionSynchronizationManager
|
||||
Neo4jConnectionHolder resourceHolder = (Neo4jConnectionHolder) TransactionSynchronizationManager
|
||||
.getResource(driver);
|
||||
return new Neo4jTransactionObject(resourceHolder);
|
||||
}
|
||||
@@ -83,13 +112,21 @@ public class Neo4jTransactionManager extends AbstractPlatformTransactionManager
|
||||
try {
|
||||
Session session = this.driver.session(accessMode, bookmarks);
|
||||
|
||||
Neo4jResourceHolder resourceHolder = new Neo4jResourceHolder(session, transactionConfig);
|
||||
transactionObject.setResourceHolder(resourceHolder);
|
||||
Neo4jConnectionHolder connectionHolder = new Neo4jConnectionHolder(session, transactionConfig);
|
||||
connectionHolder.setSynchronizedWithTransaction(true);
|
||||
transactionObject.setResourceHolder(connectionHolder);
|
||||
TransactionSynchronizationManager.bindResource(this.driver, connectionHolder);
|
||||
|
||||
resourceHolder.setSynchronizedWithTransaction(true);
|
||||
TransactionSynchronizationManager.bindResource(driver, resourceHolder);
|
||||
if (this.nodeManagerFactory != null) {
|
||||
NodeManagerHolder nodeManagerHolder = new NodeManagerHolder(this.nodeManagerFactory.createNodeManager());
|
||||
nodeManagerHolder.setSynchronizedWithTransaction(true);
|
||||
transactionObject.setNodeManagerHolder(nodeManagerHolder);
|
||||
TransactionSynchronizationManager.bindResource(this.nodeManagerFactory, nodeManagerHolder);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
throw new TransactionSystemException(String.format("Could not open a new Neo4j session: %s", ex.getMessage()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,8 +171,14 @@ public class Neo4jTransactionManager extends AbstractPlatformTransactionManager
|
||||
|
||||
Neo4jTransactionObject transactionObject = extractNeo4jTransaction(transaction);
|
||||
transactionObject.getRequiredResourceHolder().close();
|
||||
transactionObject.getNodeManagerHolder()
|
||||
.map(NodeManagerHolder::getNodeManager)
|
||||
.ifPresent(nodeManager -> nodeManager.flush());
|
||||
|
||||
TransactionSynchronizationManager.unbindResource(driver);
|
||||
if (this.nodeManagerFactory != null) {
|
||||
TransactionSynchronizationManager.unbindResource(this.nodeManagerFactory);
|
||||
}
|
||||
}
|
||||
|
||||
private static TransactionConfig createTransactionConfigFrom(TransactionDefinition definition) {
|
||||
@@ -171,16 +214,21 @@ public class Neo4jTransactionManager extends AbstractPlatformTransactionManager
|
||||
return extractNeo4jTransaction(status.getTransaction());
|
||||
}
|
||||
|
||||
|
||||
static class Neo4jTransactionObject implements SmartTransactionObject {
|
||||
|
||||
private static final String RESOURCE_HOLDER_NOT_PRESENT_MESSAGE = "Neo4jResourceHolder is required but not present. o_O";
|
||||
private static final String RESOURCE_HOLDER_NOT_PRESENT_MESSAGE = "Neo4jConnectionHolder is required but not present. o_O";
|
||||
|
||||
// The resource holder is null when the call to TransactionSynchronizationManager.getResource
|
||||
// in Neo4jTransactionManager.doGetTransaction didn't return a corresponding resource holder.
|
||||
// If it is null, there's no existing session / transaction.
|
||||
private @Nullable Neo4jResourceHolder resourceHolder;
|
||||
@Nullable
|
||||
private Neo4jConnectionHolder resourceHolder;
|
||||
|
||||
Neo4jTransactionObject(@Nullable Neo4jResourceHolder resourceHolder) {
|
||||
@Nullable
|
||||
private NodeManagerHolder nodeManagerHolder;
|
||||
|
||||
Neo4jTransactionObject(@Nullable Neo4jConnectionHolder resourceHolder) {
|
||||
this.resourceHolder = resourceHolder;
|
||||
}
|
||||
|
||||
@@ -190,23 +238,31 @@ public class Neo4jTransactionManager extends AbstractPlatformTransactionManager
|
||||
*
|
||||
* @param resourceHolder A newly created resource holder with a fresh drivers session,
|
||||
*/
|
||||
void setResourceHolder(@Nullable Neo4jResourceHolder resourceHolder) {
|
||||
void setResourceHolder(@Nullable Neo4jConnectionHolder resourceHolder) {
|
||||
this.resourceHolder = resourceHolder;
|
||||
}
|
||||
|
||||
void setNodeManagerHolder(@Nullable NodeManagerHolder nodeManagerHolder) {
|
||||
this.nodeManagerHolder = nodeManagerHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if a {@link Neo4jResourceHolder} is set.
|
||||
* @return {@literal true} if a {@link Neo4jConnectionHolder} is set.
|
||||
*/
|
||||
boolean hasResourceHolder() {
|
||||
return resourceHolder != null;
|
||||
}
|
||||
|
||||
Neo4jResourceHolder getRequiredResourceHolder() {
|
||||
Neo4jConnectionHolder getRequiredResourceHolder() {
|
||||
|
||||
Assert.state(hasResourceHolder(), RESOURCE_HOLDER_NOT_PRESENT_MESSAGE);
|
||||
return resourceHolder;
|
||||
}
|
||||
|
||||
Optional<NodeManagerHolder> getNodeManagerHolder() {
|
||||
return Optional.ofNullable(nodeManagerHolder);
|
||||
}
|
||||
|
||||
void setRollbackOnly() {
|
||||
|
||||
getRequiredResourceHolder().setRollbackOnly();
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.core.transaction;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.neo4j.driver.v1.AccessMode;
|
||||
import org.neo4j.driver.v1.Driver;
|
||||
import org.neo4j.driver.v1.StatementRunner;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.neo4j.core.NodeManagerFactory;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
public final class Neo4jTransactionUtils {
|
||||
|
||||
public static StatementRunner retrieveTransactionalStatementRunner(Driver driver) {
|
||||
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
return driver.session();
|
||||
}
|
||||
|
||||
// Try existing transaction
|
||||
Neo4jConnectionHolder connectionHolder = (Neo4jConnectionHolder) TransactionSynchronizationManager
|
||||
.getResource(driver);
|
||||
|
||||
if (connectionHolder != null) {
|
||||
|
||||
return connectionHolder.getTransaction();
|
||||
}
|
||||
|
||||
// Manually create a new synchronization
|
||||
connectionHolder = new Neo4jConnectionHolder(driver.session(AccessMode.WRITE, Collections.emptyList()));
|
||||
connectionHolder.setSynchronizedWithTransaction(true);
|
||||
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new Neo4jSessionSynchronization(connectionHolder, driver));
|
||||
|
||||
TransactionSynchronizationManager.bindResource(driver, connectionHolder);
|
||||
|
||||
return connectionHolder.getTransaction();
|
||||
}
|
||||
|
||||
public static NodeManager retrieveTransactionalNodeManager(NodeManagerFactory nodeManagerFactory) {
|
||||
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
return nodeManagerFactory.createNodeManager();
|
||||
}
|
||||
|
||||
// Try existing transaction
|
||||
NodeManagerHolder nodeManagerHolder = (NodeManagerHolder) TransactionSynchronizationManager
|
||||
.getResource(nodeManagerFactory);
|
||||
|
||||
if (nodeManagerHolder != null) {
|
||||
|
||||
return nodeManagerHolder.getNodeManager();
|
||||
}
|
||||
|
||||
// Manually create a new synchronization
|
||||
nodeManagerHolder = new NodeManagerHolder(nodeManagerFactory.createNodeManager());
|
||||
nodeManagerHolder.setSynchronizedWithTransaction(true);
|
||||
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new NodeManagerSynchronization(nodeManagerHolder, nodeManagerFactory));
|
||||
|
||||
TransactionSynchronizationManager.bindResource(nodeManagerFactory, nodeManagerHolder);
|
||||
|
||||
return nodeManagerHolder.getNodeManager();
|
||||
}
|
||||
|
||||
private Neo4jTransactionUtils() {
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,30 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.core.transaction;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.support.ResourceHolderSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Used for obtaining {@link org.neo4j.driver.v1.Session session bound (standard or reactive)} bound statement runners.
|
||||
* Dedicated holder for storing a NodeManager inside a transaction.
|
||||
* <p>
|
||||
* <strong>Note:</strong> Intended for internal usage only.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface StatementRunnerSupplier<T> extends Supplier<T> {
|
||||
public class NodeManagerHolder extends ResourceHolderSupport {
|
||||
|
||||
@Nullable
|
||||
private final NodeManager nodeManager;
|
||||
|
||||
public NodeManagerHolder(@Nullable NodeManager nodeManager) {
|
||||
this.nodeManager = nodeManager;
|
||||
}
|
||||
|
||||
public NodeManager getNodeManager() {
|
||||
Assert.state(this.nodeManager != null, "No NodeManager available");
|
||||
return this.nodeManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.core.transaction;
|
||||
|
||||
import org.springframework.data.neo4j.core.NodeManagerFactory;
|
||||
import org.springframework.transaction.support.ResourceHolderSynchronization;
|
||||
|
||||
/**
|
||||
* TODO Update Licence header https
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class NodeManagerSynchronization
|
||||
extends ResourceHolderSynchronization<NodeManagerHolder, Object> {
|
||||
|
||||
private final NodeManagerHolder localNodeManagerHolder;
|
||||
|
||||
NodeManagerSynchronization(NodeManagerHolder nodeManagerHolder, NodeManagerFactory nodeManagerFactory) {
|
||||
|
||||
super(nodeManagerHolder, nodeManagerFactory);
|
||||
this.localNodeManagerHolder = nodeManagerHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void flushResource(NodeManagerHolder resourceHolder) {
|
||||
resourceHolder.getNodeManager().flush();
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.data.neo4j.core.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@@ -78,9 +78,9 @@ public @interface EnableNeo4jRepositories {
|
||||
Class<?> repositoryFactoryBeanClass() default Neo4jRepositoryFactoryBean.class;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link Neo4jTemplate} bean to be used with the repositories detected.
|
||||
* Configures the name of the {@link NodeManager} bean to be used with the repositories detected.
|
||||
*/
|
||||
String neo4jTemplateRef() default DEFAULT_NEO4J_TEMPLATE_BEAN_NAME;
|
||||
String nodeManagerFactoryRef() default DEFAULT_NODE_MANAGER_FACTORY_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
|
||||
|
||||
@@ -24,9 +24,12 @@ import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean;
|
||||
import org.springframework.data.neo4j.repository.support.NodeManagerFactoryBean;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationSource;
|
||||
|
||||
@@ -48,9 +51,14 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
*/
|
||||
static final String GENERATE_BEAN_NAME = "(generated)";
|
||||
|
||||
static final String DEFAULT_NEO4J_TEMPLATE_BEAN_NAME = "neo4jTemplate";
|
||||
static final String DEFAULT_NODE_MANAGER_FACTORY_BEAN_NAME = "nodeManagerFactory";
|
||||
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
|
||||
/**
|
||||
* Holds the name of the shared NodeManagerBean created from the factory with the configured name.
|
||||
*/
|
||||
private String generatedNodeManagerBeanName;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.config14.RepositoryConfigurationExtension#getRepositoryFactoryBeanClassName()
|
||||
@@ -89,7 +97,57 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati
|
||||
builder.addPropertyValue("transactionManager",
|
||||
source.getAttribute("transactionManagerRef").orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
|
||||
|
||||
builder.addPropertyReference("neo4jOperations",
|
||||
source.getAttribute("neo4jTemplateRef").orElse(DEFAULT_NEO4J_TEMPLATE_BEAN_NAME));
|
||||
builder.addPropertyReference("nodeManager", this.generatedNodeManagerBeanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeansForRoot(BeanDefinitionRegistry registry,
|
||||
RepositoryConfigurationSource config) {
|
||||
|
||||
// Mapping context
|
||||
AbstractBeanDefinition neo4jMappingContextBeanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Neo4jMappingContext.class)
|
||||
.getBeanDefinition();
|
||||
String nameOfMappingContextBean = registerWithSourceAndGeneratedBeanName(
|
||||
neo4jMappingContextBeanDefinition, registry, config);
|
||||
|
||||
// Augmented node manager factory (creating injectable, shared instances of NodeManager)
|
||||
String nameOfNodeManagerFactory = config.getAttribute("nodeManagerFactoryRef")
|
||||
.orElse(DEFAULT_NODE_MANAGER_FACTORY_BEAN_NAME);
|
||||
AbstractBeanDefinition sharedSessionCreatorBeanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(NodeManagerFactoryBean.class)
|
||||
.addConstructorArgReference(nameOfNodeManagerFactory)
|
||||
.addConstructorArgReference(nameOfMappingContextBean)
|
||||
.getBeanDefinition();
|
||||
this.generatedNodeManagerBeanName = registerWithSourceAndGeneratedBeanName(
|
||||
sharedSessionCreatorBeanDefinition, registry, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses a generated bean name if {@code configuredBeanName} is equal to {@link #GENERATE_BEAN_NAME}, otherwise uses
|
||||
* the configured bean name to register the new bean. Does not check if there's already a bean under the configured
|
||||
* name but throws a {@link org.springframework.beans.factory.BeanDefinitionStoreException}. Checks whether a bean is
|
||||
* already registered under {@code configuredBeanName} in the given {@link BeanDefinitionRegistry} and uses a
|
||||
* generated name for registering the bean instead. If not, the suggested bean name is used.
|
||||
*
|
||||
* @param bean must not be {@literal null}.
|
||||
* @param registry must not be {@literal null}.
|
||||
* @param configuredBeanName must not be {@literal null} or empty.
|
||||
* @param source must not be {@literal null}.
|
||||
* @return the bean name used for registering the given {@link AbstractBeanDefinition}
|
||||
* @throws org.springframework.beans.factory.BeanDefinitionStoreException if the BeanDefinition is invalid or if there
|
||||
* is already a BeanDefinition for the specified bean name * (and we are not allowed to override it)
|
||||
*/
|
||||
private static String registerWithGeneratedNameOrUseConfigured(AbstractBeanDefinition bean,
|
||||
BeanDefinitionRegistry registry, String configuredBeanName, Object source) {
|
||||
|
||||
String registeredBeanName = configuredBeanName;
|
||||
if (GENERATE_BEAN_NAME.equals(configuredBeanName)) {
|
||||
registeredBeanName = registerWithSourceAndGeneratedBeanName(bean, registry, source);
|
||||
} else {
|
||||
bean.setSource(source);
|
||||
registry.registerBeanDefinition(configuredBeanName, bean);
|
||||
}
|
||||
return registeredBeanName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import org.springframework.data.neo4j.core.Neo4jOperations;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
@@ -26,10 +26,11 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
* Implementation of {@link RepositoryQuery} for derived finder methods.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
**/
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class PartTreeNeo4jQuery extends AbstractNeo4jQuery {
|
||||
|
||||
public PartTreeNeo4jQuery(Neo4jQueryMethod queryMethod, Neo4jOperations neo4jOperations) {
|
||||
public PartTreeNeo4jQuery(Neo4jQueryMethod queryMethod, NodeManager nodeManager) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import org.springframework.data.neo4j.core.Neo4jOperations;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
@@ -26,21 +26,22 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
* Implementation of {@link RepositoryQuery} for String based custom Cypher query.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class StringBasedNeo4jQuery extends AbstractNeo4jQuery {
|
||||
|
||||
private final Neo4jQueryMethod queryMethod;
|
||||
private final Neo4jOperations neo4jOperations;
|
||||
private final NodeManager nodeManager;
|
||||
|
||||
public StringBasedNeo4jQuery(Neo4jQueryMethod queryMethod, Neo4jOperations neo4jOperations) {
|
||||
public StringBasedNeo4jQuery(Neo4jQueryMethod queryMethod, NodeManager nodeManager) {
|
||||
|
||||
this.queryMethod = queryMethod;
|
||||
this.neo4jOperations = neo4jOperations;
|
||||
this.nodeManager = nodeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Object[] parameters) {
|
||||
return neo4jOperations.executeQuery(queryMethod.getAnnotatedQuery());
|
||||
return nodeManager.executeQuery(queryMethod.getAnnotatedQuery());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,7 +21,7 @@ package org.springframework.data.neo4j.repository.support;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.neo4j.core.Neo4jOperations;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.data.neo4j.repository.query.Neo4jQueryMethod;
|
||||
import org.springframework.data.neo4j.repository.query.PartTreeNeo4jQuery;
|
||||
@@ -41,13 +41,14 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
* Factory to create {@link Neo4jRepository} instances.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class Neo4jRepositoryFactory extends RepositoryFactorySupport {
|
||||
final class Neo4jRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final Neo4jOperations neo4jOperations;
|
||||
private final NodeManager nodeManager;
|
||||
|
||||
Neo4jRepositoryFactory(Neo4jOperations neo4jOperations) {
|
||||
this.neo4jOperations = neo4jOperations;
|
||||
Neo4jRepositoryFactory(NodeManager nodeManager) {
|
||||
this.nodeManager = nodeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,7 +58,7 @@ class Neo4jRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
@Override
|
||||
protected Object getTargetRepository(RepositoryInformation metadata) {
|
||||
return getTargetRepositoryViaReflection(metadata, neo4jOperations);
|
||||
return getTargetRepositoryViaReflection(metadata, nodeManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,18 +74,18 @@ class Neo4jRepositoryFactory extends RepositoryFactorySupport {
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
return Optional.of(new Neo4jQueryLookupStrategy(neo4jOperations, evaluationContextProvider));
|
||||
return Optional.of(new Neo4jQueryLookupStrategy(nodeManager, evaluationContextProvider));
|
||||
}
|
||||
|
||||
private class Neo4jQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final Neo4jOperations neo4jOperations;
|
||||
private final NodeManager nodeManager;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
private Neo4jQueryLookupStrategy(Neo4jOperations neo4jOperations,
|
||||
private Neo4jQueryLookupStrategy(NodeManager nodeManager,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
this.neo4jOperations = neo4jOperations;
|
||||
this.nodeManager = nodeManager;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
}
|
||||
|
||||
@@ -97,10 +98,10 @@ class Neo4jRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
Neo4jQueryMethod queryMethod = new Neo4jQueryMethod(method, metadata, factory);
|
||||
if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new StringBasedNeo4jQuery(queryMethod, neo4jOperations);
|
||||
return new StringBasedNeo4jQuery(queryMethod, nodeManager);
|
||||
}
|
||||
|
||||
return new PartTreeNeo4jQuery(queryMethod, neo4jOperations);
|
||||
return new PartTreeNeo4jQuery(queryMethod, nodeManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.data.neo4j.core.Neo4jOperations;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
|
||||
@@ -35,7 +35,7 @@ import org.springframework.data.repository.core.support.TransactionalRepositoryF
|
||||
public class Neo4jRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
|
||||
extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
private Neo4jOperations neo4jOperations;
|
||||
private NodeManager nodeManager;
|
||||
|
||||
/**
|
||||
* Creates a new {@link TransactionalRepositoryFactoryBeanSupport} for the given repository interface.
|
||||
@@ -46,13 +46,13 @@ public class Neo4jRepositoryFactoryBean<T extends Repository<S, ID>, S, ID exten
|
||||
super(repositoryInterface);
|
||||
}
|
||||
|
||||
public void setNeo4jOperations(Neo4jOperations neo4jOperations) {
|
||||
this.neo4jOperations = neo4jOperations;
|
||||
public void setNodeManager(NodeManager nodeManager) {
|
||||
this.nodeManager = nodeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryFactorySupport doCreateRepositoryFactory() {
|
||||
return new Neo4jRepositoryFactory(neo4jOperations);
|
||||
return new Neo4jRepositoryFactory(nodeManager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import org.apiguardian.api.API;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.neo4j.core.NodeManagerFactory;
|
||||
import org.springframework.data.neo4j.core.mapping.MappingContextBasedScannerImpl;
|
||||
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
|
||||
|
||||
/**
|
||||
* This is a shim that integrates a {@link NodeManagerFactory} with Spring Datas infrastructure. It takes in the factory
|
||||
* that needs to be provided by the user and the mapping context provided by our infrastructure. The node manager factory
|
||||
* is than augment by our {@link Neo4jTransactionUtils} so that it doesn't use unmanaged native transaction, but managed
|
||||
* native transaction. Furthermore, the default noop scanner is replaced by a scanner based on our Neo4j mapping context.
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@API(status = API.Status.INTERNAL, since = "1.0")
|
||||
public final class NodeManagerFactoryBean implements InitializingBean, FactoryBean<NodeManager> {
|
||||
private final NodeManagerFactory target;
|
||||
|
||||
private final Neo4jMappingContext neo4jMappingContext;
|
||||
|
||||
public NodeManagerFactoryBean(NodeManagerFactory target, Neo4jMappingContext neo4jMappingContext) {
|
||||
|
||||
this.target = target;
|
||||
this.neo4jMappingContext = neo4jMappingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeManager getObject() {
|
||||
return SharedNodeManagerCreator.createSharedNodeManager(this.target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return NodeManager.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
target.setStatementRunnerProvider(Neo4jTransactionUtils::retrieveTransactionalStatementRunner);
|
||||
target.setScanner(new MappingContextBasedScannerImpl(this.neo4jMappingContext));
|
||||
target.initialize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [http://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.neo4j.repository.support;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.neo4j.core.NodeManagerFactory;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Slf4j
|
||||
final class SharedNodeManagerCreator {
|
||||
|
||||
private static final Set<String> TRANSACTION_REQUIRING_METHODS;
|
||||
|
||||
static {
|
||||
Set<String> tmp = new HashSet<>();
|
||||
|
||||
tmp.add("save");
|
||||
tmp.add("delete");
|
||||
|
||||
TRANSACTION_REQUIRING_METHODS = Collections.unmodifiableSet(tmp);
|
||||
}
|
||||
|
||||
public static NodeManager createSharedNodeManager(NodeManagerFactory nodeManagerFactory) {
|
||||
|
||||
return (NodeManager) Proxy.newProxyInstance(SharedNodeManagerCreator.class.getClassLoader(),
|
||||
new Class<?>[] { NodeManager.class }, new SharedNodeManagerInvocationHandler(nodeManagerFactory));
|
||||
}
|
||||
|
||||
private static class SharedNodeManagerInvocationHandler implements InvocationHandler, Serializable {
|
||||
|
||||
private final NodeManagerFactory targetFactory;
|
||||
|
||||
SharedNodeManagerInvocationHandler(NodeManagerFactory targetFactory) {
|
||||
this.targetFactory = targetFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
||||
String methodName = method.getName();
|
||||
switch (methodName) {
|
||||
case "equals":
|
||||
// Only consider equal when proxies are identical.
|
||||
return (proxy == args[0]);
|
||||
case "hashCode":
|
||||
// Use hashCode of proxy.
|
||||
return hashCode();
|
||||
case "toString":
|
||||
// Deliver toString without touching a target Session.
|
||||
return "Shared Session proxy for target factory [" + this.targetFactory + "]";
|
||||
default:
|
||||
Function<NodeManager, Object> methodCall =
|
||||
targetSession -> ReflectionUtils.invokeMethod(method, targetSession, args);
|
||||
return invokeInTransaction(methodName, methodCall);
|
||||
}
|
||||
}
|
||||
|
||||
private Object invokeInTransaction(String methodName, Function<NodeManager, Object> methodCall) {
|
||||
|
||||
// Determine current Session: either the transactional one
|
||||
// managed by the factory or a temporary one for the given invocation.
|
||||
NodeManager target = Neo4jTransactionUtils.retrieveTransactionalNodeManager(this.targetFactory);
|
||||
|
||||
if (TRANSACTION_REQUIRING_METHODS.contains(methodName)) {
|
||||
if (target == null || (!TransactionSynchronizationManager.isActualTransactionActive()
|
||||
&& target.getTransaction() != null)) {
|
||||
throw new IllegalStateException("No NodeManager with actual transaction available "
|
||||
+ "for current thread - cannot reliably process '" + methodName + "' call");
|
||||
}
|
||||
}
|
||||
|
||||
// Regular Session operations.
|
||||
boolean isNewSession = false;
|
||||
if (target == null) {
|
||||
log.debug("Creating new Session for shared Session invocation");
|
||||
target = this.targetFactory.createNodeManager();
|
||||
isNewSession = true;
|
||||
}
|
||||
|
||||
// Invoke method on current Session.
|
||||
try {
|
||||
return methodCall.apply(target);
|
||||
} finally {
|
||||
if (isNewSession) {
|
||||
target.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SharedNodeManagerCreator() {
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import org.springframework.data.domain.Example;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.core.Neo4jOperations;
|
||||
import org.springframework.data.neo4j.core.NodeManager;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -39,10 +39,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(readOnly = true)
|
||||
class SimpleNeo4jRepository<T, ID> implements Neo4jRepository<T, ID> {
|
||||
|
||||
private final Neo4jOperations neo4jOperations;
|
||||
private final NodeManager nodeManager;
|
||||
|
||||
SimpleNeo4jRepository(Neo4jOperations neo4jOperations) {
|
||||
this.neo4jOperations = neo4jOperations;
|
||||
SimpleNeo4jRepository(NodeManager nodeManager) {
|
||||
this.nodeManager = nodeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.data.neo4j.core.schema.Node;
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public class Neo4jPersistentEntityImplTest {
|
||||
public class DefaultNeo4JPersistentEntityTest {
|
||||
|
||||
private Neo4jMappingContext mappingContext = new Neo4jMappingContext();
|
||||
|
||||
@@ -49,7 +49,7 @@ class MappingContextBasedScannerImplTest {
|
||||
neo4jMappingContext.initialize();
|
||||
|
||||
final Scanner scanner = new MappingContextBasedScannerImpl(neo4jMappingContext);
|
||||
Schema schema = scanner.scan();
|
||||
Schema schema = scanner.scan(new HashSet<>(Arrays.asList(BikeNode.class, UserNode.class)));
|
||||
|
||||
Optional<NodeDescription> optionalUserNodeDescription = schema.getNodeDescription("User");
|
||||
assertThat(optionalUserNodeDescription)
|
||||
|
||||
@@ -54,7 +54,7 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
class Neo4jTransactionUtilsTest {
|
||||
|
||||
@Mock
|
||||
private Driver driver;
|
||||
@@ -90,11 +90,8 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
|
||||
@Test
|
||||
void shouldWorkWithoutSynchronizations() {
|
||||
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
|
||||
driver);
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
StatementRunner statementRunner = statementRunnerSupplier.get();
|
||||
StatementRunner statementRunner = Neo4jTransactionUtils.retrieveTransactionalStatementRunner(driver);
|
||||
|
||||
verify(driver).session();
|
||||
verifyNoMoreInteractions(driver, session, transaction);
|
||||
@@ -107,8 +104,6 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
|
||||
Neo4jTransactionManager txManager = new Neo4jTransactionManager(driver);
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
|
||||
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
|
||||
driver);
|
||||
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@@ -120,7 +115,8 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
assertThat(TransactionSynchronizationManager.hasResource(driver)).isTrue();
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
StatementRunner statementRunner = statementRunnerSupplier.get();
|
||||
StatementRunner statementRunner = Neo4jTransactionUtils
|
||||
.retrieveTransactionalStatementRunner(driver);
|
||||
|
||||
transactionStatus.setRollbackOnly();
|
||||
}
|
||||
@@ -142,8 +138,6 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
|
||||
Neo4jTransactionManager txManager = new Neo4jTransactionManager(driver);
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
|
||||
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
|
||||
driver);
|
||||
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@@ -151,7 +145,8 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
protected void doInTransactionWithoutResult(TransactionStatus outerStatus) {
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
StatementRunner outerStatementRunner = statementRunnerSupplier.get();
|
||||
StatementRunner outerStatementRunner = Neo4jTransactionUtils
|
||||
.retrieveTransactionalStatementRunner(driver);
|
||||
assertThat(outerStatus.isNewTransaction()).isTrue();
|
||||
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
@@ -162,7 +157,8 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
assertThat(innerStatus.isNewTransaction()).isFalse();
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
StatementRunner innerStatementRunner = statementRunnerSupplier.get();
|
||||
StatementRunner innerStatementRunner = Neo4jTransactionUtils
|
||||
.retrieveTransactionalStatementRunner(driver);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -193,8 +189,6 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
|
||||
JtaTransactionManager txManager = new JtaTransactionManager(userTransaction);
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
|
||||
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
|
||||
driver);
|
||||
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@@ -206,7 +200,8 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
assertThat(TransactionSynchronizationManager.hasResource(driver)).isFalse();
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
StatementRunner statementRunner = statementRunnerSupplier.get();
|
||||
StatementRunner statementRunner = Neo4jTransactionUtils
|
||||
.retrieveTransactionalStatementRunner(driver);
|
||||
|
||||
assertThat(TransactionSynchronizationManager.hasResource(driver)).isTrue();
|
||||
}
|
||||
@@ -233,8 +228,6 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
|
||||
JtaTransactionManager txManager = new JtaTransactionManager(userTransaction);
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
|
||||
StatementRunnerSupplier<StatementRunner> statementRunnerSupplier = new DefaultNeo4jStatementRunnerSupplier(
|
||||
driver);
|
||||
|
||||
txTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@@ -246,7 +239,8 @@ class DefaultNeo4jStatementRunnerSupplierTest {
|
||||
assertThat(TransactionSynchronizationManager.hasResource(driver)).isFalse();
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
StatementRunner statementRunner = statementRunnerSupplier.get();
|
||||
StatementRunner statementRunner = Neo4jTransactionUtils
|
||||
.retrieveTransactionalStatementRunner(driver);
|
||||
|
||||
assertThat(TransactionSynchronizationManager.hasResource(driver)).isTrue();
|
||||
|
||||
@@ -23,9 +23,11 @@ import java.util.List;
|
||||
import org.neo4j.driver.v1.Record;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.data.neo4j.repository.query.Query;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
public interface PersonRepository extends Neo4jRepository<Person, Long> {
|
||||
|
||||
@Transactional
|
||||
@Query("RETURN 1")
|
||||
List<Record> customQuery();
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@ import org.neo4j.driver.v1.Record;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.neo4j.core.Neo4jOperations;
|
||||
import org.springframework.data.neo4j.core.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.core.NodeManagerFactory;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
|
||||
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -90,15 +89,15 @@ class RepositoryIT {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager() {
|
||||
public NodeManagerFactory nodeManagerFactory(Driver driver) {
|
||||
|
||||
return new Neo4jTransactionManager(driver());
|
||||
return new NodeManagerFactory(driver, Person.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Neo4jOperations neo4jTemplate() {
|
||||
public PlatformTransactionManager transactionManager(Driver driver) {
|
||||
|
||||
return new Neo4jTemplate(driver());
|
||||
return new Neo4jTransactionManager(driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user