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 faba30c61..ace073b0e 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 @@ -24,6 +24,7 @@ import javax.persistence.Query; import javax.persistence.StoredProcedureQuery; import javax.persistence.TypedQuery; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -43,6 +44,12 @@ import org.springframework.util.Assert; */ public abstract class JpaQueryExecution { + private static final DefaultConversionService conversionService = new DefaultConversionService(); + + static { + conversionService.addConverter(JpaResultConverters.BlobToByteArrayConverter.INSTANCE); + } + /** * Executes the given {@link AbstractStringBasedJpaQuery} with the given {@link ParameterBinder}. * @@ -55,11 +62,26 @@ public abstract class JpaQueryExecution { Assert.notNull(query); Assert.notNull(values); + Object result; + try { - return doExecute(query, values); + result = doExecute(query, values); } catch (NoResultException e) { return null; } + + if (result == null) { + return null; + } + + JpaQueryMethod queryMethod = query.getQueryMethod(); + Class requiredType = queryMethod.getReturnType(); + + if (void.class.equals(requiredType) || requiredType.isAssignableFrom(result.getClass())) { + return result; + } + + return conversionService.convert(result, requiredType); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java new file mode 100644 index 000000000..8c80105c7 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java @@ -0,0 +1,87 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jpa.repository.query; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Blob; +import java.sql.SQLException; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.dao.CleanupFailureDataAccessException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.util.StreamUtils; + +/** + * Container for additional JPA result {@link Converter}s. + * + * @author Thomas Darimont + * @since 1.6 + */ +class JpaResultConverters { + + /** + * {@code private} to prevent instantiation. + */ + private JpaResultConverters() {} + + /** + * Converts the given {@link Blob} into a {@code byte[]}. + * + * @author Thomas Darimont + */ + enum BlobToByteArrayConverter implements Converter { + + INSTANCE; + + @Override + public byte[] convert(Blob source) { + + if (source == null) { + return null; + } + + InputStream blobStream = null; + try { + + blobStream = source.getBinaryStream(); + + if (blobStream != null) { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + StreamUtils.copy(blobStream, baos); + return baos.toByteArray(); + } + + } catch (SQLException e) { + throw new DataRetrievalFailureException("Couldn't retrieve data from blob.", e); + } catch (IOException e) { + throw new DataRetrievalFailureException("Couldn't retrieve data from blob.", e); + } finally { + if (blobStream != null) { + try { + blobStream.close(); + } catch (IOException e) { + throw new CleanupFailureDataAccessException("Couldn't close binary stream for given blob.", e); + } + } + } + + return null; + } + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 2cd92db3b..ea5de1694 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -1390,6 +1390,23 @@ public class UserRepositoryTests { flushTestUsers(); byte[] result = null; // repository.findBinaryDataByIdJpaQl(firstUser.getId()); + + assertThat(result.length, is(data.length)); + assertThat(result, is(data)); + } + + /** + * @see DATAJPA-506 + */ + @Test + public void findBinaryDataByIdNative() throws Exception { + + byte[] data = "Woho!!".getBytes("UTF-8"); + firstUser.setBinaryData(data); + + flushTestUsers(); + + byte[] result = repository.findBinaryDataByIdNative(firstUser.getId()); assertThat(result.length, is(data.length)); assertThat(result, is(data)); } diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 06db46638..42e626eb6 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -445,4 +445,10 @@ public interface UserRepository extends JpaRepository, JpaSpecifi * @see DATAJPA-551 */ Slice findTop2UsersBy(Pageable page); + + /** + * @see DATAJPA-506 + */ + @Query(value = "select u.binaryData from User u where u.id = ?", nativeQuery = true) + byte[] findBinaryDataByIdNative(Integer id); }