如何使用 JdbcTemplate 在 Spring 中简洁地调用 PL/SQL 过程和 return 其输出变量?
How to succinctly call a PL/SQL procedure and return its output variables in Spring with JdbcTemplate?
这里是 Oracle 的新手。我的经验是构建将查询发送到数据库并获得 "result set" 返回的 Web 应用程序,例如在 Java 中使用 Spring 和 JdbcTemplate.
例如,这是一个带有常规 SQL 查询的代码示例,使用 RowMapper
class 将结果集的行转换为 Java 列表:
public List<Widget> findAllWidgets() {
return jdbcTemplate.query("SELECT * FROM widgets", new WidgetRowMapper());
}
现在我的任务是编写调用 PL/SQL 存储过程的查询。这个过程有两个输入参数(我可以处理)和两个输出参数(我们称它们为 error_code
和 error_message
)。我想向数据库发送一个查询,该查询将 (a) 运行 使用我的输入的过程和 (b) return 两个输出。我同样乐意将两个输出作为 "row" 或简单地将它们绑定到两个 Java 变量。
这是我试过的方法,它没有抛出错误,但也没有获得输出值。 Java 变量 errorCode
和 errorMessage
保持空字符串:
public Map<String,String> callMyProcedure() {
String errorCode="";
String errorMessage="";
jdbcTemplate.update("call myprocedure(?,?,?,?)","input1","input2",errorCode,errorMessage);
return Map.of("errorCode",errorCode,"errorMessage",errorMessage);
}
问题是:当使用 JdbcTemplate 从 Java 调用过程时,如何捕获 PL/SQL 过程的 "OUT" 变量的值?
编辑:我接受了 Alex 不使用 JdbcTemplate 的回答,因为这似乎是更好的方法。我自己的回答 确实 使用 JdbcTemplate 但需要更多代码,因此如果您要搜索专门回答问题的内容,那就可以了。
我从 older question here 那里找到了一些指导,并想出了这个怪诞的东西:
public Map<String,Object> callMyProcedure() {
return jdbcTemplate.call(new CallableStatementCreator() {
@Override
public CallableStatement createCallableStatement(Connection connection) throws SQLException {
CallableStatement cs = connection.prepareCall("{call myprocedure(?,?,?,?,?,?,?,?)}");
cs.setString(1,"foo");
cs.setString(2,"bar");
cs.setString(3,"baz");
cs.setString(4,"whisky");
cs.setString(5,"tango");
cs.setString(6,"foxtrot");
cs.registerOutParameter(7, Types.VARCHAR);
cs.registerOutParameter(8, Types.VARCHAR);
return cs;
}
},Arrays.asList(
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlOutParameter("errorCode",Types.VARCHAR),
new SqlOutParameter("errorMessage",Types.VARCHAR)
));
}
它有效,但我正在寻找一个可以更简洁地完成同样事情的答案。也许 Spring 在那个旧答案之后的几年里为 JdbcTemplate 添加了一个新接口?
您可以在 jdbcTemplate
下获取连接并使用 get 方法获取输出,如 getNString
try (Connection connection = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
CallableStatement statement = connection.prepareCall("{call myprocedure(?,?,?,?,?,?,?,?)}");
statement.execute();
statement.getNString(1); // using index or your parameter name
Retrieves the value of the designated NCHAR, NVARCHAR or LONGNVARCHAR parameter as a String in the Java programming language.
你可以使用普通的 JDBC.
final String charlie;
final String zulu;
try (CallableStatement cs = connection.prepareCall("{call myprocedure(?,?,?,?,?,?,?,?)}")) {
cs.setString(1, "foo");
cs.setString(2, "bar");
cs.setString(3, "baz");
cs.setString(4, "whisky");
cs.setString(5, "tango");
cs.setString(6, "foxtrot");
cs.registerOutParameter(7, Types.VARCHAR);
cs.registerOutParameter(8, Types.VARCHAR);
cs.execute();
connection.commit(); // optional
charlie = cs.getString(7);
zulu = cs.getString(8);
}
当使用JDBC时,使用getInt
方法和类似的方法是危险的,因为它们将类型转换为原始类型并将零替换为0。最好使用(Integer) cs.getObject()
。同样,setInt
不支持引用类型。
这里是 Oracle 的新手。我的经验是构建将查询发送到数据库并获得 "result set" 返回的 Web 应用程序,例如在 Java 中使用 Spring 和 JdbcTemplate.
例如,这是一个带有常规 SQL 查询的代码示例,使用 RowMapper
class 将结果集的行转换为 Java 列表:
public List<Widget> findAllWidgets() {
return jdbcTemplate.query("SELECT * FROM widgets", new WidgetRowMapper());
}
现在我的任务是编写调用 PL/SQL 存储过程的查询。这个过程有两个输入参数(我可以处理)和两个输出参数(我们称它们为 error_code
和 error_message
)。我想向数据库发送一个查询,该查询将 (a) 运行 使用我的输入的过程和 (b) return 两个输出。我同样乐意将两个输出作为 "row" 或简单地将它们绑定到两个 Java 变量。
这是我试过的方法,它没有抛出错误,但也没有获得输出值。 Java 变量 errorCode
和 errorMessage
保持空字符串:
public Map<String,String> callMyProcedure() {
String errorCode="";
String errorMessage="";
jdbcTemplate.update("call myprocedure(?,?,?,?)","input1","input2",errorCode,errorMessage);
return Map.of("errorCode",errorCode,"errorMessage",errorMessage);
}
问题是:当使用 JdbcTemplate 从 Java 调用过程时,如何捕获 PL/SQL 过程的 "OUT" 变量的值?
编辑:我接受了 Alex 不使用 JdbcTemplate 的回答,因为这似乎是更好的方法。我自己的回答 确实 使用 JdbcTemplate 但需要更多代码,因此如果您要搜索专门回答问题的内容,那就可以了。
我从 older question here 那里找到了一些指导,并想出了这个怪诞的东西:
public Map<String,Object> callMyProcedure() {
return jdbcTemplate.call(new CallableStatementCreator() {
@Override
public CallableStatement createCallableStatement(Connection connection) throws SQLException {
CallableStatement cs = connection.prepareCall("{call myprocedure(?,?,?,?,?,?,?,?)}");
cs.setString(1,"foo");
cs.setString(2,"bar");
cs.setString(3,"baz");
cs.setString(4,"whisky");
cs.setString(5,"tango");
cs.setString(6,"foxtrot");
cs.registerOutParameter(7, Types.VARCHAR);
cs.registerOutParameter(8, Types.VARCHAR);
return cs;
}
},Arrays.asList(
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlParameter(Types.VARCHAR),
new SqlOutParameter("errorCode",Types.VARCHAR),
new SqlOutParameter("errorMessage",Types.VARCHAR)
));
}
它有效,但我正在寻找一个可以更简洁地完成同样事情的答案。也许 Spring 在那个旧答案之后的几年里为 JdbcTemplate 添加了一个新接口?
您可以在 jdbcTemplate
下获取连接并使用 get 方法获取输出,如 getNString
try (Connection connection = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
CallableStatement statement = connection.prepareCall("{call myprocedure(?,?,?,?,?,?,?,?)}");
statement.execute();
statement.getNString(1); // using index or your parameter name
Retrieves the value of the designated NCHAR, NVARCHAR or LONGNVARCHAR parameter as a String in the Java programming language.
你可以使用普通的 JDBC.
final String charlie;
final String zulu;
try (CallableStatement cs = connection.prepareCall("{call myprocedure(?,?,?,?,?,?,?,?)}")) {
cs.setString(1, "foo");
cs.setString(2, "bar");
cs.setString(3, "baz");
cs.setString(4, "whisky");
cs.setString(5, "tango");
cs.setString(6, "foxtrot");
cs.registerOutParameter(7, Types.VARCHAR);
cs.registerOutParameter(8, Types.VARCHAR);
cs.execute();
connection.commit(); // optional
charlie = cs.getString(7);
zulu = cs.getString(8);
}
当使用JDBC时,使用getInt
方法和类似的方法是危险的,因为它们将类型转换为原始类型并将零替换为0。最好使用(Integer) cs.getObject()
。同样,setInt
不支持引用类型。