【问题标题】:CallableStatement - InOut Parameter returns null valueCallableStatement - InOut 参数返回空值
【发布时间】:2020-06-26 11:14:46
【问题描述】:

我最近遇到了一个问题,我无法处理它。项目是在 Spring Boot 2.3.1 中使用 Java 8 和 Oracle 12c 创建的。在 Java 中执行 PL/SQL 块没有按预期工作。每次 INOUT 参数返回 null。 重现问题的示例:

public String executeSQLBlock() throws SQLException {
        Connection connection = dataSource.getConnection();
        CallableStatement statement = connection.prepareCall(
                "BEGIN\n" +
                "     IF UPPER(?) = UPPER(?) THEN\n" +
                "        ? := 'Test';\n" +
                "      END IF;\n" +
                "END;");
        statement.setString(1, "Test1");
        statement.setString(2, "Test2");
        statement.setString(3, "Name");
        statement.registerOutParameter(3, VARCHAR);
        statement.execute();
        return statement.getString(3);
    }

此方法返回 null 而不是“名称”值。怎么了?

【问题讨论】:

    标签: java oracle spring-boot jdbc plsql


    【解决方案1】:

    我不能确定为什么 Oracle 和/或 Oracle JDBC 驱动程序会这样。我最好的猜测是无法确定第三个绑定变量是OUT 参数还是IN OUT 参数。对于存储过程或函数,相关参数可以声明为IN OUT,但对于匿名 PL/SQL 块则没有类似的参数。

    JDBC 仅支持按位置而不是按名称绑定变量,因此在 PL/SQL 块中,绑定变量只能被读取一次(适合 IN 参数)或分配一次(适合 @ 987654325@参数)。如果它支持按名称绑定变量,也许它可以处理IN OUT 参数,但这不是我们所处的情况。

    另一种方法是在 PL/SQL 块中声明一个局部变量,使用IN 参数设置其初始值并使用OUT 参数获取其最终值:

            CallableStatement statement = connection.prepareCall(
                    "DECLARE\n" +
                    "     l_var   VARCHAR2(1000 CHAR) := ?;\n" +
                    "BEGIN\n" +
                    "     IF UPPER(?) = UPPER(?) THEN\n" +
                    "        l_var := 'Test';\n" +
                    "     END IF;\n" +
                    "     ? := l_var;\n" +
                    "END;");
            statement.setString(1, "Name");
            statement.setString(2, "Test1");
            statement.setString(3, "Test2");
            statement.registerOutParameter(4, Types.VARCHAR);
            statement.execute();
            return statement.getString(4);
    

    【讨论】:

      【解决方案2】:

      我怀疑这与您使用匿名块有关。使用 IN OUT 意味着某种过程,您实际上是在传递一个值 IN 或 OUT(或两者)。这是一个使用 PL/SQL 过程的测试。

      CREATE OR REPLACE PROCEDURE add_one_if_odd(x IN OUT INTEGER) IS
      BEGIN
        IF MOD(x,
               2) > 0 THEN
          x := x + 1;
        END IF;
      END add_one_if_odd;
      /
      

      这是要演示的 Java 程序。

      import java.sql.CallableStatement;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.SQLException;
      import java.sql.Types;
      
      public class InOutTest {
          public static void main(String[] args)
                  throws ClassNotFoundException, SQLException {
              Class.forName("oracle.jdbc.driver.OracleDriver");
              Connection connection = DriverManager.getConnection(
                      "jdbc:oracle:thin:@//your.db.string", "user", "password");
              CallableStatement statement = connection
                      .prepareCall("BEGIN add_one_if_odd(?); END;");
      
              for (int i = 0; i < 10; i++) {
                  statement.registerOutParameter(1, Types.INTEGER);
                  statement.setInt(1, i);
                  statement.execute();
      
                  System.out.println(i + " " + statement.getInt(1));
              }
      
              statement.close();
              connection.close();
          }
      }
      

      结果如下:

      0 0
      1 2
      2 2
      3 4
      4 4
      5 6
      6 6
      7 8
      8 8
      9 10
      

      更新:正如你所说,你只允许匿名块。如果您知道 IN OUT 参数的 NULL 值表示它没有被设置(如您的示例中所示),您可以使用 wasNull() 来检测和处理这种情况。

      import java.sql.CallableStatement;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.SQLException;
      import java.sql.Types;
      
      public class InOutTest {
          public static void main(String[] args)
                  throws ClassNotFoundException, SQLException {
              Class.forName("oracle.jdbc.driver.OracleDriver");
              Connection connection = DriverManager.getConnection(
                      "jdbc:oracle:thin:@//your.db.string", "user", "password");
              CallableStatement statement = connection.prepareCall("BEGIN\n"
                      + "     IF UPPER(?) = UPPER(?) THEN\n"
                      + "        ? := 'Test';\n" + "      END IF;\n" + "END;");
              String inOut = "Name";
              statement.setString(1, "Test1");
              statement.setString(2, "Test1");
              statement.setString(3, inOut);
              statement.registerOutParameter(3, Types.VARCHAR);
              statement.execute();
      
              String out = statement.getString(3);
              out = statement.wasNull() ? inOut : out;
              System.out.println(out);
              statement.close();
              connection.close();
          }
      }
      

      为不匹配生成“名称”,为匹配生成“测试”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-25
        • 1970-01-01
        • 2021-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多