GH-2888 - Respect multiple Neo4jTransactionManagers.

There might be different transaction managers in place for different
Neo4jTemplates.

The change is also applied to the ReactiveNeo4jTemplate.

Co-authored-by: Michael Simons <michael@simons.ac>
This commit is contained in:
Gerrit Meier
2024-04-05 11:51:31 +02:00
parent c02dd5a8c0
commit 0e40d9053c
4 changed files with 71 additions and 16 deletions

View File

@@ -158,8 +158,17 @@ public final class Neo4jTemplate implements
this(neo4jClient, neo4jMappingContext, EntityCallbacks.create());
}
public Neo4jTemplate(Neo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext, PlatformTransactionManager transactionManager) {
this(neo4jClient, neo4jMappingContext, EntityCallbacks.create(), transactionManager);
}
public Neo4jTemplate(Neo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext,
EntityCallbacks entityCallbacks) {
this(neo4jClient, neo4jMappingContext, entityCallbacks, null);
}
public Neo4jTemplate(Neo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext,
EntityCallbacks entityCallbacks, @Nullable PlatformTransactionManager platformTransactionManager) {
Assert.notNull(neo4jClient, "The Neo4jClient is required");
Assert.notNull(neo4jMappingContext, "The Neo4jMappingContext is required");
@@ -170,6 +179,7 @@ public final class Neo4jTemplate implements
this.eventSupport = EventSupport.useExistingCallbacks(neo4jMappingContext, entityCallbacks);
this.renderer = Renderer.getDefaultRenderer();
this.elementIdOrIdFunction = SpringDataCypherDsl.elementIdOrIdFunction.apply(null);
setTransactionManager(platformTransactionManager);
}
ProjectionFactory getProjectionFactory() {
@@ -1102,9 +1112,22 @@ public final class Neo4jTemplate implements
this.elementIdOrIdFunction = SpringDataCypherDsl.elementIdOrIdFunction.apply(cypherDslConfiguration.getDialect());
this.cypherGenerator.setElementIdOrIdFunction(elementIdOrIdFunction);
PlatformTransactionManager transactionManager = beanFactory.getBeanProvider(PlatformTransactionManager.class).getIfUnique(() -> beanFactory.getBean(Neo4jTransactionManager.class));
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.transactionTemplateReadOnly = new TransactionTemplate(transactionManager, readOnlyTransactionDefinition);
if (this.transactionTemplate != null && this.transactionTemplateReadOnly != null) {
return;
}
PlatformTransactionManager transactionManager = null;
var it = beanFactory.getBeanProvider(PlatformTransactionManager.class).stream().iterator();
while (it.hasNext()) {
PlatformTransactionManager transactionManagerCandidate = it.next();
if (transactionManagerCandidate instanceof Neo4jTransactionManager neo4jTransactionManager) {
if (transactionManager != null) {
throw new IllegalStateException("Multiple Neo4jTransactionManagers are defined in this context. " +
"If this in intended, please pass the transaction manager to use with this Neo4jTemplate in the constructor");
}
transactionManager = neo4jTransactionManager;
}
}
setTransactionManager(transactionManager);
}
// only used for the CDI configuration
@@ -1112,10 +1135,12 @@ public final class Neo4jTemplate implements
this.renderer = rendererFromCdiConfiguration;
}
// only used for the CDI configuration
public void setTransactionManager(PlatformTransactionManager platformTransactionManager) {
this.transactionTemplate = new TransactionTemplate(platformTransactionManager);
this.transactionTemplateReadOnly = new TransactionTemplate(platformTransactionManager, readOnlyTransactionDefinition);
public void setTransactionManager(@Nullable PlatformTransactionManager transactionManager) {
if (transactionManager == null) {
return;
}
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.transactionTemplateReadOnly = new TransactionTemplate(transactionManager, readOnlyTransactionDefinition);
}
@Override

View File

@@ -150,9 +150,14 @@ public final class ReactiveNeo4jTemplate implements
private ProjectionFactory projectionFactory;
private Renderer renderer;
private Function<Named, FunctionInvocation> elementIdOrIdFunction;
public ReactiveNeo4jTemplate(ReactiveNeo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext) {
this(neo4jClient, neo4jMappingContext, null);
}
public ReactiveNeo4jTemplate(ReactiveNeo4jClient neo4jClient, Neo4jMappingContext neo4jMappingContext, @Nullable ReactiveTransactionManager transactionManager) {
Assert.notNull(neo4jClient, "The Neo4jClient is required");
Assert.notNull(neo4jMappingContext, "The Neo4jMappingContext is required");
@@ -163,6 +168,7 @@ public final class ReactiveNeo4jTemplate implements
this.eventSupport = ReactiveEventSupport.useExistingCallbacks(neo4jMappingContext, ReactiveEntityCallbacks.create());
this.renderer = Renderer.getDefaultRenderer();
this.elementIdOrIdFunction = SpringDataCypherDsl.elementIdOrIdFunction.apply(null);
setTransactionManager(transactionManager);
}
ProjectionFactory getProjectionFactory() {
@@ -1182,10 +1188,32 @@ public final class ReactiveNeo4jTemplate implements
this.renderer = Renderer.getRenderer(cypherDslConfiguration);
this.elementIdOrIdFunction = SpringDataCypherDsl.elementIdOrIdFunction.apply(cypherDslConfiguration.getDialect());
this.cypherGenerator.setElementIdOrIdFunction(elementIdOrIdFunction);
ReactiveTransactionManager reactiveTransactionManager = beanFactory.getBeanProvider(ReactiveTransactionManager.class)
.getIfUnique(() -> beanFactory.getBean(ReactiveNeo4jTransactionManager.class));
this.transactionalOperatorReadOnly = TransactionalOperator.create(reactiveTransactionManager, readOnlyTransactionDefinition);
if (this.transactionalOperator != null && this.transactionalOperatorReadOnly != null) {
return;
}
ReactiveTransactionManager reactiveTransactionManager = null;
var iter = beanFactory.getBeanProvider(ReactiveTransactionManager.class).stream().iterator();
while (iter.hasNext()) {
ReactiveTransactionManager transactionManagerCandidate = iter.next();
if (transactionManagerCandidate instanceof ReactiveNeo4jTransactionManager reactiveNeo4jTransactionManager) {
if (reactiveTransactionManager != null) {
throw new IllegalStateException("Multiple ReactiveNeo4jTransactionManagers are defined in this context. " +
"If this in intended, please pass the transaction manager to use with this ReactiveNeo4jTemplate in the constructor");
}
reactiveTransactionManager = reactiveNeo4jTransactionManager;
}
}
setTransactionManager(reactiveTransactionManager);
}
private void setTransactionManager(@Nullable ReactiveTransactionManager reactiveTransactionManager) {
if (reactiveTransactionManager == null) {
return;
}
this.transactionalOperator = TransactionalOperator.create(reactiveTransactionManager);
this.transactionalOperatorReadOnly = TransactionalOperator.create(reactiveTransactionManager, readOnlyTransactionDefinition);
}
@Override

View File

@@ -63,9 +63,10 @@ public class Domain1Config {
@Primary @Bean
public Neo4jOperations domain1Template(
@Qualifier("domain1Client") Neo4jClient domain1Client,
@Qualifier("domain1Context") Neo4jMappingContext domain1Context
@Qualifier("domain1Context") Neo4jMappingContext domain1Context,
@Qualifier("domain1Manager") PlatformTransactionManager domain1TransactionManager
) {
return new Neo4jTemplate(domain1Client, domain1Context);
return new Neo4jTemplate(domain1Client, domain1Context, domain1TransactionManager);
}
@Primary @Bean
@@ -78,7 +79,7 @@ public class Domain1Config {
@Primary @Bean
public DatabaseSelectionProvider domain1Selection() {
return () -> DatabaseSelection.undecided();
return DatabaseSelection::undecided;
}
@Primary @Bean

View File

@@ -62,9 +62,10 @@ public class Domain2Config {
@Bean
public Neo4jOperations domain2Template(
@Qualifier("domain2Client") Neo4jClient domain2Client,
@Qualifier("domain2Context") Neo4jMappingContext domain2Context
@Qualifier("domain2Context") Neo4jMappingContext domain2Context,
@Qualifier("domain2Manager") PlatformTransactionManager domain2TransactionManager
) {
return new Neo4jTemplate(domain2Client, domain2Context);
return new Neo4jTemplate(domain2Client, domain2Context, domain2TransactionManager);
}
@Bean
@@ -77,7 +78,7 @@ public class Domain2Config {
@Bean
public DatabaseSelectionProvider domain2Selection() {
return () -> DatabaseSelection.undecided();
return DatabaseSelection::undecided;
}
@Bean