JDBI用@BindList结合CAST枚举数组查询报UnableToCreateStatementException
报错核心根因
- JDBI的SQL解析器没有从你编写的查询字符串中识别到任何合法的命名参数占位符,但
@BindList注解执行绑定时,会向参数上下文注入名为userType的命名参数,两者不匹配就触发了“提供了多余的命名参数,但当前查询未声明任何命名参数”的异常。
具体触发原因:- 自定义占位符不符合JDBI对
@BindList的识别规则。JDBI规定@BindList的匹配占位符格式为尖括号直接包裹参数名,即<userType>,你在尖括号内部拼接的CAST语法、类型转换逻辑,会让解析器跳过对该位置的参数名识别,整个位置不会被判定为可绑定的命名参数。 - 占位符结尾使用了中文全角右括号“)”,这类非ASCII字符会直接打断SQL解析流程,进一步导致占位符识别失效。
- 对
@BindList的能力存在认知偏差:@BindList仅负责将传入列表拆分为独立单值绑定,自动展开为(?, ?, ?)的IN条件格式,不支持数据库数组类型的对象序列化转换,也不会自动处理枚举类型到数据库自定义类型的映射。
- 自定义占位符不符合JDBI对
正确实现方案
根据实际查询逻辑,两种可直接运行的写法:
常规IN列表匹配(最常用场景)
如果需求是匹配类型值在传入枚举列表内的记录,不需要用到PostgreSQL数组语法,直接调整SQL占位符为JDBI可识别的格式:
@SqlQuery("Select id from user where type in (<userType>)") List<Long> getUserIdsByListOfTypes(@BindList("userType") List<User.UserType> userType);
配套配置:给JDBI实例注册Java枚举与数据库user_type类型的双向映射,确保枚举值可以正确作为绑定参数、查询结果可以正确映射回Java枚举,避免类型不匹配异常。
PostgreSQL数组语法匹配
如果查询逻辑需要使用数组类型传参,配合ANY()函数做匹配,就不要用@BindList做占位符展开,改用单个数组参数绑定,显式声明类型转换:
@SqlQuery("Select id from user where type = ANY(?::user_type[])") List<Long> getUserIdsByListOfTypes(@Bind("userType") User.UserType[] userType);
配套配置:给JDBI注册枚举数组的参数工厂,支持把Java枚举数组正确序列化为数据库侧的user_type[]类型。
避坑说明
- 不要在
@BindList对应的尖括号占位符内部拼接自定义SQL片段,这类占位符的唯一作用是标记需要展开列表的位置,自定义拼接内容会直接破坏识别规则,导致参数绑定失效。 - 编写SQL字符串时统一使用半角英文字符,全角符号会造成解析逻辑异常。
- JDBI 3.x版本默认支持列表占位符解析,不需要额外开启配置,只要占位符格式正确即可正常识别。
内容的提问来源于stack exchange,提问作者Swarnim Kumar
相关产品推荐
相关产品推荐

