Skip to content

Commit 079dd3c

Browse files
committed
fix: use readonly transaction template where possible, #2953
Signed-off-by: Gerrit Meier <[email protected]>
1 parent 0bf6918 commit 079dd3c

File tree

2 files changed

+49
-32
lines changed

2 files changed

+49
-32
lines changed

src/main/java/org/springframework/data/neo4j/core/Neo4jTemplate.java

+46-31
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public long count(String cypherQuery, Map<String, Object> parameters) {
216216
return transactionTemplateReadOnly.execute(tx -> {
217217
PreparedQuery<Long> preparedQuery = PreparedQuery.queryFor(Long.class).withCypherQuery(cypherQuery)
218218
.withParameters(parameters).build();
219-
return toExecutableQuery(preparedQuery).getRequiredSingleResult();
219+
return toExecutableQuery(preparedQuery, true).getRequiredSingleResult();
220220
});
221221
}
222222

@@ -230,45 +230,46 @@ private <T> List<T> doFindAll(Class<T> domainType, @Nullable Class<?> resultType
230230
return transactionTemplateReadOnly
231231
.execute(tx -> {
232232
Neo4jPersistentEntity<?> entityMetaData = neo4jMappingContext.getRequiredPersistentEntity(domainType);
233-
return createExecutableQuery(domainType, resultType, QueryFragmentsAndParameters.forFindAll(entityMetaData))
233+
return createExecutableQuery(
234+
domainType, resultType, QueryFragmentsAndParameters.forFindAll(entityMetaData), true)
234235
.getResults();
235236
});
236237
}
237238

238239
@Override
239240
public <T> List<T> findAll(Statement statement, Class<T> domainType) {
240241
return transactionTemplateReadOnly
241-
.execute(tx -> createExecutableQuery(domainType, statement).getResults());
242+
.execute(tx -> createExecutableQuery(domainType, statement, true).getResults());
242243
}
243244

244245
@Override
245246
public <T> List<T> findAll(Statement statement, Map<String, Object> parameters, Class<T> domainType) {
246247
return transactionTemplateReadOnly
247-
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters).getResults());
248+
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters, true).getResults());
248249
}
249250

250251
@Override
251252
public <T> Optional<T> findOne(Statement statement, Map<String, Object> parameters, Class<T> domainType) {
252253
return transactionTemplateReadOnly
253-
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters).getSingleResult());
254+
.execute(tx -> createExecutableQuery(domainType, null, statement, parameters, true).getSingleResult());
254255
}
255256

256257
@Override
257258
public <T> List<T> findAll(String cypherQuery, Class<T> domainType) {
258259
return transactionTemplateReadOnly
259-
.execute(tx -> createExecutableQuery(domainType, cypherQuery).getResults());
260+
.execute(tx -> createExecutableQuery(domainType, cypherQuery, true).getResults());
260261
}
261262

262263
@Override
263264
public <T> List<T> findAll(String cypherQuery, Map<String, Object> parameters, Class<T> domainType) {
264265
return transactionTemplateReadOnly
265-
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters).getResults());
266+
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters, true).getResults());
266267
}
267268

268269
@Override
269270
public <T> Optional<T> findOne(String cypherQuery, Map<String, Object> parameters, Class<T> domainType) {
270271
return transactionTemplateReadOnly
271-
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters).getSingleResult());
272+
.execute(tx -> createExecutableQuery(domainType, null, cypherQuery, parameters, true).getSingleResult());
272273
}
273274

