使用Spring SpannerTemplate执行SQL查询时遇ConverterNotFoundException
我使用Java Spring Boot,按照Spanner的query-modify文档步骤,通过SpannerTemplate连接Spanner数据库。插入数据时调用this.spannerTemplate.insert(singer)可正常执行,但执行以下查询语句时抛出异常:
List<Singer> allSingers = this.spannerTemplate .query(Singer.class, Statement.of("SELECT * FROM Singers"), new SpannerQueryOptions().setAllowPartialRead(true));
异常信息
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.google.cloud.ByteArray] to type [java.lang.String] at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-5.3.27.jar:5.3.27] at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-5.3.27.jar:5.3.27] at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175) ~[spring-core-5.3.27.jar:5.3.27] at com.google.cloud.spring.data.spanner.core.convert.SpannerCustomConverter.convert(SpannerCustomConverter.java:67) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.convert.StructPropertyValueProvider.convertOrRead(StructPropertyValueProvider.java:144) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.convert.StructPropertyValueProvider.readSingleWithConversion(StructPropertyValueProvider.java:120) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.convert.StructPropertyValueProvider.getPropertyValue(StructPropertyValueProvider.java:96) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.convert.ConverterAwareMappingSpannerEntityReader.lambda$read$0(ConverterAwareMappingSpannerEntityReader.java:110) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:368) ~[spring-data-commons-2.7.11.jar:2.7.11] at com.google.cloud.spring.data.spanner.core.convert.ConverterAwareMappingSpannerEntityReader.read(ConverterAwareMappingSpannerEntityReader.java:90) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.convert.ConverterAwareMappingSpannerEntityProcessor.mapToList(ConverterAwareMappingSpannerEntityProcessor.java:82) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.SpannerTemplate.mapToListAndResolveChildren(SpannerTemplate.java:649) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.SpannerTemplate.queryAndResolveChildren(SpannerTemplate.java:636) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.google.cloud.spring.data.spanner.core.SpannerTemplate.query(SpannerTemplate.java:258) ~[spring-cloud-gcp-data-spanner-3.5.5.jar:3.5.5] at com.example.spanner.SpannerAppRunner.readRecords(SpannerAppRunner.java:32) ~[classes/:na] at com.example.spanner.SpannerAppRunner.run(SpannerAppRunner.java:28) ~[classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:759) ~[spring-boot-2.7.11.jar:2.7.11]
已尝试的解决方法
- 实现
Converter<ByteArray, String>接口编写自定义转换器,通过@Bean注册:
@Bean public Converter<ByteArray, String> customConvertor() { return new CustomConvertor(); }
- 实现
org.springframework.core.convert.converter.GenericConverter,自定义ConversionService注册:
@Bean public ConversionService conversionService() { GenericConversionService service = new GenericConversionService(); GcpStringGenericConverter converter = new GcpStringGenericConverter(); service.addConverter(converter); return service; }
以上方案均未解决问题,需要相关解决指引。
解决指引
检查实体类字段映射
确认Singer实体类中对应Spanner表的字段类型是否匹配:如果Spanner中的字段是BYTES类型,但实体类用String接收,会触发转换异常。若字段确实为BYTES类型,可将实体类字段改为ByteArray,或确保自定义转换器被正确加载。正确注册Spanner自定义转换器
Spring Cloud GCP Spanner需要将自定义转换器注册到SpannerCustomConversions中,而非普通Spring ConversionService。正确注册方式如下:@Configuration public class SpannerConfig { @Bean public SpannerCustomConversions spannerCustomConversions() { return new SpannerCustomConversions(Collections.singletonList(new ByteArrayToStringConverter())); } static class ByteArrayToStringConverter implements Converter<ByteArray, String> { @Override public String convert(ByteArray source) { return source != null ? new String(source.toByteArray(), StandardCharsets.UTF_8) : null; } } }此方式能让Spanner实体转换器识别到自定义转换规则。
验证依赖版本兼容性
确认spring-cloud-gcp-data-spanner(当前版本3.5.5)与spring-boot(2.7.11)、spring-data-commons(2.7.11)的版本兼容性。版本不匹配可能导致转换器注册逻辑失效,建议参考官方版本矩阵调整依赖。避免覆盖全局ConversionService
手动创建ConversionServiceBean会覆盖Spring Boot自动配置的默认服务,导致Spanner内置转换器丢失。若必须自定义ConversionService,需同时加入Spanner默认转换器:@Bean public ConversionService conversionService(SpannerCustomConversions spannerCustomConversions) { GenericConversionService service = new GenericConversionService(); // 添加自定义转换器 service.addConverter(new ByteArrayToStringConverter()); // 添加Spanner默认转换器 spannerCustomConversions.registerConverters(service); return service; }
内容的提问来源于stack exchange,提问作者user2135533

