Skip to content

Commit ae98285

Browse files
committed
Use getResultList() and remove(…) flow for derived AOT query methods.
We now correctly obtain a list of entities to be deleted and remove those through EntityManager.remove(…). Previously, we've erroneously used executeUpdate(…) that is intended for string-based queries only. Closes #4102
1 parent 858ac1a commit ae98285

File tree

7 files changed

+45
-3
lines changed

7 files changed

+45
-3
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ public CodeBlock build() {
656656
: TypeName.get(context.getDomainType());
657657
builder.add("\n");
658658

659-
if (modifying.isPresent()) {
659+
if (modifying.isPresent() && !(aotQuery instanceof StringAotQuery.DerivedAotQuery)) {
660660

661661
if (modifying.getBoolean("flushAutomatically")) {
662662
builder.addStatement("this.$L.flush()", context.fieldNameOf(EntityManager.class));

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ private Optional<Class<QueryEnhancerSelector>> getQueryEnhancerSelectorClass() {
207207
.metadataOnly(aotQueries.toMetadata(queryMethod.isPageQuery()));
208208
}
209209

210-
if (queryMethod.isModifyingQuery()) {
210+
if (queryMethod.isModifyingQuery() && !(aotQueries.result() instanceof StringAotQuery.DerivedAotQuery)) {
211211

212212
TypeInformation<?> returnType = getRepositoryInformation().getReturnType(method);
213213

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/StringAotQuery.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public String getQueryName() {
173173
*
174174
* @author Mark Paluch
175175
*/
176-
private static class DerivedAotQuery extends StringAotQuery {
176+
static class DerivedAotQuery extends StringAotQuery {
177177

178178
private final String queryString;
179179
private final Limit limit;

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,15 @@ void deleteOneShouldFailWhenMatchingMultipleResults() {
16211621
.isThrownBy(() -> repository.deleteOneByLastname(firstUser.getLastname()));
16221622
}
16231623

1624+
@Test // GH-4102
1625+
void deleteOneModifying() {
1626+
1627+
flushTestUsers();
1628+
1629+
User user = repository.deleteModifyingByLastname(firstUser.getLastname());
1630+
assertThat(user).isEqualTo(firstUser);
1631+
}
1632+
16241633
@Test // DATAJPA-460
16251634
void deleteByShouldRemoveElementsMatchingDerivedQuery() {
16261635

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributorIntegrationTests.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,31 @@ void testDerivedDeleteSingle() {
558558
assertThat(yodaShouldBeGone).isNull();
559559
}
560560

561+
@Test // GH-4102
562+
void testDerivedDeleteSingleWithModifying() {
563+
564+
User user = fragment.deleteByEmailAddressAndIdIsNotNull("[email protected]");
565+
assertThat(user).isNotNull().extracting(User::getEmailAddress).isEqualTo("[email protected]");
566+
567+
Object yodaShouldBeGone = em
568+
.createQuery("SELECT u FROM %s u WHERE u.emailAddress = '[email protected]'".formatted(User.class.getName()))
569+
.getSingleResultOrNull();
570+
assertThat(yodaShouldBeGone).isNull();
571+
}
572+
573+
@Test // GH-4102
574+
void testDerivedDeleteAndReturnCount() {
575+
576+
int count = fragment.deleteAndReturnCountByEmailAddress("[email protected]");
577+
578+
assertThat(count).isEqualTo(1);
579+
580+
Object yodaShouldBeGone = em
581+
.createQuery("SELECT u FROM %s u WHERE u.emailAddress = '[email protected]'".formatted(User.class.getName()))
582+
.getSingleResultOrNull();
583+
assertThat(yodaShouldBeGone).isNull();
584+
}
585+
561586
@Test // GH-3830
562587
void shouldReturnStreamableDelete() {
563588

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ List<User> findWithParameterNameByLastnameStartingWithOrLastnameEndingWith(@Para
205205

206206
User deleteByEmailAddress(String username);
207207

208+
@Modifying
209+
User deleteByEmailAddressAndIdIsNotNull(String email);
210+
211+
int deleteAndReturnCountByEmailAddress(String username);
212+
208213
Streamable<User> deleteStreamableByEmailAddress(String username);
209214

210215
// cannot generate delete and return a domain object

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,9 @@ Window<User> findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S
307307
// DATAJPA-460
308308
List<User> deleteByLastname(String lastname);
309309

310+
@Modifying
311+
User deleteModifyingByLastname(String lastname);
312+
310313
User deleteOneByLastname(String lastname);
311314

312315
Optional<User> deleteOneOptionalByLastname(String lastname);

0 commit comments

Comments
 (0)