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 c3a32c15b..54a9de4b5 100644 --- a/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java +++ b/src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java @@ -216,7 +216,7 @@ public long count(String cypherQuery, Map parameters) { return transactionTemplateReadOnly.execute(tx -> { PreparedQuery preparedQuery = PreparedQuery.queryFor(Long.class).withCypherQuery(cypherQuery) .withParameters(parameters).build(); - return toExecutableQuery(preparedQuery).getRequiredSingleResult(); + return toQuery(preparedQuery, true).getRequiredSingleResult(); }); } @@ -230,7 +230,8 @@ private List doFindAll(Class domainType, @Nullable Class resultType 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 @@ private List doFindAll(Class domainType, @Nullable Class resultType @Override public List findAll(Statement statement, Class domainType) { return transactionTemplateReadOnly - .execute(tx -> createExecutableQuery(domainType, statement).getResults()); + .execute(tx -> createExecutableQuery(domainType, statement, true).getResults()); } @Override public List findAll(Statement statement, Map parameters, Class domainType) { return transactionTemplateReadOnly - .execute(tx -> createExecutableQuery(domainType, null, statement, parameters).getResults()); + .execute(tx -> createExecutableQuery(domainType, null, statement, parameters, true).getResults()); } @Override public Optional findOne(Statement statement, Map parameters, Class domainType) { return transactionTemplateReadOnly - .execute(tx -> createExecutableQuery(domainType, null, statement, parameters).getSingleResult()); + .execute(tx -> createExecutableQuery(domainType, null, statement, parameters, true).getSingleResult()); } @Override public List findAll(String cypherQuery, Class domainType) { return transactionTemplateReadOnly - .execute(tx -> createExecutableQuery(domainType, cypherQuery).getResults()); + .execute(tx -> createExecutableQuery(domainType, cypherQuery, true).getResults()); } @Override public List findAll(String cypherQuery, Map parameters, Class domainType) { return transactionTemplateReadOnly - .execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters).getResults()); + .execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters, true).getResults()); } @Override public Optional findOne(String cypherQuery, Map parameters, Class 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 @@ List doFind(@Nullable String cypherQuery, @Nullable Map 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 Optional findById(Object id, Class domainType) { return createExecutableQuery(domainType, null, QueryFragmentsAndParameters.forFindById(entityMetaData, - convertIdValues(entityMetaData.getRequiredIdProperty(), id))) + convertIdValues(entityMetaData.getRequiredIdProperty(), id)), + true) .getSingleResult(); }); } @@ -354,7 +357,8 @@ public List findAllById(Iterable ids, Class domainType) { return createExecutableQuery(domainType, null, QueryFragmentsAndParameters.forFindByAllId( - entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), ids))) + entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), ids)), + true) .getResults(); }); } @@ -697,7 +701,7 @@ public void deleteByIdWithVersion(Object id, Class domainType, Neo4jPersi 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 void deleteAll(Class domainType) { }); } - private ExecutableQuery createExecutableQuery(Class domainType, Statement statement) { - return createExecutableQuery(domainType, null, statement, Collections.emptyMap()); + private ExecutableQuery createExecutableQuery(Class domainType, Statement statement, boolean readOnly) { + return createExecutableQuery(domainType, null, statement, Collections.emptyMap(), readOnly); } - private ExecutableQuery createExecutableQuery(Class domainType, String cypherQuery) { - return createExecutableQuery(domainType, null, cypherQuery, Collections.emptyMap()); + private ExecutableQuery createExecutableQuery(Class domainType, String cypherQuery, boolean readOnly) { + return createExecutableQuery(domainType, null, cypherQuery, Collections.emptyMap(), readOnly); } - private ExecutableQuery createExecutableQuery(Class domainType, @Nullable Class resultType, Statement statement, Map parameters) { + private ExecutableQuery createExecutableQuery(Class domainType, @Nullable Class resultType, Statement statement, Map 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 ExecutableQuery createExecutableQuery(Class domainType, @Nullable Class resultType, @Nullable String cypherStatement, - Map parameters) { + private ExecutableQuery createExecutableQuery( + Class domainType, @Nullable Class resultType, + @Nullable String cypherStatement, + Map parameters, + boolean readOnly) { Supplier> mappingFunction = TemplateSupport .getAndDecorateMappingFunction(neo4jMappingContext, domainType, resultType); @@ -768,7 +775,7 @@ private ExecutableQuery createExecutableQuery(Class domainType, @Nulla .usingMappingFunction(mappingFunction) .build(); - return toExecutableQuery(preparedQuery); + return toQuery(preparedQuery, readOnly); } /** @@ -1191,12 +1198,14 @@ public void setTransactionManager(@Nullable PlatformTransactionManager transacti public ExecutableQuery toExecutableQuery(Class domainType, QueryFragmentsAndParameters queryFragmentsAndParameters) { - return createExecutableQuery(domainType, null, queryFragmentsAndParameters); + return createExecutableQuery(domainType, null, queryFragmentsAndParameters, false); } - private ExecutableQuery createExecutableQuery(Class domainType, @Nullable Class resultType, - QueryFragmentsAndParameters queryFragmentsAndParameters) { + private ExecutableQuery createExecutableQuery( + Class domainType, @Nullable Class resultType, + QueryFragmentsAndParameters queryFragmentsAndParameters, + boolean readOnly) { Supplier> mappingFunction = TemplateSupport .getAndDecorateMappingFunction(neo4jMappingContext, domainType, resultType); @@ -1204,13 +1213,16 @@ private ExecutableQuery createExecutableQuery(Class domainType, @Nulla .withQueryFragmentsAndParameters(queryFragmentsAndParameters) .usingMappingFunction(mappingFunction) .build(); - return toExecutableQuery(preparedQuery); + return toQuery(preparedQuery, readOnly); } @Override public ExecutableQuery toExecutableQuery(PreparedQuery preparedQuery) { + return toQuery(preparedQuery, false); + } - return new DefaultExecutableQuery<>(preparedQuery); + private ExecutableQuery toQuery(PreparedQuery preparedQuery, boolean readOnly) { + return new DefaultExecutableQuery<>(preparedQuery, readOnly); } @Override @@ -1255,14 +1267,17 @@ String render(Statement statement) { final class DefaultExecutableQuery implements ExecutableQuery { private final PreparedQuery preparedQuery; + private final TransactionTemplate txTemplate; - DefaultExecutableQuery(PreparedQuery preparedQuery) { + DefaultExecutableQuery(PreparedQuery preparedQuery, boolean readOnly) { this.preparedQuery = preparedQuery; + this.txTemplate = readOnly ? transactionTemplateReadOnly : transactionTemplate; } + @SuppressWarnings("unchecked") public List getResults() { - return transactionTemplate + return txTemplate .execute(tx -> { Collection all = createFetchSpec().map(Neo4jClient.RecordFetchSpec::all).orElse(Collections.emptyList()); if (preparedQuery.resultsHaveBeenAggregated()) { @@ -1274,7 +1289,7 @@ public List getResults() { @SuppressWarnings("unchecked") public Optional getSingleResult() { - return transactionTemplate.execute(tx -> { + return txTemplate.execute(tx -> { try { Optional one = createFetchSpec().flatMap(Neo4jClient.RecordFetchSpec::one); if (preparedQuery.resultsHaveBeenAggregated()) { @@ -1291,7 +1306,7 @@ public Optional getSingleResult() { @SuppressWarnings("unchecked") public T getRequiredSingleResult() { - return transactionTemplate.execute(tx -> { + return txTemplate.execute(tx -> { Optional one = createFetchSpec().flatMap(Neo4jClient.RecordFetchSpec::one); if (preparedQuery.resultsHaveBeenAggregated()) { one = one.map(aggregatedResults -> ((LinkedHashSet) aggregatedResults).iterator().next());