diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index ada670440..6a5833df3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query; import java.util.Collection; import java.util.List; +import java.util.Optional; import javax.persistence.EntityManager; import javax.persistence.NoResultException; @@ -38,6 +39,7 @@ import org.springframework.data.repository.support.PageableExecutionUtils.TotalS import org.springframework.data.util.CloseableIterator; import org.springframework.data.util.StreamUtils; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; /** * Set of classes to contain query execution strategies. Depending (mostly) on the return type of a @@ -55,8 +57,10 @@ public abstract class JpaQueryExecution { static { ConfigurableConversionService conversionService = new DefaultConversionService(); - conversionService.removeConvertible(Collection.class, Object.class); + conversionService.addConverter(JpaResultConverters.BlobToByteArrayConverter.INSTANCE); + conversionService.removeConvertible(Collection.class, Object.class); + potentiallyRemoveOptionalConverter(conversionService); CONVERSION_SERVICE = conversionService; } @@ -319,4 +323,29 @@ public abstract class JpaQueryExecution { return StreamUtils.createStreamFromIterator(iter); } } + + /** + * Removes the converter being able to convert any object into an {@link Optional} from the given + * {@link ConversionService} in case we're running on Java 8. + * + * @param conversionService must not be {@literal null}. + */ + public static void potentiallyRemoveOptionalConverter(ConfigurableConversionService conversionService) { + + ClassLoader classLoader = JpaQueryExecution.class.getClassLoader(); + + if (ClassUtils.isPresent("java.util.Optional", classLoader)) { + + try { + + Class optionalType = ClassUtils.forName("java.util.Optional", classLoader); + conversionService.removeConvertible(Object.class, optionalType); + + } catch (ClassNotFoundException e) { + return; + } catch (LinkageError e) { + return; + } + } + } } 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 25f3da18e..44f71d31d 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 @@ -22,6 +22,7 @@ import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; +import java.util.Optional; import javax.persistence.EntityManager; import javax.persistence.Query; @@ -220,15 +221,32 @@ public class JpaQueryExecutionUnitTests { verify(jpaQuery).createCountQuery((Object[]) any()); } - public static void sampleMethod(Pageable pageable) { + /** + * @see DATAJPA-951 + */ + @Test + public void doesNotPreemtivelyWrapResultIntoOptional() throws Exception { + doReturn(method).when(jpaQuery).getQueryMethod(); + doReturn(Optional.class).when(method).getReturnType(); + + StubQueryExecution execution = new StubQueryExecution() { + protected Object doExecute(AbstractJpaQuery query, Object[] values) { + return "result"; + } + }; + + Object result = execution.execute(jpaQuery, new Object[0]); + + assertThat(result, is(instanceOf(String.class))); } + public static void sampleMethod(Pageable pageable) {} + static class StubQueryExecution extends JpaQueryExecution { @Override protected Object doExecute(AbstractJpaQuery query, Object[] values) { - return null; } }