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

基于jOOQ+DuckDB的Parquet列动态聚合类型适配问题

解决jOOQ操作DuckDB时SUM聚合的类型映射问题

问题核心

jOOQ默认会将数值类型的SUM结果统一映射为BigDecimal,但DuckDB对整数列SUM返回HUGEINT(对应Java的BigInteger)、浮点列SUM返回Double——直接用JDBC查询能正确获取对应类型,但jOOQ的默认映射覆盖了这一行为,且直接强制转换Number会因方言未指定报错。

解决方案

1. 自定义数据类型Binding

针对SUM结果的动态类型需求,自定义Binding来适配DuckDB的原生返回类型:

import org.jooq.*;
import org.jooq.impl.DSL;
import java.sql.*;
import java.math.BigInteger;
import java.math.BigDecimal;

public class DuckDbSumBinding implements Binding<Object, Number> {

    @Override
    public Converter<Object, Number> converter() {
        return new Converter<>() {
            @Override
            public Number from(Object databaseObject) {
                if (databaseObject instanceof BigInteger) {
                    return (BigInteger) databaseObject;
                } else if (databaseObject instanceof Double) {
                    return (Double) databaseObject;
                } else if (databaseObject instanceof BigDecimal) {
                    BigDecimal bd = (BigDecimal) databaseObject;
                    return bd.scale() == 0 ? bd.toBigInteger() : bd.doubleValue();
                }
                return null;
            }

            @Override
            public Object to(Number userObject) {
                return userObject;
            }

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

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

    @Override
    public void sql(BindingSQLContext<Number> ctx) throws SQLException {
        ctx.render().visit(DSL.val(ctx.convert(converter()).value()));
    }

    @Override
    public void register(BindingRegisterContext<Number> ctx) throws SQLException {
        ctx.statement().registerOutParameter(ctx.index(), Types.OTHER);
    }

    @Override
    public void set(BindingSetStatementContext<Number> ctx) throws SQLException {
        ctx.statement().setObject(ctx.index(), ctx.convert(converter()).value());
    }

    @Override
    public void set(BindingSetSQLOutputContext<Number> ctx) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void get(BindingGetResultSetContext<Number> ctx) throws SQLException {
        ctx.convert(converter()).value(ctx.resultSet().getObject(ctx.index()));
    }

    @Override
    public void get(BindingGetStatementContext<Number> ctx) throws SQLException {
        ctx.convert(converter()).value(ctx.statement().getObject(ctx.index()));
    }

    @Override
    public void get(BindingGetSQLInputContext<Number> ctx) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }
}

2. 绑定自定义Binding执行查询

在动态表场景下,通过coerce+bind方法指定自定义Binding,让jOOQ按DuckDB原生类型处理结果:

// 加载Parquet表
Table<?> parquetTable = DSL.table("parquet_scan('path/to/your/file.parquet')");

// 为SUM聚合绑定自定义Binding
Field<Number> sumKI = DSL.sum(parquetTable.field("kI"))
                          .coerce(Number.class)
                          .bind(new DuckDbSumBinding());
Field<Number> sumKD = DSL.sum(parquetTable.field("kD"))
                          .coerce(Number.class)
                          .bind(new DuckDbSumBinding());

// 执行查询(必须指定DUCKDB方言)
Result<?> result = DSL.using(connection, SQLDialect.DUCKDB)
                      .select(sumKI, sumKD)
                      .from(parquetTable)
                      .fetch();

// 处理结果
Number kiSum = result.getValue(sumKI);
Number kdSum = result.getValue(sumKD);

if (kiSum instanceof BigInteger) {
    // 整数列SUM逻辑
} else if (kdSum instanceof Double) {
    // 浮点列SUM逻辑
}

3. 全局类型映射配置(可选)

若需全局生效,可在jOOQ配置中为DuckDB的HUGEINT类型指定映射:

Configuration configuration = new DefaultConfiguration()
    .set(connection)
    .set(SQLDialect.DUCKDB)
    .set(new DefaultDataTypeProvider() {
        @Override
        public DataType<?> defaultDataType(int sqlType) {
            if (sqlType == Types.OTHER && "HUGEINT".equals(getTypeName(sqlType))) {
                return SQLDataType.BIGINT.asConvertedDataType(new Converter<>() {
                    @Override
                    public BigInteger from(Object o) {
                        return (BigInteger) o;
                    }

                    @Override
                    public Object to(BigInteger bigInteger) {
                        return bigInteger;
                    }

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

                    @Override
                    public Class<BigInteger> toType() {
                        return BigInteger.class;
                    }
                });
            }
            return super.defaultDataType(sqlType);
        }
    });

关键注意点

  • 必须显式指定SQLDialect.DUCKDB,否则会触发“Type class java.lang.Number is not supported in dialect null”报错。
  • 自定义Binding的核心是在converter中根据DuckDB返回的实际类型动态转换,避免jOOQ统一转为BigDecimal。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 18:12:36