调用存储函数和过程
PostgreSQL® 支持两种类型的存储对象,可以返回结果值的函数,以及从 v11 开始支持执行事务控制的过程。两种类型的存储对象都使用 CallableStatement
和标准 JDBC 转义调用语法 {call storedobject(?)}
来调用。escapeSyntaxCallMode
连接属性控制驱动程序如何转换调用语法以调用函数或过程。
默认模式 select
支持现有应用程序的向后兼容性,并且仅支持函数调用。这是调用返回 void 的函数所必需的。
对于新应用程序,使用 escapeSyntaxCallMode=callIfNoReturn
将具有返回值的 CallableStatements
映射到存储函数,将没有返回值的 CallableStatements
映射到存储过程。
示例 6.1. 调用内置存储函数
此示例演示了如何调用 PostgreSQL® 内置函数 upper
,该函数只是将提供的字符串参数转换为大写。
CallableStatement upperFunc = conn.prepareCall("{? = call upper( ? ) }");
upperFunc.registerOutParameter(1, Types.VARCHAR);
upperFunc.setString(2, "lowercase to uppercase");
upperFunc.execute();
String upperCased = upperFunc.getString(1);
upperFunc.close();
从存储函数获取ResultSet
PostgreSQL™ 的存储函数可以通过两种不同的方式返回结果。函数可以返回一个 refcursor 值或一个SETOF
数据类型。根据使用哪种返回方法来确定如何调用函数。
从返回SETOF
类型的函数
返回数据为集合的函数不应通过CallableStatement
接口调用,而应使用正常的Statement
或PreparedStatement
接口。
示例 6.2. 从函数获取SETOF
类型的值
Statement stmt = conn.createStatement();
stmt.execute("CREATE OR REPLACE FUNCTION setoffunc() RETURNS SETOF int AS " +
"' SELECT 1 UNION SELECT 2;' LANGUAGE sql");
ResultSet rs = stmt.executeQuery("SELECT * FROM setoffunc()");
while (rs.next()) {
// do something
}
rs.close();
stmt.close();
从返回 refcursor 的函数
当调用返回 refcursor 的函数时,必须将 getObject
的返回值类型转换为 ResultSet。
注意
当前对从 refcursor 创建的
ResultSet
的支持的一个显著限制是,即使它是一个由游标支持的ResultSet
,所有数据都将在客户端检索和缓存。在名为 基于游标获取结果 的部分中描述的Statement
获取大小参数将被忽略。此限制是 JDBC 驱动程序的缺陷,而不是服务器的缺陷,从技术上讲可以消除它,只是我们还没有找到时间。
示例 6.3. 从函数获取 refcursor 值
// Setup function to call.
Statement stmt = conn.createStatement();
stmt.execute("CREATE OR REPLACE FUNCTION refcursorfunc() RETURNS refcursor AS '" +
" DECLARE " +
" mycurs refcursor; " +
" BEGIN " +
" OPEN mycurs FOR SELECT 1 UNION SELECT 2; " +
" RETURN mycurs; " +
" END;' language plpgsql");
stmt.close();
// We must be inside a transaction for cursors to work.
conn.setAutoCommit(false);
// Function call.
CallableStatement func = conn.prepareCall("{? = call refcursorfunc() }");
func.registerOutParameter(1, Types.OTHER);
func.execute();
ResultSet results = (ResultSet) func.getObject(1);
while (results.next()) {
// do something with the results.
}
results.close();
func.close();
也可以将 refcursor 返回值直接视为游标名称。为此,请使用 ResultSet
的 getString
。有了底层游标名称,您可以自由地在它上面直接使用游标命令,例如 FETCH
和 MOVE
。
示例 6.4. 将 refcursor 视为游标名称
conn.setAutoCommit(false);
CallableStatement func = conn.prepareCall("{? = call refcursorfunc() }");
func.registerOutParameter(1, Types.OTHER);
func.execute();
String cursorName = func.getString(1);
func.close();
示例 6.5. 调用存储过程
此示例展示了如何调用使用事务控制的 PostgreSQL® 过程。
// set up a connection
String url = "jdbc:postgresql:///test";
Properties props = new Properties();
...other properties...
// Ensure EscapeSyntaxCallmode property set to support procedures if no return value
props.setProperty("escapeSyntaxCallMode", "callIfNoReturn");
Connection con = DriverManager.getConnection(url, props);
// Setup procedure to call.
Statement stmt = con.createStatement();
stmt.execute("CREATE TEMP TABLE temp_val ( some_val bigint )");
stmt.execute("CREATE OR REPLACE PROCEDURE commitproc(a INOUT bigint) AS '" +
" BEGIN " +
" INSERT INTO temp_val values(a); " +
" COMMIT; " +
" END;' LANGUAGE plpgsql");
stmt.close();
// As of v11, we must be outside a transaction for procedures with transactions to work.
con.setAutoCommit(true);
// Procedure call with transaction
CallableStatement proc = con.prepareCall("{call commitproc( ? )}");
proc.setInt(1, 100);
proc.execute();
proc.close();