Browse Source

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
pull/4108/head
Mark Paluch 5 months ago
parent
commit
ae98285b0f
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 2
      spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaCodeBlocks.java
  2. 2
      spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributor.java
  3. 2
      spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/StringAotQuery.java
  4. 9
      spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java
  5. 25
      spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/JpaRepositoryContributorIntegrationTests.java
  6. 5
      spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/aot/UserRepository.java
  7. 3
      spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java

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

@ -656,7 +656,7 @@ class JpaCodeBlocks { @@ -656,7 +656,7 @@ class JpaCodeBlocks {
: TypeName.get(context.getDomainType());
builder.add("\n");
if (modifying.isPresent()) {
if (modifying.isPresent() && !(aotQuery instanceof StringAotQuery.DerivedAotQuery)) {
if (modifying.getBoolean("flushAutomatically")) {
builder.addStatement("this.$L.flush()", context.fieldNameOf(EntityManager.class));

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

@ -207,7 +207,7 @@ public class JpaRepositoryContributor extends RepositoryContributor { @@ -207,7 +207,7 @@ public class JpaRepositoryContributor extends RepositoryContributor {
.metadataOnly(aotQueries.toMetadata(queryMethod.isPageQuery()));
}
if (queryMethod.isModifyingQuery()) {
if (queryMethod.isModifyingQuery() && !(aotQueries.result() instanceof StringAotQuery.DerivedAotQuery)) {
TypeInformation<?> returnType = getRepositoryInformation().getReturnType(method);

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

@ -173,7 +173,7 @@ abstract class StringAotQuery extends AotQuery { @@ -173,7 +173,7 @@ abstract class StringAotQuery extends AotQuery {
*
* @author Mark Paluch
*/
private static class DerivedAotQuery extends StringAotQuery {
static class DerivedAotQuery extends StringAotQuery {
private final String queryString;
private final Limit limit;

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

@ -1621,6 +1621,15 @@ class UserRepositoryTests { @@ -1621,6 +1621,15 @@ class UserRepositoryTests {
.isThrownBy(() -> repository.deleteOneByLastname(firstUser.getLastname()));
}
@Test // GH-4102
void deleteOneModifying() {
flushTestUsers();
User user = repository.deleteModifyingByLastname(firstUser.getLastname());
assertThat(user).isEqualTo(firstUser);
}
@Test // DATAJPA-460
void deleteByShouldRemoveElementsMatchingDerivedQuery() {

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

@ -558,6 +558,31 @@ class JpaRepositoryContributorIntegrationTests { @@ -558,6 +558,31 @@ class JpaRepositoryContributorIntegrationTests {
assertThat(yodaShouldBeGone).isNull();
}
@Test // GH-4102
void testDerivedDeleteSingleWithModifying() {
User user = fragment.deleteByEmailAddressAndIdIsNotNull("yoda@jedi.org");
assertThat(user).isNotNull().extracting(User::getEmailAddress).isEqualTo("yoda@jedi.org");
Object yodaShouldBeGone = em
.createQuery("SELECT u FROM %s u WHERE u.emailAddress = 'yoda@jedi.org'".formatted(User.class.getName()))
.getSingleResultOrNull();
assertThat(yodaShouldBeGone).isNull();
}
@Test // GH-4102
void testDerivedDeleteAndReturnCount() {
int count = fragment.deleteAndReturnCountByEmailAddress("yoda@jedi.org");
assertThat(count).isEqualTo(1);
Object yodaShouldBeGone = em
.createQuery("SELECT u FROM %s u WHERE u.emailAddress = 'yoda@jedi.org'".formatted(User.class.getName()))
.getSingleResultOrNull();
assertThat(yodaShouldBeGone).isNull();
}
@Test // GH-3830
void shouldReturnStreamableDelete() {

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

@ -205,6 +205,11 @@ interface UserRepository extends CrudRepository<User, Integer> { @@ -205,6 +205,11 @@ interface UserRepository extends CrudRepository<User, Integer> {
User deleteByEmailAddress(String username);
@Modifying
User deleteByEmailAddressAndIdIsNotNull(String email);
int deleteAndReturnCountByEmailAddress(String username);
Streamable<User> deleteStreamableByEmailAddress(String username);
// cannot generate delete and return a domain object

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

@ -307,6 +307,9 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi @@ -307,6 +307,9 @@ public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecifi
// DATAJPA-460
List<User> deleteByLastname(String lastname);
@Modifying
User deleteModifyingByLastname(String lastname);
User deleteOneByLastname(String lastname);
Optional<User> deleteOneOptionalByLastname(String lastname);

Loading…
Cancel
Save