【问题标题】:How to use Oracle's JSON_VALUE function with a PreparedStatement如何将 Oracle 的 JSON_VALUE 函数与 PreparedStatement 一起使用
【发布时间】:2019-07-09 08:05:04
【问题描述】:

我正在尝试使用 Oracle 的 json_value() 函数使用 PreparedStatement 运行 SQL 查询。

假设如下表设置:

drop table foo cascade constraints purge;
create table foo
(
  id integer primary key, 
  payload clob, 
  constraint ensure_json check (payload IS JSON STRICT)
);

insert into foo values (1, '{"data": {"k1": 1, "k2": "foo"}}');

以下 SQL 查询工作正常:

select *
from foo
where json_value(payload, '$.data.k1') = '1'

并返回预期的行。

但是,当我尝试使用 PreparedStatement 运行此查询时,如下代码所示:

String sql =
     "select *\n" +
     "from foo\n" +
     "where json_value(payload, ?) = ?";

PreparedStatement pstmt = conection.prepareStatement(sql);
pstmt.setString(1, "$.data.k1");
pstmt.setString(2, "1");
ResultSet rs = pstmt.executeQuery();

(为了简单起见,我从示例中删除了所有错误检查)

这会导致:

java.sql.SQLException: ORA-40454: 路径表达式不是文字

罪魁祸首是传json路径值(参数索引1),第二个参数没问题。

当我(仅)用字符串常量 json_value(payload, '$.data.k1') = ? 替换第一个参数时,准备好的语句可以正常工作。

在绝望的尝试中,我还尝试在参数中包含单引号:pstmt.setString(1, "'$.data.k1'"),但毫不奇怪,Oracle 也不接受它(同样的错误消息)。

我也尝试使用 json_value(payload, concat('$.', ?) ) 并且只传递 "data.k1" 作为参数 - 结果相同。

所以,问题是:

  • 如何使用PreparedStatement 参数将JSON 路径表达式传递给Oracle 的json_value 函数?

有什么想法吗?这是驱动程序中的错误还是 Oracle 中的错误? (我在 My Oracle Support 上找不到任何东西)

或者这仅仅是“未实施”的情况?


环境:

我正在使用 Oracle 18.0
我尝试了 18.3 和 19.3 版本的 ojdbc10.jar 驱动程序以及 OpenJDK 11。

【问题讨论】:

    标签: json oracle jdbc prepared-statement


    【解决方案1】:

    这不是驱动程序 - 你得到同样的东西with dynamic SQL

    declare
      result foo%rowtype;
    begin
      execute immediate 'select *
        from foo
        where json_value(payload, :1) = :2'
      into result using '$.data.k1', '1';
      dbms_output.put_line(result.payload);
    end;
    /
    
    ORA-40454: path expression not a literal
    ORA-06512: at line 4
    

    这并不是真正的错误,它是documented(已添加重点):

    JSON_basic_path_expression

    使用此子句指定 SQL/JSON 路径表达式。该函数使用路径表达式计算 expr 并找到匹配或满足路径表达式的标量 JSON 值。 路径表达式必须是文本文字。请参阅 Oracle Database JSON Developer's Guide 了解 JSON_basic_path_expression 的完整语义

    不幸的是,你必须embed the path literal,而不是绑定它:

    declare
      result foo%rowtype;
    begin
      execute immediate 'select *
        from foo
        where json_value(payload, ''' || '$.data.k1' || ''') = :1'
      into result using '1';
      dbms_output.put_line(result.payload);
    end;
    /
    
    1 rows affected
    
    dbms_output:
    {"data": {"k1": 1, "k2": "foo"}}
    

    或者对于您的 JDBC 示例(将路径保留为单独的字符串,因为您可能希望它真的是一个变量):

    String sql =
         "select *\n" +
         "from foo\n" +
         "where json_value(payload, '" + "$.data.k1" + "') = ?";
    
    PreparedStatement pstmt = conection.prepareStatement(sql);
    pstmt.setString(1, "1");
    ResultSet rs = pstmt.executeQuery();
    

    这显然不是您想要做的*,但似乎没有其他选择。除了将您的查询转换为函数并将路径变量传递给该函数之外,该函数必须使用动态 SQL,因此效果大致相同——尽管这样可能更容易处理 SQL 注入问题。

    * 而且我知道您知道如何以嵌入式方式执行此操作,并且知道您想使用绑定变量,因为这是正确的做法;我已经把它说得比 需要其他访客 *8-)

    【讨论】:

    • 感谢您确认(我没有在手册中发现该部分) - Oracle 的 JSON 支持的另一个(许多)令人讨厌的限制
    猜你喜欢
    • 2021-02-12
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 2016-08-24
    • 2023-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多