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

JDBI调用Dremio时SQL预编译异常求助

问题分析:Jdbi调用Dremio SQL失败但UI执行正常

问题背景

我用Java结合Jdbi编写了查询Dremio中Iceberg数据源的方法,传入参数后执行报错,但相同的SQL在Dremio UI中可以正常运行。

Java查询方法代码

@Retry
@SqlQuery("""
        WITH IdsGrouped AS (
            SELECT /* comment */
                record_key,
                max(gen_datetime) as max_dt
            FROM "ad".iceberg_data.<sourceSchema.schema>_<sourceSchema.tablePrefix>
            WHERE record_key IN (<ids>)
            GROUP BY record_key
        )
        SELECT *
        FROM "ad".iceberg_data.<sourceSchema.schema>_<sourceSchema.tablePrefix>
        WHERE (record_key, gen_datetime) IN (SELECT * FROM IdsGrouped)
        """)
@UseStringTemplateEngine
@RegisterRowMapper(MapMapper.class)
List<Map<String, Object>> findDataById(@Define("sourceSchema") SourceSchema sourceSchema,
                                 @DefineCollection(value = "ids") Collection<String> ids);

报错信息

org.jdbi.v3.core.statement.UnableToCreateStatementException: java.sql.SQLException: while preparing SQL:
    WITH IdsGrouped AS (
        SELECT /* comment */ 
        record_key,
        max(gen_datetime) as max_dt 
        FROM "ad".iceberg_data.bl_merged WHERE record_key IN ('69823926','70674377','101113') 
        GROUP BY record_key
        ) 
     SELECT * 
     FROM "ad".iceberg_data.bl_merged 
     WHERE (record_key, gen_datetime) IN (SELECT * FROM IdsGrouped)

堆栈跟踪信息

at com.btg.fit.nex.pln.common.persistence.decorator.RetryDecorator.lambda$decorateHandler$1(RetryDecorator.java:35)
at net.jodah.failsafe.Functions.lambda$get$0(Functions.java:48)
at net.jodah.failsafe.RetryPolicyExecutor.lambda$supply$0(RetryPolicyExecutor.java:62)
at net.jodah.failsafe.Execution.executeSync(Execution.java:129)
at net.jodah.failsafe.FailsafeExecutor.call(FailsafeExecutor.java:376)
at net.jodah.failsafe.FailsafeExecutor.get(FailsafeExecutor.java:67)
at com.btg.fit.nex.pln.common.persistence.decorator.RetryDecorator.lambda$decorateHandler$2(RetryDecorator.java:31)
at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.lambda$invoke$0(SqlObjectInitData.java:132)
at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:44)
at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:26)
at org.jdbi.v3.core.LazyHandleSupplier.lambda$invokeInContext$1(LazyHandleSupplier.java:82)
at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:44)
at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:26)
at org.jdbi.v3.core.LazyHandleSupplier.invokeInContext(LazyHandleSupplier.java:81)
at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.call(SqlObjectInitData.java:138)
at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.invoke(SqlObjectInitData.java:132)
at org.jdbi.v3.sqlobject.SqlObjectFactory.lambda$attach$2(SqlObjectFactory.java:108)
at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$invoke$5(OnDemandExtensions.java:98)
at org.jdbi.v3.core.internal.exceptions.Unchecked.lambda$function$4(Unchecked.java:76)
at org.jdbi.v3.core.internal.OnDemandExtensions.invoke(OnDemandExtensions.java:98)
at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$2(OnDemandExtensions.java:82)
at org.jdbi.v3.core.Jdbi.callWithExtension(Jdbi.java:491)
at org.jdbi.v3.core.Jdbi.withExtension(Jdbi.java:478)
at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$3(OnDemandExtensions.java:82)
at jdk.proxy2/jdk.proxy2.$Proxy67.findDataById(Unknown Source)
at com.btg.fit.nex.pln.logistics.precombination.Fetcher.findDataById(Fetcher.java:68)

自定义Retry装饰器定义

@SqlMethodDecoratingAnnotation(RetryDecorator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Retry {
    int value() default 3;
}

可能的原因及排查方向

1. Dremio JDBC驱动兼容性问题

Dremio的UI和JDBC驱动对SQL语法的解析可能存在差异,尤其是多列IN子句(record_key, gen_datetime) IN (...)这种语法,部分旧版本JDBC驱动可能不支持Iceberg表的该语法特性。

2. Jdbi字符串模板的参数处理问题

虽然生成的SQL看起来正确,但@UseStringTemplateEngine配合@DefineCollection直接拼接字符串到IN子句,可能存在隐藏的格式问题(比如特殊字符转义、空格处理),导致JDBC端解析SQL失败。

3. 自定义重试装饰器的上下文问题

自定义的RetryDecorator在重试过程中可能没有正确维护Jdbi的Handle上下文,导致Statement创建时出现异常,比如重复绑定参数或Handle未正确复用。

4. 预编译参数替代字符串拼接的必要性

直接用@DefineCollection拼接IN子句属于静态SQL拼接,而JDBC驱动通常更期望预编译参数方式,这种拼接可能触发驱动的语法检查机制,导致预编译失败。

排查建议

  • 改用预编译参数绑定:将SQL中的<ids>替换为:ids,参数注解换成@BindList("ids") Collection<String> ids,避免直接字符串拼接,同时确保Jdbi配置启用多值绑定支持。
  • 升级Dremio JDBC驱动:更新到最新版本的Dremio JDBC驱动,验证对Iceberg表和多列IN语法的支持。
  • 临时禁用重试装饰器:去掉方法上的@Retry注解,测试是否是重试逻辑导致的上下文异常。
  • 纯JDBC测试生成的SQL:将报错信息中生成的完整SQL复制出来,用纯JDBC代码执行,排除Jdbi框架的影响,确认驱动本身是否支持该SQL。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 23:24:54