You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自定义JdbcClient所使用的ConversionService?

JdbcClient自定义类型转换失败:如何配置其ConversionService

问题场景

使用JdbcClient读取数据库记录时,需要将字符串列转换为自定义PersonName类型,已实现StringToPersonNameConverter转换器并尝试注册到Spring容器,但始终触发org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [com.example.demo.DemoApplication$PersonName]异常。希望直接通过配置ConversionService解决,避免使用RowMapper。

环境:Java 17 + Spring Boot 3.3.0

原测试代码:

package com.example.demo;

import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.format.FormatterRegistry;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import jakarta.annotation.PostConstruct;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        System.out.println("*** DemoApplication.main");
        SpringApplication.run(new Class[]{
                        MyWebConfiguration.class,
                        MyOtherConfiguration.class,
                        DemoApplication.class
                },
                args);
    }

    private final JdbcClient jdbcClient;

    public DemoApplication(JdbcClient jdbcClient) {
        System.out.println("*** DemoApplication.DemoApplication");
        this.jdbcClient = jdbcClient;
    }

    @PostConstruct
    public void init() {
        final List<Person> list = jdbcClient.sql("select 'John;Doe' as \"name\" from dual")
                .query(Person.class)
                .list();
        System.out.println("list = " + list);
    }

    @Configuration
    public static class MyWebConfiguration implements WebMvcConfigurer {
        private final StringToPersonNameConverter stringToPersonNameConverter;

        public MyWebConfiguration(StringToPersonNameConverter stringToPersonNameConverter) {
            System.out.println("*** MyWebConfiguration.MyWebConfiguration");
            this.stringToPersonNameConverter = stringToPersonNameConverter;
        }

        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(stringToPersonNameConverter);
        }
    }

    @Configuration
    public static class MyOtherConfiguration {
        private final ConfigurableEnvironment environment;
        private final ConverterRegistry converterRegistry;
        private final GenericConversionService conversionService;
        private final StringToPersonNameConverter stringToPersonNameConverter;

        public MyOtherConfiguration(
                ConfigurableEnvironment environment,
                ConverterRegistry converterRegistry,
                GenericConversionService conversionService,
                StringToPersonNameConverter stringToPersonNameConverter) {
            System.out.println("*** MyOtherConfiguration.MyOtherConfiguration");
            this.environment = environment;
            this.converterRegistry = converterRegistry;
            this.conversionService = conversionService;
            this.stringToPersonNameConverter = stringToPersonNameConverter;
        }

        @PostConstruct
        public void addCustomConverters() {
            environment.getConversionService().addConverter(stringToPersonNameConverter);
            converterRegistry.addConverter(stringToPersonNameConverter);
            conversionService.addConverter(stringToPersonNameConverter);
        }
    }

    @Component
    @ConfigurationPropertiesBinding
    public static class StringToPersonNameConverter implements Converter<String, PersonName> {
        public StringToPersonNameConverter() {
            System.out.println("*** StringToPersonNameConverter.StringToPersonNameConverter");
        }

        @Override
        public PersonName convert(String source) {
            if (source.contains(";")) {
                String[] parts = source.split(";", 2);
                return new PersonName(parts[0], parts[1]);
            }
            return new PersonName(source, "?");
        }
    }

    public record Person(PersonName name) {
    }

    public record PersonName(String firstName, String lastName) {
    }

}

问题原因

JdbcClient默认使用内部独立的GenericConversionService实例,不会自动复用Spring容器中的全局ConversionService。之前将转换器注册到WebMvc或环境的ConversionService中,对JdbcClient完全无效。

解决方案

通过自定义JdbcClient Bean,在构建JdbcClient时手动注入包含自定义转换器的ConversionService。

修改后的代码

package com.example.demo;

import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import javax.sql.DataSource;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    private final JdbcClient jdbcClient;

    public DemoApplication(JdbcClient jdbcClient) {
        this.jdbcClient = jdbcClient;
    }

    @PostConstruct
    public void init() {
        final List<Person> list = jdbcClient.sql("select 'John;Doe' as \"name\" from dual")
                .query(Person.class)
                .list();
        System.out.println("list = " + list);
    }

    // 自定义JdbcClient配置
    @Configuration
    static class JdbcClientConfig {
        @Bean
        JdbcClient jdbcClient(DataSource dataSource, StringToPersonNameConverter converter) {
            // 创建包含自定义转换器的ConversionService
            GenericConversionService conversionService = new GenericConversionService();
            conversionService.addConverter(converter);

            return JdbcClient.builder(dataSource)
                    .conversionService(conversionService) // 替换默认的ConversionService
                    .build();
        }
    }

    @Component
    public static class StringToPersonNameConverter implements Converter<String, PersonName> {
        @Override
        public PersonName convert(String source) {
            if (source.contains(";")) {
                String[] parts = source.split(";", 2);
                return new PersonName(parts[0], parts[1]);
            }
            return new PersonName(source, "?");
        }
    }

    public record Person(PersonName name) {
    }

    public record PersonName(String firstName, String lastName) {
    }
}

关键要点

  1. 移除冗余配置:删除原代码中MyWebConfiguration和MyOtherConfiguration的转换器注册逻辑,这些配置对JdbcClient不起作用。
  2. 自定义JdbcClient构建:通过JdbcClient.builder(dataSource)获取构建器,调用conversionService()方法传入包含自定义转换器的实例,覆盖默认的内部ConversionService。
  3. 转换器简化:StringToPersonNameConverter只需保留@Component注解,作为Spring组件被注入即可,无需额外注册到其他ConversionService。

内容的提问来源于stack exchange,提问作者Kristof Neirynck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 04:47:03