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

如何正确实现jOOQ Binding以渲染合法的PostgreSQL Interval语法

修复jOOQ PostgreSQL Interval自定义绑定的SQL渲染括号问题

问题描述

在使用jOOQ为PostgreSQL的Interval类型绑定自定义类型时,渲染生成的SQL语句中,Interval值被多余的括号包裹,导致语句无法执行。例如生成的无效SQL:

insert into "XX"."YY" ([...], "my_interval") 
values ([...], interval ('0 years 0 mons 1 days 1 hours 0 mins 0.0 secs'))

调试时发现ctx.query()中的正确格式应为interval '0 years 0 mons 1 days 1 hours 0 mins 0.0 secs',多余的括号是问题根源。

问题根源

问题出在IntervalBinding#sql方法的实现上,原代码使用DSL.val()生成值,该方法在部分渲染上下文会为字符串添加括号,导致PostgreSQL无法识别的语法:

@Override
public void sql(final BindingSQLContext<Interval> ctx) {
    ctx.render().sql("interval ").visit(DSL.val(ctx.convert(converter).value()));
}

解决方案

方案1:使用DSL.inline()生成无括号字面量(推荐,安全合规)

DSL.inline()会生成不带括号的字面量,同时自动处理字符串转义,避免SQL注入风险:

@Override
public void sql(final BindingSQLContext<Interval> ctx) {
    ctx.render().sql("interval ").visit(DSL.inline(ctx.convert(converter).value()));
}

方案2:直接拼接字面量(仅当转换后的字符串安全时使用)

如果确定转换后的Interval字符串不会存在SQL注入风险,也可以直接拼接成PostgreSQL支持的格式:

@Override
public void sql(final BindingSQLContext<Interval> ctx) {
    String intervalStr = ctx.convert(converter).value();
    if (intervalStr == null) {
        ctx.render().sql("null");
    } else {
        ctx.render().sql("interval '").sql(intervalStr).sql("'");
    }
}

额外优化:完善Converter双向转换逻辑

原converter()方法返回的是空实现,建议修改为复用已定义的IntervalConverter,保证数据库YearToSecond类型与自定义Interval类型的双向转换正常工作:

@Override
public Converter<YearToSecond, Interval> converter() {
    return new Converter<>() {
        @Override
        public Interval from(final YearToSecond databaseObject) {
            if (databaseObject == null) {
                return null;
            }
            return converter.from(databaseObject.toString());
        }

        @Override
        public YearToSecond to(final Interval userObject) {
            if (userObject == null) {
                return null;
            }
            return YearToSecond.valueOf(converter.to(userObject));
        }

        @Override
        public Class<YearToSecond> fromType() {
            return YearToSecond.class;
        }

        @Override
        public Class<Interval> toType() {
            return Interval.class;
        }
    };
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 23:54:50