Spring Data JPA - 接口投影中的自定义类型转换

12

我正在尝试实现基于接口的投影,但我无法在自定义类型列中使其工作。

以下是我尝试做的示例:

存储库:

@Query(value = "SELECT customType from TABLE", nativeQuery = true)
List<TestClass> getResults();

接口投影:

public interface TestClass {
  @Convert(converter = MyCustomTypeConverter.class)
  MyCustomType getCustomType();
}

转换器:

@Converter
public class MyCustomTypeConverter implements Converter<String, MyCustomType> {

      @Override
      public MyCustomType convert(String source) {
        // whatever
      }
}

当我在库上调用getResults()时,我按预期收到结果列表,但当我尝试在其中一个结果上调用getCustomType()时,我会收到异常:
java.lang.IllegalArgumentException: Projection type must be an interface!
at org.springframework.util.Assert.isTrue(Assert.java:118)
at org.springframework.data.projection.ProxyProjectionFactory.createProjection(ProxyProjectionFactory.java:100)
at org.springframework.data.projection.SpelAwareProxyProjectionFactory.createProjection(SpelAwareProxyProjectionFactory.java:45)
at org.springframework.data.projection.ProjectingMethodInterceptor.getProjection(ProjectingMethodInterceptor.java:131)
at org.springframework.data.projection.ProjectingMethodInterceptor.invoke(ProjectingMethodInterceptor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.ProxyProjectionFactory$TargetAwareMethodInterceptor.invoke(ProxyProjectionFactory.java:245)

我发现问题出在

中。

org.springframework.data.projection.ProxyProjectionFactory

这个技术使用了

org.springframework.core.convert.support.DefaultConversionService

很明显,我的自定义类型转换器未注册。

如果我在 ConversionService 中断点停止,并在运行时手动添加我的转换器,那么投影将没有任何问题。

所以问题是:我是否可以在基于接口的 Projection 期间,以某种方式向 Spring JPA 使用的 ConversionService 注册我的自定义转换器?

编辑:

我在 InitializingBean 中向 DefaultConversionService 的 sharedInstance 添加了我的转换器,它起作用了。

@Component
public class DefaultConversionServiceInitializer implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        DefaultConversionService conversionService = (DefaultConversionService) DefaultConversionService.getSharedInstance();
        conversionService.addConverter(new MyCustomTypeConverter());
    }
}

1
我有同样的问题,但这个解决方案不起作用。自定义转换器已经在上下文创建时添加到共享的conversionService中,但在ProxyProjectionFactory中解析转换器时仍然找不到。 你使用的spring-data版本是什么? - Guillaume
另一件需要检查的事情是确保转换器的源类型符合您的期望。 例如,我曾经遇到过这样一个情况,我不得不使用字符(Character)而不是字符串(String)来创建转换器。 我会调试它,并检查它尝试转换的确切类型。 - jprzystupa
1
我在2.0.4 Spring Boot中遇到了问题,所以我猜这是一个已经解决的问题。我尝试调试这个不透明的系统,并且发现我的存储库动态代理构建中根本没有使用转换服务的共享实例。非常感谢您的反馈!做出更改的提交:https://github.com/spring-projects/spring-data-commons/commit/ea8cf8c629bd0c50991801811e1334589c1a4aff#diff-c80238b8ee9becc1b1dc7abaf85a2c4f - Guillaume
我猜这是版本问题,因为我正在使用2.2.x。也许只需尝试使用AttributeConverter而不是Converter? - jprzystupa
这在Spring Boot中又出问题了,建议的解决方法是在@Value注释中指定一个转换器;请参见https://github.com/spring-projects/spring-data-commons/issues/2260#issuecomment-774029777。 - MikaelF
显示剩余3条评论
2个回答

4
在Spring Data Jpa 2.4.0中,这个问题再次出现了(请参见此已关闭的GitHub问题)。
一个解决方法是由用户ajobra76建议的(链接),可以像这样指定要使用的转换器:
public interface TestClass {
    @Value("#{@myCustomTypeConverter.convert(target.customType)}")
    MyCustomType getCustomType();
}

myCustomTypeConverter应该是ApplicationContext中的一个bean。当然,使用SpEL指定方法还有其他方式,包括创建一个new对象和调用静态方法。但这只是为了说明它是可以完成的。

target是一个标识符,将被绑定到"支持投影的聚合根"(Spring Data Jpa文档,第"开放式投影"部分)。


4

使用的ConversionServiceDefaultConversionService.getSharedInstance()

因此,您应该能够访问它并添加您的转换器。


1
好的,它起作用了。我只需要将它强制转换为DefaultConversionService,因为ConversionService没有addConverter()方法。 - jprzystupa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接