diff --git a/pom.xml b/pom.xml
index 119e2f15c..9183c7a3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,7 @@
2.7.5
5.4.8.Final
2.19.1
+ 0.10.3
org.hibernate
2.4.3-SNAPSHOT
@@ -204,6 +205,13 @@
true
+
+ io.vavr
+ vavr
+ ${vavr}
+ test
+
+
diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java
index d26c7759f..d1fd62ed3 100644
--- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java
+++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java
@@ -40,7 +40,9 @@ import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryMethod;
+import org.springframework.data.repository.util.QueryExecutionConverters;
import org.springframework.data.util.Lazy;
+import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -79,6 +81,7 @@ public class JpaQueryMethod extends QueryMethod {
private final QueryExtractor extractor;
private final Method method;
+ private final Class> returnType;
private @Nullable StoredProcedureAttributes storedProcedureAttributes;
private final Lazy lockModeType;
@@ -107,6 +110,7 @@ public class JpaQueryMethod extends QueryMethod {
Assert.notNull(extractor, "Query extractor must not be null!");
this.method = method;
+ this.returnType = potentiallyUnwrapReturnTypeFor(metadata, method);
this.extractor = extractor;
this.lockModeType = Lazy
.of(() -> (LockModeType) Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, Lock.class)) //
@@ -126,8 +130,7 @@ public class JpaQueryMethod extends QueryMethod {
return new JpaEntityGraph(entityGraph, getNamedQueryName());
});
this.isNativeQuery = Lazy.of(() -> getAnnotationValue("nativeQuery", Boolean.class));
- this.isCollectionQuery = Lazy
- .of(() -> super.isCollectionQuery() && !NATIVE_ARRAY_TYPES.contains(method.getReturnType()));
+ this.isCollectionQuery = Lazy.of(() -> super.isCollectionQuery() && !NATIVE_ARRAY_TYPES.contains(this.returnType));
this.isProcedureQuery = Lazy.of(() -> AnnotationUtils.findAnnotation(method, Procedure.class) != null);
this.entityMetadata = Lazy.of(() -> new DefaultJpaEntityMetadata<>(getDomainClass()));
@@ -136,6 +139,18 @@ public class JpaQueryMethod extends QueryMethod {
assertParameterNamesInAnnotatedQuery();
}
+ private static Class> potentiallyUnwrapReturnTypeFor(RepositoryMetadata metadata, Method method) {
+
+ TypeInformation> returnType = metadata.getReturnType(method);
+
+ while (QueryExecutionConverters.supports(returnType.getType())
+ || QueryExecutionConverters.supportsUnwrapping(returnType.getType())) {
+ returnType = returnType.getRequiredComponentType();
+ }
+
+ return returnType.getType();
+ }
+
private void assertParameterNamesInAnnotatedQuery() {
String annotatedQuery = getAnnotatedQuery();
@@ -243,7 +258,7 @@ public class JpaQueryMethod extends QueryMethod {
* @return
*/
Class> getReturnType() {
- return method.getReturnType();
+ return returnType;
}
/**
diff --git a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java
index bb7d3989f..715f4c7ea 100644
--- a/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java
+++ b/src/test/java/org/springframework/data/jpa/repository/query/JpaQueryExecutionUnitTests.java
@@ -19,6 +19,9 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
+import io.vavr.control.Try;
+
+import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
@@ -38,8 +41,13 @@ import org.mockito.quality.Strictness;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.provider.QueryExtractor;
+import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ModifyingExecution;
import org.springframework.data.jpa.repository.query.JpaQueryExecution.PagedExecution;
+import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
+import org.springframework.data.repository.Repository;
+import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
/**
* Unit test for {@link JpaQueryExecution}.
@@ -85,6 +93,27 @@ class JpaQueryExecutionUnitTests {
assertThatIllegalArgumentException().isThrownBy(() -> new StubQueryExecution().execute(jpaQuery, null));
}
+ @Test // DATAJPA-1827
+ void supportsModifyingResultsUsingWrappers() throws Exception {
+
+ Method method = VavrRepository.class.getMethod("updateUsingVavrMethod");
+ DefaultRepositoryMetadata repositoryMetadata = new DefaultRepositoryMetadata(VavrRepository.class);
+ JpaQueryMethod queryMethod = new JpaQueryMethod(method, repositoryMetadata, new SpelAwareProxyProjectionFactory(),
+ mock(QueryExtractor.class));
+
+ new JpaQueryExecution.ModifyingExecution(queryMethod, mock(EntityManager.class));
+
+ assertThat(queryMethod.isModifyingQuery()).isTrue();
+ }
+
+ interface VavrRepository extends Repository {
+
+ // Wrapped outcome allowed
+ @org.springframework.data.jpa.repository.Query("update Credential d set d.enabled = false where d.enabled = true")
+ @Modifying
+ Try updateUsingVavrMethod();
+ }
+
@Test
void transformsNoResultExceptionToNull() {