PostgreSQL JDBC调用存储过程prepareCall自动转为select语句问题咨询
问题原因
- PostgreSQL 11 才正式引入
PROCEDURE对象(此前只有FUNCTION),42.2.5版本的PostgreSQL JDBC驱动属于较早版本,对PROCEDURE的兼容逻辑不完善:默认会将JDBC的{call ...}转义语法按照旧版本的FUNCTION调用规则,转换为select * from 函数名(参数)的形式——FUNCTION可以通过SELECT语句调用,而PROCEDURE只能通过CALL语句调用,因此直接执行转换后的SQL就会触发你遇到的报错。 - 驱动默认的转义规则下,只有当检测到调用对象没有返回值时才会生成
CALL语句,否则都会优先生成SELECT查询语句。你的存储过程带有出参,驱动会误判为有返回值的FUNCTION,因此触发了错误转换。
正确调用方案
方案1:升级JDBC驱动(推荐)
将PostgreSQL JDBC驱动升级到42.2.16及以上版本,该版本开始已经完善了对PG11+版本PROCEDURE的识别逻辑,默认就可以正确处理带出参的存储过程调用,不需要额外修改代码逻辑。
方案2:修改驱动转义规则(不升级驱动的临时方案)
如果暂时不能升级驱动,可以在JDBC连接URL中添加参数escapeSyntaxCallMode=call,强制驱动将所有{call ...}转义语法直接转换为原生CALL语句,不会再转成SELECT查询,示例连接URL格式:jdbc:postgresql://host:port/dbname?escapeSyntaxCallMode=call
正确代码示例
CallableStatement callableStatement = null; // 继续使用原来的转义语法即可 String sqlCall = "{call my_schema.my_procedure(?,?,?,?,?,?)}"; callableStatement = connection.prepareCall(sqlCall); // 注册第一个出参,按你实际参数类型调整 callableStatement.registerOutParameter(1, Types.INTEGER); // 按顺序设置其余入参 callableStatement.setInt(2, xxx); callableStatement.setString(3, xxx); // 其余参数设置省略... // 执行用execute(),不要用executeQuery() callableStatement.execute(); // 读取出参 Integer outValue = callableStatement.getInt(1);
配置关联说明
该问题和数据库服务端配置、存储过程本身的配置无关,完全是PostgreSQL JDBC驱动的版本特性和转义规则导致的问题,按上述方案调整驱动版本或参数即可解决。
内容的提问来源于stack exchange,提问作者NEON
相关产品推荐
相关产品推荐

