使用含输出参数的存储过程

下载 JDBC 驱动程序

可以调用的 SQL Server 存储过程是返回一个或多个 OUT 参数的存储过程,存储过程使用这些参数将数据返回到调用它的应用程序。 可以使用 Microsoft JDBC Driver for SQL Server 提供的 SQLServerCallableStatement 类,调用此类存储过程并处理其返回的数据。

使用 JDBC 驱动程序调用此类存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。 带有 OUT 参数的 call 转义序列的语法如下所示:

{call procedure-name[([parameter][,[parameter]]...)]}

注意

若要详细了解 SQL 转义序列,请参阅使用 SQL 转义序列

构造 call 转义序列时,请使用 ?(问号)字符 来指定 IN 参数。 此字符充当要从该存储过程返回的参数值的占位符。 要为 OUT 参数指定值,必须在运行存储过程前使用 SQLServerCallableStatement 类的 registerOutParameter 方法指定各参数的数据类型。

使用 registerOutParameter 方法为 OUT 参数指定的值必须是 java.sql.Types 所包含的 JDBC 数据类型之一,而它又被映射成本地 SQL Server 数据类型之一。 若要详细了解 JDBC 和 SQL Server 数据类型,请参阅了解 JDBC 驱动程序数据类型

对于 OUT 参数向 registerOutParameter 方法传递一个值时,不仅必须指定要用于此参数的数据类型,而且必须在存储过程中指定此参数的序号位置或此参数的名称。 例如,如果存储过程包含单个 OUT 参数,则其序数值为 1;如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2。

注意

JDBC 驱动程序不支持将 CURSOR、SQLVARIANT、TABLE 和 TIMESTAMP SQL Server 数据类型用作 OUT 参数。

作为示例,在 AdventureWorks2022 示例数据库中创建以下存储过程:

CREATE PROCEDURE GetImmediateManager  
   @employeeID INT,  
   @managerID INT OUTPUT  
AS  
BEGIN  
   SELECT @managerID = ManagerID
   FROM HumanResources.Employee
   WHERE EmployeeID = @employeeID  
END

根据指定的整数 IN 参数 (employeeID),该存储过程也返回单个整数 OUT 参数 (managerID)。 根据 HumanResources.Employee 表中包含的 EmployeeID,OUT 参数中返回的值为 ManagerID。

在下面的实例中,将向此函数传递 AdventureWorks2022 示例数据库的开放式连接,然后使用 execute 方法调用 GetImmediateManager 存储过程:

public static void executeStoredProcedure(Connection con) throws SQLException {  
    try(CallableStatement cstmt = con.prepareCall("{call dbo.GetImmediateManager(?, ?)}");) {  
        cstmt.setInt(1, 5);  
        cstmt.registerOutParameter(2, java.sql.Types.INTEGER);  
        cstmt.execute();  
        System.out.println("MANAGER ID: " + cstmt.getInt(2));  
    }  
}

本示例使用序号位置来标识参数。 或者,也可以使用参数的名称(而非其序号位置)来标识此参数。 下面的代码示例修改了上一个示例,以说明如何在 Java 应用程序中使用命名参数。 请注意,这些参数名称对应于存储过程的定义中的参数名称:

public static void executeStoredProcedure(Connection con) throws SQLException {  
    try(CallableStatement cstmt = con.prepareCall("{call dbo.GetImmediateManager(?, ?)}"); ) {  
        cstmt.setInt("employeeID", 5);  
        cstmt.registerOutParameter("managerID", java.sql.Types.INTEGER);  
        cstmt.execute();  
        System.out.println("MANAGER ID: " + cstmt.getInt("managerID"));  
    }  
}

对于版本 12.6 及更高版本的驱动程序,引入了新的连接属性 useFlexibleCallableStatements。 如果设置为 true,此属性将保留驱动程序的旧行为,以便用户能够在标识参数时使用序号位置和参数名称的组合。 如果设置为 false,用户必须使用其中一个,但不能同时使用两者。

此外,如果将属性设置为 true,则 useFlexibleCallableStatements 将保留可在其中设置有关灵活顺序语句参数的现有行为。 但是,如果设置为 false,则顺序必须与存储过程定义匹配。 在以下示例中可以看到 useFlexibleCallableStatements=true 的这两项功能:

public static void executeStoredProcedure(Connection con) throws SQLException {  
    try(CallableStatement cstmt = con.prepareCall("{call dbo.GetImmediateManager(?, ?)}"); ) {
        cstmt.registerOutParameter("managerID", java.sql.Types.INTEGER);
        cstmt.setInt(1, 5);  
        cstmt.execute();  
        System.out.println("MANAGER ID: " + cstmt.getInt("managerID"));  
    }  
}

注意

这些示例使用 SQLServerCallableStatement 类的 execute 方法来运行存储过程。 使用此方法是因为存储过程也不会返回结果集。 如果返回,则使用 executeQuery 方法。

存储过程可能返回更新计数和多个结果集。 Microsoft JDBC Driver for SQL Server 遵循 JDBC 3.0 规范,此规范规定在检索 OUT 参数之前应检索多个结果集和更新计数。 也就是说,应用程序应先检索所有 ResultSet 对象和更新计数,再使用 CallableStatement.getter 方法检索 OUT 参数。 否则,当检索 OUT 参数时,尚未检索的 ResultSet 对象和更新计数将丢失。 若要详细了解更新计数和多个结果集,请参阅使用包含更新计数的存储过程使用多个结果集

另请参阅

结合使用语句和存储过程