DATAJPA-1827 - Consider wrapper types for Modifying JPA Query Execution.
We now consider wrapper types (nullable types, Vavr/Javaslang/Futures) as potential wrappers and inspect the component type of each wrapper to determine the actual method return type. Original pull request: #438.
This commit is contained in:
committed by
Jens Schauder
parent
8ff88631e0
commit
f21e91c5e8
8
pom.xml
8
pom.xml
@@ -24,6 +24,7 @@
|
||||
<eclipselink>2.7.5</eclipselink>
|
||||
<hibernate>5.4.8.Final</hibernate>
|
||||
<mockito>2.19.1</mockito>
|
||||
<vavr>0.10.3</vavr>
|
||||
<hibernate.groupId>org.hibernate</hibernate.groupId>
|
||||
<springdata.commons>2.4.3-SNAPSHOT</springdata.commons>
|
||||
|
||||
@@ -204,6 +205,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.vavr</groupId>
|
||||
<artifactId>vavr</artifactId>
|
||||
<version>${vavr}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Persistence providers -->
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -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> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<String, String> {
|
||||
|
||||
// Wrapped outcome allowed
|
||||
@org.springframework.data.jpa.repository.Query("update Credential d set d.enabled = false where d.enabled = true")
|
||||
@Modifying
|
||||
Try<Integer> updateUsingVavrMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
void transformsNoResultExceptionToNull() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user