Я пытаюсь использовать JPA Projection (Spring v1.5.9.RELEASE), чтобы вернуть пользовательский тип из JpaRepository
следующим образом (MCVE):
@Repository
public interface TestRepository extends JpaRepository<Void, Void> {
public interface TestValue {
UUID getId();
int getNum();
}
@Query(value = "SELECT :id as id, 1 as num", nativeQuery = true)
List<TestValue> getTestValues(@Param("id") UUID id);
}
Я хотел бы использовать репозиторий следующим образом:
List<TestValue> testValues = testRepository.getTestValues(UUID.randomUUID());
int num = testValues.get(0).getNum(); // Returns 1 as expected
UUID id = testValues.get(0).getId(); // ***Fails with an exception***
Однако вызов getId()
завершается неудачно со следующим исключением:
java.lang.IllegalArgumentException: Projection type must be an interface!
at org.springframework.util.Assert.isTrue(Assert.java:92) ~[spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.data.projection.ProxyProjectionFactory.createProjection(ProxyProjectionFactory.java:110) ~[spring-data-commons-1.13.9.RELEASE.jar:?]
at org.springframework.data.projection.SpelAwareProxyProjectionFactory.createProjection(SpelAwareProxyProjectionFactory.java:42) ~[spring-data-commons-1.13.9.RELEASE.jar:?]
at org.springframework.data.projection.ProjectingMethodInterceptor.getProjection(ProjectingMethodInterceptor.java:126) ~[spring-data-commons-1.13.9.RELEASE.jar:?]
at org.springframework.data.projection.ProjectingMethodInterceptor.invoke(ProjectingMethodInterceptor.java:76) ~[spring-data-commons-1.13.9.RELEASE.jar:?]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.data.projection.ProxyProjectionFactory$TargetAwareMethodInterceptor.invoke(ProxyProjectionFactory.java:264) ~[spring-data-commons-1.13.9.RELEASE.jar:?]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56) ~[spring-data-commons-1.13.9.RELEASE.jar:?]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.13.RELEASE.jar:4.3.13.RELEASE]
at com.sun.proxy.$Proxy199.getId(Unknown Source) ~[?:?]
at ***my.company***
В возвращенном прокси-объекте TestValue
хранитсяАтрибут id
в виде необработанного массива byte
.Я ожидаю, что ProjectingMethodInterceptor
в конечном итоге выполнит фактическое преобразование типа в экземпляр UUID
.Глядя на исходный код (ProjectingMethodInterceptor.invoke(..):74
), кажется, что он использует ConversionService
для выполнения преобразований.Я попробовал следующее:
@Configuration
public class MyApplicationConfig {
@Bean
public ConversionService conversionService() {
DefaultConversionService service = new DefaultConversionService();
service.addConverter(new UUIDConverter());
return service;
}
}
Класс UUIDConverter
реализует интерфейс org.springframework.core.convert.converter.Converter<byte[], UUID>
.К сожалению, этот подход не работает , поскольку используемый ProjectingMethodInterceptor
создается через ProxyProjectingFactory
, который обходит сконфигурированный ConversionService
и создает экземпляр DefaultConversionService
(ProxyProjectionFactory.ctor():65
).
Я также попытался добавить аннотацию @Convert(converter = UUIDConverter.class)
к объявлению getId()
в TestValue
, однако этот подход тоже не работает .
Я, очевидно, мог бы изменить тип возвращаемого значенияметода getId()
в byte[]
и выполнить фактическое преобразование в UUID
в коде моего приложения, но этот подход не кажется правильным способом сделать это.
Вопрос 1: Как определяются пользовательские преобразования типов в Интерфейсах JPA ?
Вопрос 2: Если это невозможно, каков «пружинный» способ реализации вышеуказанного метода хранилища с этимточная подпись метода и базовый запрос.