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

Spring Boot 3.2.0升级后自定义to_timestamp SQL函数无法识别求助

Spring Boot 3.2.0升级后自定义to_timestamp函数报错解决方案

问题背景

将Spring Boot应用从2.3.8(Java 11)升级至3.2.0(Java 17)后,基于PostgreSQL的JPA动态时间范围查询功能失效,报错:

ERROR: function to_timestamp(text) does not exist

原功能通过自定义MetadataBuilderContributor注册to_timestamp函数实现,升级后尝试用NamedSqmFunctionDescriptor重构未解决问题,需适配Spring Boot 3.2.0对应Hibernate版本的方案。

相关代码与错误信息

1. JPA查询语句

select c from Customer c where to_timestamp(c.created_date) BETWEEN to_timestamp(cast(:fromtime as text)) AND to_timestamp(cast(:toTime as text))

2. 旧版本(Spring Boot 2.3.8)配置代码

import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.spi.MetadataBuilderContributor;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.stereotype.Component;

@Component 
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {
    @Override 
    public void contribute(MetadataBuilder metadataBuilder) {
        metadataBuilder.applySqlFunction("to_timestamp", 
            new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, 
                "to_timestamp(to_char(?1 :: timestamptz AT TIME ZONE 'UTC' ,'HH24:MI'),'HH24:MI')::time"));
    }
}

3. 升级后尝试的代码

@Component 
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {
    @Override 
    public void contribute(MetadataBuilder metadataBuilder) {
        metadataBuilder.applySqlFunction("to_timestamp", ToTimestampFunction.INSTANCE);
    }

    public static class ToTimestampFunction extends NamedSqmFunctionDescriptor {
        public static final ToTimestampFunction INSTANCE = new ToTimestampFunction();

        public ToTimestampFunction() {
            super("to_timestamp", false, StandardArgumentsValidators.exactly(1), null);
        }

        public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> arguments, SqlAstTranslator<?> walker) {
            Expression time = (Expression) arguments.get(0);
            sqlAppender.appendSql("to_timestamp(");
            walker.render(time, SqlAstNodeRenderingMode.DEFAULT);
            sqlAppender.appendSql(" ::text,'HH24:MI')::time");
        }
    }
}

适配方案

方案1:使用SqmFunctionTemplate快速迁移

Hibernate 6.x中SQLFunctionTemplate已过时,替换为SqmFunctionTemplate即可兼容原有逻辑:

import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.spi.MetadataBuilderContributor;
import org.hibernate.query.sqm.function.SqmFunctionTemplate;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.stereotype.Component;

@Component
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {
    @Override
    public void contribute(MetadataBuilder metadataBuilder) {
        // 复用旧版本的SQL模板逻辑,注册自定义to_timestamp函数
        metadataBuilder.applySqlFunction(
            "to_timestamp",
            SqmFunctionTemplate.from(
                "to_timestamp(to_char(?1 :: timestamptz AT TIME ZONE 'UTC' ,'HH24:MI'),'HH24:MI')::time",
                StandardBasicTypes.TIME
            )
        );
    }
}

方案2:自定义SqmFunctionDescriptor实现更灵活逻辑

如果需要更精细的SQL渲染控制,可继承AbstractSqmFunctionDescriptor实现:

import org.hibernate.boot.MetadataBuilder;
import org.hibernate.boot.spi.MetadataBuilderContributor;
import org.hibernate.query.sqm.function.AbstractSqmFunctionDescriptor;
import org.hibernate.query.sqm.function.FunctionRenderingContext;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.sql.ast.SqlAstNode;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.stereotype.Component;
import java.util.List;

@Component
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {
    @Override
    public void contribute(MetadataBuilder metadataBuilder) {
        metadataBuilder.applySqlFunction("to_timestamp", new CustomToTimestampFunction());
    }

    public static class CustomToTimestampFunction extends AbstractSqmFunctionDescriptor {
        public CustomToTimestampFunction() {
            super(
                "to_timestamp",
                StandardArgumentsValidators.exactly(1),
                null,
                StandardBasicTypes.TIME,
                null
            );
        }

        @Override
        public void render(
            SqlAppender sqlAppender,
            List<? extends SqlAstNode> arguments,
            FunctionRenderingContext context
        ) {
            Expression timeExpr = (Expression) arguments.get(0);
            
            sqlAppender.appendSql("to_timestamp(to_char(");
            context.getTranslator().render(timeExpr);
            sqlAppender.appendSql(" :: timestamptz AT TIME ZONE 'UTC' ,'HH24:MI'),'HH24:MI')::time");
        }
    }
}

关键说明

  • 报错根源是升级后自定义函数未正确注册,Hibernate直接调用PostgreSQL原生to_timestamp函数,但原生函数需要格式参数,因此触发函数不存在的错误。
  • 两种方案均确保生成的SQL与旧版本一致,完美兼容原有业务逻辑,同时适配Hibernate 6.x的API规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 15:01:08