我正在通过JDBC使用JDBI连接到Oracle数据库。我们有存储过程,可以将用户定义的对象作为out参数返回。我可以使用JDBC来调用它,但是看不到如何使用JDBI。
示例,假设我在数据库中将对象定义为
create or replace type myObj as object
( id varchar2(11),
text varchar2(4000) )
/
我有一个带有功能的包装
FUNCTION foo ( pId in varchar2, pMyObj_o out myObj )
RETURN NUMBER IS
...
然后,使用JDBI,我可以这样称呼它:
String sql = "{ ? = call my_package.foo(?, ?) }";
CallableStatement cstmt = getConnection().prepareCall(sql);
cstmt.registerOutParameter(1, oracle.jdbc.OracleTypes.INTEGER);
cstmt.setString(2, 1);
cstmt.registerOutParameter(3, oracle.jdbc.OracleTypes.STRUCT, "MYSCHEMA.MYOBJ");
但是,我看不到如何使用命名参数对JDBI进行此操作。 JDBI似乎有两个版本registerOutParameter
。基本的只是一个ID和类型,但是如果我叫它
String sql = "{ :numRows = call my_package.foo(:id, :obj) }";
Integer num = getJdbi().withHandle( handle -> {
OutParameters outParams = handle.createCall(sql)
.bind("id", "1")
.registerOutParameter("obj", Types.STRUCT)
.invoke();
return outParams.getInt("numRows");
});
然后我得到一个错误 org.jdbi.v3.core.statement.UnableToCreateStatementException:绑定命名参数obj时发生异常
唯一的选择是registerOutParameter
,它带有第三个参数CallableStatementMapper
,但我真的找不到如何使用它的任何示例。
我试图定义一个类
public class ObjRecErrMsg implements SQLData {
private String sQLTypeName;
private String id;
private String text;
public ObjRecErrMsg() {}
public ObjRecErrMsg(String SqlType, String Id, String Text) {
this.sQLTypeName = SqlType;
this.id = Id;
this.text = Text;
}
@Override
public String getSQLTypeName() throws SQLException {
return sQLTypeName;
}
@Override
public void readSQL(SQLInput stream, String typeName) throws SQLException {
sQLTypeName = typeName;
id = stream.readString();
text = stream.readString();
}
@Override
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(getId());
stream.writeString(getText());
}
public String getId() {
return id;
}
public String getText() {
return text;
}
}
如果我随后将该映射添加到连接中,则可以使用它,并且它可以与JDBC一起使用:
Connection conn = getConnection();
Map<String, Class<?>> newMap = conn.getTypeMap();
if( newMap == null ) {
newMap = new HashMap<String, Class<?>>();
}
newMap.put(MYSCHEMA.MYOBJ", MyObj.class);
conn.setTypeMap(newMap);
String sql = "{ ? = call my_package.foo(?, ?) }";
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.registerOutParameter(1, oracle.jdbc.OracleTypes.INTEGER);
cstmt.setString(2, 1);
cstmt.registerOutParameter(3, oracle.jdbc.OracleTypes.STRUCT, "MYSCHEMA.MYOBJ");
并将对象作为
msg = (MyObj)cstmt.getObject(3);
但是,当我尝试如下定义CallableStatementMapper
时:
public class ObjWrapper implements CallableStatementMapper {
public ObjWrapper() {
// TODO Auto-generated constructor stub
}
@Override
public Object map(int position, CallableStatement stmt) throws SQLException {
return (MyObj) stmt.getObject(position);
}
}
并在JDBI调用中使用它:
String sql = "{ :numRows = call my_package.foo(:id, :obj) }";
Integer num = getJdbi().withHandle( handle -> {
Map<String, Class<?>> newMap = handle.getConnection().getTypeMap();
if( newMap == null ) {
newMap = new HashMap<String, Class<?>>();
}
newMap.put("C20.OBJRECERRMSG", ObjRecErrMsg.class);
handle.getConnection().setTypeMap(newMap);
OutParameters outParams = handle.createCall(sql)
.bind("id", "1")
.registerOutParameter("obj", Types.STRUCT, new ObjWrapper ())
.invoke();
return outParams.getInt("numRows");
});
然后,在绑定命名参数时,我仍然会收到 Exception。
所以,主要问题是如何使用带有out参数(它是用户定义的对象)来调用存储过程?
子问题:
CallableStatementMapper
的用途是什么?