274275
@Override
@@ -288,9 +289,10 @@ <T, R> List<R> doFind(@Nullable String cypherQuery, @Nullable Map<String, Object
288289
ExecutableQuery<T> executableQuery;
289290
if (queryFragmentsAndParameters == null) {
290291
executableQuery = createExecutableQuery(domainType, resultType, cypherQuery,
291-
parameters == null ? Collections.emptyMap() : parameters);
292+
parameters == null ? Collections.emptyMap() : parameters,
293+
true);
292294
} else {
293-
executableQuery = createExecutableQuery(domainType, resultType, queryFragmentsAndParameters);
295+
executableQuery = createExecutableQuery(domainType, resultType, queryFragmentsAndParameters, true);
294296
}
295297
intermediaResults = switch (fetchType) {
296298
case ALL -> executableQuery.getResults();
@@ -341,7 +343,8 @@ public <T> Optional<T> findById(Object id, Class<T> domainType) {
341343

342344
return createExecutableQuery(domainType, null,
343345
QueryFragmentsAndParameters.forFindById(entityMetaData,
344-
convertIdValues(entityMetaData.getRequiredIdProperty(), id)))
346+
convertIdValues(entityMetaData.getRequiredIdProperty(), id)),
347+
true)
345348
.getSingleResult();
346349
});
347350
}
@@ -354,7 +357,8 @@ public <T> List<T> findAllById(Iterable<?> ids, Class<T> domainType) {
354357

355358
return createExecutableQuery(domainType, null,
356359
QueryFragmentsAndParameters.forFindByAllId(
357-
entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), ids)))
360+
entityMetaData, convertIdValues(entityMetaData.getRequiredIdProperty(), ids)),
361+
true)
358362
.getResults();
359363
});
360364
}
@@ -697,7 +701,7 @@ public <T> void deleteByIdWithVersion(Object id, Class<T> domainType, Neo4jPersi
697701
parameters.put(nameOfParameter, convertIdValues(entityMetaData.getRequiredIdProperty(), id));
698702
parameters.put(Constants.NAME_OF_VERSION_PARAM, versionValue);
699703

700-
createExecutableQuery(domainType, null, statement, parameters).getSingleResult().orElseThrow(
704+
createExecutableQuery(domainType, null, statement, parameters, false).getSingleResult().orElseThrow(
701705
() -> new OptimisticLockingFailureException(OPTIMISTIC_LOCKING_ERROR_MESSAGE)
702706
);
703707

@@ -744,21 +748,24 @@ public void deleteAll(Class<?> domainType) {
744748
});
745749
}
746750

747-
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, Statement statement) {
748-
return createExecutableQuery(domainType, null, statement, Collections.emptyMap());
751+
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, Statement statement, boolean readOnly) {
752+
return createExecutableQuery(domainType, null, statement, Collections.emptyMap(), readOnly);
749753
}
750754

751-
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, String cypherQuery) {
752-
return createExecutableQuery(domainType, null, cypherQuery, Collections.emptyMap());
755+
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, String cypherQuery, boolean readOnly) {
756+
return createExecutableQuery(domainType, null, cypherQuery, Collections.emptyMap(), readOnly);
753757
}
754758

755-
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType, Statement statement, Map<String, Object> parameters) {
759+
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType, Statement statement, Map<String, Object> parameters, boolean readOnly) {
756760

757-
return createExecutableQuery(domainType, resultType, renderer.render(statement), TemplateSupport.mergeParameters(statement, parameters));
761+
return createExecutableQuery(domainType, resultType, renderer.render(statement), TemplateSupport.mergeParameters(statement, parameters), readOnly);
758762
}
759763

