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

Spring Boot 3 GraalVM Native镜像中DataClassRowMapper类型转换失败

解决Spring Boot Native镜像中DataClassRowMapper无法转换Timestamp到Instant的问题

问题背景

Spring Boot项目在JVM模式(./gradlew bootRun)下CRUD操作正常,但通过./gradlew clean nativeCompile生成Native镜像运行时,使用DataClassRowMapper从PostgreSQL查询数据时,出现Timestamp转Instant失败的错误:

org.postgresql.util.PSQLException: conversion to class java.time.Instant from timestamptz not supported
...
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.sql.Timestamp' to required type 'java.time.Instant' for property 'createdOn': no matching editors or conversion strategy found

当前使用的查询代码:

public Optional<ClientDto> findById(String tenantId, UUID id) {
  List<ClientDto> result =
      db.query(
          "SELECT * FROM clients WHERE tenant_id = ? AND id = ?",
          DataClassRowMapper.newInstance(ClientDto.class),
          tenantId,
          id);
  return result.size() == 1 ? Optional.ofNullable(result.get(0)) : Optional.empty();
}

ClientDto定义:

public record ClientDto(
    UUID id,
    String tenantId,
    String name,
    String description,
    Instant createdOn,
    Instant modifiedOn) {
}

此前尝试过改用BeanPropertyRowMapper、将record改为普通类、自定义Timestamp转Instant转换器均无效,但使用ListCRUDRepository或自定义RowMapper可正常运行,目标是让DataClassRowMapper在Native镜像中正常工作。

问题根源

GraalVM Native镜像在构建时会做静态分析,未被显式注册的反射信息、类型转换器会被裁剪。DataClassRowMapper依赖Spring的类型转换系统和反射实例化DTO,但Native环境下默认没有包含Timestamp与Instant之间的转换元数据,也没有注册DTO类的反射信息,导致转换失败。

解决方案

方案1:注册类型转换器+反射元数据

创建配置类,显式注册Timestamp与Instant的转换器,并通过@RegisterReflectionForBinding注册相关类的反射信息:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.nativex.hint.RegisterReflectionForBinding;

import java.sql.Timestamp;
import java.time.Instant;

@Configuration
@RegisterReflectionForBinding({Timestamp.class, Instant.class, ClientDto.class})
public class NativeConversionConfig {

    @Bean
    public Converter<Timestamp, Instant> timestampToInstantConverter() {
        return Timestamp::toInstant;
    }

    @Bean
    public Converter<Instant, Timestamp> instantToTimestampConverter() {
        return Timestamp::from;
    }
}

方案2:使用RuntimeHints注册Native镜像元数据

通过Spring Boot的RuntimeHintsRegistrar注册所需的反射和类型信息,确保GraalVM在构建镜像时保留这些元数据:

import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;

import java.sql.Timestamp;
import java.time.Instant;

@Configuration
@ImportRuntimeHints(NativeDataClassRowMapperHints.class)
public class NativeConfig {

    static class NativeDataClassRowMapperHints implements RuntimeHintsRegistrar {
        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            // 注册Timestamp、Instant的反射信息
            hints.reflection().registerType(Timestamp.class, MemberCategory.values());
            hints.reflection().registerType(Instant.class, MemberCategory.values());
            // 注册ClientDto的反射信息,支持DataClassRowMapper实例化
            hints.reflection().registerType(ClientDto.class, MemberCategory.values());
            // 注册DataClassRowMapper的反射信息
            hints.reflection().registerType(org.springframework.jdbc.core.DataClassRowMapper.class, MemberCategory.values());
        }
    }
}

方案3:确保PostgreSQL Native驱动依赖

在build.gradle中添加PostgreSQL Native驱动的运行时依赖,确保Native环境下数据库驱动正常工作:

dependencies {
    implementation 'org.postgresql:postgresql'
    runtimeOnly 'org.postgresql:postgresql-native'
}

验证

重新执行./gradlew clean nativeCompile生成Native镜像,运行后测试findById操作,即可正常完成Timestamp到Instant的转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:15:00