fix: use readonly transaction template where possible, #2953

Signed-off-by: Gerrit Meier <meistermeier@gmail.com>
Signed-off-by: Marcel Körtgen <marcel.koertgen@gmail.com>
This commit is contained in:
Marcel Körtgen
2024-10-18 12:55:11 +02:00
committed by Gerrit Meier
parent 4092c566c0
commit 0bedafb831
2 changed files with 49 additions and 32 deletions

View File

@@ -216,7 +216,7 @@ public final class Neo4jTemplate implements
return transactionTemplateReadOnly.execute(tx -> {
PreparedQuery<Long> preparedQuery = PreparedQuery.queryFor(Long.class).withCypherQuery(cypherQuery)
.withParameters(parameters).build();
return toExecutableQuery(preparedQuery).getRequiredSingleResult();
return toExecutableQuery(preparedQuery, true).getRequiredSingleResult();
});
}
@@ -230,7 +230,8 @@ public final class Neo4jTemplate implements
return transactionTemplateReadOnly
.execute(tx -> {
Neo4jPersistentEntity<?> entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(domainType);
return createExecutableQuery(domainType, resultType, QueryFragmentsAndParameters.forFindAll(entityMetaData))
return createExecutableQuery(
domainType, resultType, QueryFragmentsAndParameters.forFindAll(entityMetaData), true)
.getResults();
});
}
@@ -238,37 +239,37 @@ public final class Neo4jTemplate implements
@Override
public <T> List<T> findAll(Statement statement, Class<T> domainType) {
return transactionTemplateReadOnly
.execute(tx -> createExecutableQuery(domainType, statement).getResults());
.execute(tx -> createExecutableQuery(domainType, statement, true).getResults());
}
@Override
public <T> List<T> findAll(Statement statement, Map<String, Object> parameters, Class<T> domainType) {
return transactionTemplateReadOnly
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters).getResults());
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters, true).getResults());
}
@Override
public <T> Optional<T> findOne(Statement statement, Map<String, Object> parameters, Class<T> domainType) {
return transactionTemplateReadOnly
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters).getSingleResult());
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters, true).getSingleResult());
}
@Override
public <T> List<T> findAll(String cypherQuery, Class<T> domainType) {
return transactionTemplateReadOnly
.execute(tx -> createExecutableQuery(domainType, cypherQuery).getResults());
.execute(tx -> createExecutableQuery(domainType, cypherQuery, true).getResults());
}
@Override
public <T> List<T> findAll(String cypherQuery, Map<String, Object> parameters, Class<T> domainType) {
return transactionTemplateReadOnly
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters).getResults());
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters, true).getResults());
}
@Override
public <T> Optional<T> findOne(String cypherQuery, Map<String, Object> parameters, Class<T> domainType) {
return transactionTemplateReadOnly
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters).getSingleResult());
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters, true).getSingleResult());
}
@Override
@@ -288,9 +289,10 @@ public final class Neo4jTemplate implements
ExecutableQuery<T> executableQuery;
if (queryFragmentsAndParameters == null) {
executableQuery = createExecutableQuery(domainType, resultType, cypherQuery,
parameters == null ? Collections.emptyMap() : parameters);
parameters == null ? Collections.emptyMap() : parameters,
true);
} else {
executableQuery = createExecutableQuery(domainType, resultType, queryFragmentsAndParameters);
executableQuery = createExecutableQuery(domainType, resultType, queryFragmentsAndParameters, true);
}
intermediaResults = switch (fetchType) {
case ALL -> executableQuery.getResults();
@@ -341,7 +343,8 @@ public final class Neo4jTemplate implements
return createExecutableQuery(domainType, null,
QueryFragmentsAndParameters.forFindById(entityMetaData,
convertIdValues(entityMetaData.getRequiredIdProperty(), id)))
convertIdValues(entityMetaData.getRequiredIdProperty(), id)),
true)
.getSingleResult();
});
}
@@ -354,7 +357,8 @@ public final class Neo4jTemplate implements
return createExecutableQuery(domainType, null,
QueryFragmentsAndParameters.forFindByAllId(
entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), ids)))
entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), ids)),
true)
.getResults();
});
}
@@ -697,7 +701,7 @@ public final class Neo4jTemplate implements
parameters.put(nameOfParameter, convertIdValues(entityMetaData.getRequiredIdProperty(), id));
parameters.put(Constants.NAME_OF_VERSION_PARAM, versionValue);
createExecutableQuery(domainType, null, statement, parameters).getSingleResult().orElseThrow(
createExecutableQuery(domainType, null, statement, parameters, false).getSingleResult().orElseThrow(
() -> new OptimisticLockingFailureException(OPTIMISTIC_LOCKING_ERROR_MESSAGE)
);
@@ -744,21 +748,24 @@ public final class Neo4jTemplate implements
});
}
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, Statement statement) {
return createExecutableQuery(domainType, null, statement, Collections.emptyMap());
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, Statement statement, boolean readOnly) {
return createExecutableQuery(domainType, null, statement, Collections.emptyMap(), readOnly);
}
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, String cypherQuery) {
return createExecutableQuery(domainType, null, cypherQuery, Collections.emptyMap());
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, String cypherQuery, boolean readOnly) {
return createExecutableQuery(domainType, null, cypherQuery, Collections.emptyMap(), readOnly);
}
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType, Statement statement, Map<String, Object> parameters) {
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType, Statement statement, Map<String, Object> parameters, boolean readOnly) {
return createExecutableQuery(domainType, resultType, renderer.render(statement), TemplateSupport.mergeParameters(statement, parameters));
return createExecutableQuery(domainType, resultType, renderer.render(statement), TemplateSupport.mergeParameters(statement, parameters), readOnly);
}
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType, @Nullable String cypherStatement,
Map<String, Object> parameters) {
private <T> ExecutableQuery<T> createExecutableQuery(
Class<T> domainType, @Nullable Class<?> resultType,
@Nullable String cypherStatement,
Map<String, Object> parameters,
boolean readOnly) {
Supplier<BiFunction<TypeSystem, MapAccessor, ?>> mappingFunction = TemplateSupport
.getAndDecorateMappingFunction(neo4jMappingContext, domainType, resultType);
@@ -768,7 +775,7 @@ public final class Neo4jTemplate implements
.usingMappingFunction(mappingFunction)
.build();
return toExecutableQuery(preparedQuery);
return toExecutableQuery(preparedQuery, readOnly);
}
/**
@@ -1191,12 +1198,14 @@ public final class Neo4jTemplate implements
public <T> ExecutableQuery<T> toExecutableQuery(Class<T> domainType,
QueryFragmentsAndParameters queryFragmentsAndParameters) {
return createExecutableQuery(domainType, null, queryFragmentsAndParameters);
return createExecutableQuery(domainType, null, queryFragmentsAndParameters, false);
}
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType,
QueryFragmentsAndParameters queryFragmentsAndParameters) {
private <T> ExecutableQuery<T> createExecutableQuery(
Class<T> domainType, @Nullable Class<?> resultType,
QueryFragmentsAndParameters queryFragmentsAndParameters,
boolean readOnlyTransaction) {
Supplier<BiFunction<TypeSystem, MapAccessor, ?>> mappingFunction = TemplateSupport
.getAndDecorateMappingFunction(neo4jMappingContext, domainType, resultType);
@@ -1204,13 +1213,16 @@ public final class Neo4jTemplate implements
.withQueryFragmentsAndParameters(queryFragmentsAndParameters)
.usingMappingFunction(mappingFunction)
.build();
return toExecutableQuery(preparedQuery);
return toExecutableQuery(preparedQuery, readOnlyTransaction);
}
@Override
public <T> ExecutableQuery<T> toExecutableQuery(PreparedQuery<T> preparedQuery) {
return toExecutableQuery(preparedQuery, false);
}
return new DefaultExecutableQuery<>(preparedQuery);
private <T> ExecutableQuery<T> toExecutableQuery(PreparedQuery<T> preparedQuery, boolean readOnly) {
return new DefaultExecutableQuery<>(preparedQuery, readOnly);
}
@Override
@@ -1255,14 +1267,17 @@ public final class Neo4jTemplate implements
final class DefaultExecutableQuery<T> implements ExecutableQuery<T> {
private final PreparedQuery<T> preparedQuery;
private final TransactionTemplate txTemplate;
DefaultExecutableQuery(PreparedQuery<T> preparedQuery) {
DefaultExecutableQuery(PreparedQuery<T> preparedQuery, boolean readOnly) {
this.preparedQuery = preparedQuery;
this.txTemplate = readOnly ? transactionTemplateReadOnly : transactionTemplate;
}
@SuppressWarnings("unchecked")
public List<T> getResults() {
return transactionTemplate
return txTemplate
.execute(tx -> {
Collection<T> all = createFetchSpec().map(Neo4jClient.RecordFetchSpec::all).orElse(Collections.emptyList());
if (preparedQuery.resultsHaveBeenAggregated()) {
@@ -1274,7 +1289,7 @@ public final class Neo4jTemplate implements
@SuppressWarnings("unchecked")
public Optional<T> getSingleResult() {
return transactionTemplate.execute(tx -> {
return txTemplate.execute(tx -> {
try {
Optional<T> one = createFetchSpec().flatMap(Neo4jClient.RecordFetchSpec::one);
if (preparedQuery.resultsHaveBeenAggregated()) {
@@ -1291,7 +1306,7 @@ public final class Neo4jTemplate implements
@SuppressWarnings("unchecked")
public T getRequiredSingleResult() {
return transactionTemplate.execute(tx -> {
return txTemplate.execute(tx -> {
Optional<T> one = createFetchSpec().flatMap(Neo4jClient.RecordFetchSpec::one);
if (preparedQuery.resultsHaveBeenAggregated()) {
one = one.map(aggregatedResults -> ((LinkedHashSet<T>) aggregatedResults).iterator().next());

View File

@@ -1006,7 +1006,9 @@ class Neo4jTemplateIT {
public PlatformTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider) {
BookmarkCapture bookmarkCapture = bookmarkCapture();
return new Neo4jTransactionManager(driver, databaseNameProvider, Neo4jBookmarkManager.create(bookmarkCapture));
Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(driver, databaseNameProvider, Neo4jBookmarkManager.create(bookmarkCapture));
transactionManager.setValidateExistingTransaction(true);
return transactionManager;
}
@Override