Java HSQLDB在准备callablestatement时为过程设置了错误的输入参数名称
这件事快把我逼疯了 我在一个junit测试中使用HSQLDB作为内存数据库,测试的是我为调用存储过程而编写的一组通用类 初始化数据库: 然后在准备语句的类中的某个点,我使用setObject方法注册输入参数:Java HSQLDB在准备callablestatement时为过程设置了错误的输入参数名称,java,stored-procedures,jdbc,hsqldb,Java,Stored Procedures,Jdbc,Hsqldb,这件事快把我逼疯了 我在一个junit测试中使用HSQLDB作为内存数据库,测试的是我为调用存储过程而编写的一组通用类 初始化数据库: 然后在准备语句的类中的某个点,我使用setObject方法注册输入参数: ... CallableStatement callableStatement = con.prepareCall("{call sp_say_hi(?)}"); callableStatement.setObject("greeting_p", "Hola"); ... setObje
...
CallableStatement callableStatement = con.prepareCall("{call sp_say_hi(?)}");
callableStatement.setObject("greeting_p", "Hola");
...
setObject方法引发JavaSqlException,原因如下:
Caused by: org.hsqldb.HsqlException: Column not found: greeting_p
at org.hsqldb.error.Error.error(Unknown Source)
at org.hsqldb.error.Error.error(Unknown Source)
通过使用调试器,我发现org.hsqldb.jdbc.JDBCCallableStatement类中有一个包私有方法,它是hsqldb对CallableStatement的实现,方法名为findParameterIndex,在setObject方法中调用,检查提供的参数是否存在于程序的参数映射中:
int findParameterIndex(String parameterName) throws SQLException {
if (isClosed || connection.isClosed) {
checkClosed();
}
int index = parameterNameMap.get(parameterName, -1);
if (index >= 0) {
return index + 1;
}
throw JDBCUtil.sqlException(ErrorCode.JDBC_COLUMN_NOT_FOUND,
parameterName);
}
通过使用调试器查看该映射,我能够看到hsqldb错误地设置了参数名,至少在该映射中是这样的:
[@p1, null, null, null, null, null, null, null]
我可以通过更改setObject方法调用中的名称进行验证:
callableStatement.setObject("@p1", "Hola");
在那之后,它工作得很好
奇怪的是,如果我使用DatabaseMetaData.getprocedureclumns方法检索该过程的元数据,那么从jdbc元数据的角度来看,参数的名称是正确的:
DatabaseMetaData dbMetadata = con.getMetaData();
ResultSet rs = dbMetadata.getProcedureColumns(con.getCatalog(),
con.getSchema(),
"SP_SAY_HI",
"%_P");
while(rs.next()) {
// get stored procedure metadata
String procedureCatalog = rs.getString(1);
String procedureSchema = rs.getString(2);
String procedureName = rs.getString(3);
String columnName = rs.getString(4);
short columnReturn = rs.getShort(5);
int columnDataType = rs.getInt(6);
String columnReturnTypeName = rs.getString(7);
int columnPrecision = rs.getInt(8);
int columnByteLength = rs.getInt(9);
short columnScale = rs.getShort(10);
short columnRadix = rs.getShort(11);
short columnNullable = rs.getShort(12);
String columnRemarks = rs.getString(13);
System.out.println("stored Procedure name="+procedureName);
System.out.println("procedureCatalog=" + procedureCatalog);
System.out.println("procedureSchema=" + procedureSchema);
System.out.println("procedureName=" + procedureName);
System.out.println("columnName=" + columnName);
System.out.println("columnReturn=" + columnReturn);
System.out.println("columnDataType=" + columnDataType);
System.out.println("columnReturnTypeName=" + columnReturnTypeName);
System.out.println("columnPrecision=" + columnPrecision);
System.out.println("columnByteLength=" + columnByteLength);
System.out.println("columnScale=" + columnScale);
System.out.println("columnRadix=" + columnRadix);
System.out.println("columnNullable=" + columnNullable);
System.out.println("columnRemarks=" + columnRemarks);
}
它打印出:
stored Procedure name=SP_SAY_HI
procedureCatalog=PUBLIC
procedureSchema=PUBLIC
procedureName=SP_SAY_HI
columnName=GREETING_P
columnReturn=1
columnDataType=12
columnReturnTypeName=CHARACTER VARYING
columnPrecision=10
columnByteLength=0
columnScale=0
columnRadix=0
columnNullable=1
columnRemarks=null
注意,名称是大写的,我已经检查过了。我在setObject方法中将参数名的注册改为大写,以查看这是否有帮助,但没有任何区别
更新:
我在HSQLDB issue tracking system中创建了一个票证,他们刚刚修复了它并将其提交到当前realease:我认为在将变量绑定到存储过程调用时,使用继承自的setX方法(例如: 从以下的JavaDocs: 当方法prepareCall完成时,一些驱动程序可能会将调用语句发送到数据库;其他人可能会等待执行CallableStatement对象
如果驱动程序在调用execute之前没有与DB通信,那么它也无法知道参数的名称,因为JDBC查询中的绑定变量总是匿名的。当绑定发生时,HSQLDB驱动程序似乎确实不知道实际的参数名,因为它在内部使用占位符名@p1等 我在HSQLDB问题跟踪系统中创建了一个记录单,他们刚刚修复了它并提交到下一个版本,了解更多信息:您所说的很有意义,因为它解释了为什么一些框架,如Spring JdbcTemplate,特别是SimpleJDBCall及其助手类在设置参数时使用基于位置的方法,从他们的代码中我看到,他们所做的是首先使用db元数据检索参数的位置,例如使用GetProcedureClumns方法,然后在setX方法中使用该位置。然而,有趣的是,如果驱动程序在该阶段没有与db通信,那么它如何知道'@p1'参数的存在。如何在变量sqlCall中,用于过程调用的SQL看起来像吗?如果它有绑定占位符问号,那么驱动程序知道需要多少参数。@raspacorp-我同意Mick的评估。我不认为这是您或HSQLDB做得不正确的问题,可能只是HSQLDB在.set中赋值时不支持使用参数名。。。声明。HSQLDB没有提到可选参数,因此如果所有参数都必须在调用中表示,那么它们的值可以简单地由数字1..n按照过程声明定义的顺序提供。@MickMnemonic抱歉,我没有在问题中提供sqlCall的值,我只是更新了它:{CALL sp_say_hi}谢谢你的最新更新。请考虑在这个问题上添加你自己的答案,表明这个问题将被固定在下一版本的HQLDB中。
stored Procedure name=SP_SAY_HI
procedureCatalog=PUBLIC
procedureSchema=PUBLIC
procedureName=SP_SAY_HI
columnName=GREETING_P
columnReturn=1
columnDataType=12
columnReturnTypeName=CHARACTER VARYING
columnPrecision=10
columnByteLength=0
columnScale=0
columnRadix=0
columnNullable=1
columnRemarks=null
callableStatement.setString(1, "Hola");