760-
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType, @Nullable String cypherStatement,
761-
Map<String, Object> parameters) {
764+
private <T> ExecutableQuery<T> createExecutableQuery(
765+
Class<T> domainType, @Nullable Class<?> resultType,
766+
@Nullable String cypherStatement,
767+
Map<String, Object> parameters,
768+
boolean readOnly) {
762769

763770
Supplier<BiFunction<TypeSystem, MapAccessor, ?>> mappingFunction = TemplateSupport
764771
.getAndDecorateMappingFunction(neo4jMappingContext, domainType, resultType);
@@ -768,7 +775,7 @@ private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nulla
768775
.usingMappingFunction(mappingFunction)
769776
.build();
770777

771-
return toExecutableQuery(preparedQuery);
778+
return toExecutableQuery(preparedQuery, readOnly);
772779
}
773780

774781
/**
@@ -1191,26 +1198,31 @@ public void setTransactionManager(@Nullable PlatformTransactionManager transacti
11911198
public <T> ExecutableQuery<T> toExecutableQuery(Class<T> domainType,
11921199
QueryFragmentsAndParameters queryFragmentsAndParameters) {
11931200

1194-
return createExecutableQuery(domainType, null, queryFragmentsAndParameters);
1201+
return createExecutableQuery(domainType, null, queryFragmentsAndParameters, false);
11951202
}
11961203

11971204

1198-
private <T> ExecutableQuery<T> createExecutableQuery(Class<T> domainType, @Nullable Class<?> resultType,
1199-
QueryFragmentsAndParameters queryFragmentsAndParameters) {
1205+
private <T> ExecutableQuery<T> createExecutableQuery(
1206+
Class<T> domainType, @Nullable Class<?> resultType,
1207+
QueryFragmentsAndParameters queryFragmentsAndParameters,
1208+
boolean readOnlyTransaction) {
12001209

12011210
Supplier<BiFunction<TypeSystem, MapAccessor, ?>> mappingFunction = TemplateSupport
12021211
.getAndDecorateMappingFunction(neo4jMappingContext, domainType, resultType);
12031212
PreparedQuery<T> preparedQuery = PreparedQuery.queryFor(domainType)
12041213
.withQueryFragmentsAndParameters(queryFragmentsAndParameters)
12051214
.usingMappingFunction(mappingFunction)
12061215
.build();
1207-
return toExecutableQuery(preparedQuery);
1216+
return toExecutableQuery(preparedQuery, readOnlyTransaction);
12081217
}
12091218

12101219
@Override
12111220
public <T> ExecutableQuery<T> toExecutableQuery(PreparedQuery<T> preparedQuery) {
1221+
return toExecutableQuery(preparedQuery, false);
1222+
}
12121223

1213-
return new DefaultExecutableQuery<>(preparedQuery);
1224+
private <T> ExecutableQuery<T> toExecutableQuery(PreparedQuery<T> preparedQuery, boolean readOnly) {
1225+
return new DefaultExecutableQuery<>(preparedQuery, readOnly);
12141226
}
12151227

12161228
@Override
@@ -1255,14 +1267,17 @@ String render(Statement statement) {
12551267
final class DefaultExecutableQuery<T> implements ExecutableQuery<T> {
12561268

12571269
private final PreparedQuery<T> preparedQuery;
1270+
private final TransactionTemplate txTemplate;
12581271

1259-
DefaultExecutableQuery(PreparedQuery<T> preparedQuery) {
1272+
DefaultExecutableQuery(PreparedQuery<T> preparedQuery, boolean readOnly) {
12601273
this.preparedQuery = preparedQuery;
1274+
this.txTemplate = readOnly ? transactionTemplateReadOnly : transactionTemplate;
12611275
}
12621276

1277+
12631278
@SuppressWarnings("unchecked")
12641279
public List<T> getResults() {
1265-
return transactionTemplate
1280+
return txTemplate
12661281
.execute(tx -> {
12671282
Collection<T> all = createFetchSpec().map(Neo4jClient.RecordFetchSpec::all).orElse(Collections.emptyList());
12681283
if (preparedQuery.resultsHaveBeenAggregated()) {
@@ -1274,7 +1289,7 @@ public List<T> getResults() {
12741289

12751290
@SuppressWarnings("unchecked")
12761291
public Optional<T> getSingleResult() {
1277-
return transactionTemplate.execute(tx -> {
1292+
return txTemplate.execute(tx -> {
12781293
try {
12791294
Optional<T> one = createFetchSpec().flatMap(Neo4jClient.RecordFetchSpec::one);
12801295
if (preparedQuery.resultsHaveBeenAggregated()) {
@@ -1291,7 +1306,7 @@ public Optional<T> getSingleResult() {
12911306

12921307
@SuppressWarnings("unchecked")
12931308
public T getRequiredSingleResult() {
1294-
return transactionTemplate.execute(tx -> {
1309+
return txTemplate.execute(tx -> {
12951310
Optional<T> one = createFetchSpec().flatMap(Neo4jClient.RecordFetchSpec::one);
12961311
if (preparedQuery.resultsHaveBeenAggregated()) {
12971312
one = one.map(aggregatedResults -> ((LinkedHashSet<T>) aggregatedResults).iterator().next());

src/test/java/org/springframework/data/neo4j/integration/imperative/Neo4jTemplateIT.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,9 @@ public BookmarkCapture bookmarkCapture() {
10061006
public PlatformTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider) {
10071007

10081008
BookmarkCapture bookmarkCapture = bookmarkCapture();
1009-
return new Neo4jTransactionManager(driver, databaseNameProvider, Neo4jBookmarkManager.create(bookmarkCapture));
1009+
Neo4jTransactionManager transactionManager = new Neo4jTransactionManager(driver, databaseNameProvider, Neo4jBookmarkManager.create(bookmarkCapture));
1010+
transactionManager.setValidateExistingTransaction(true);
1011+
return transactionManager;
10101012
}
10111013

10121014
@Override

0 commit comments

Comments
 (0)