如何在PreparedStatement中使用Oracle的JSON_VALUE函数

如何在PreparedStatement中使用Oracle的JSON_VALUE函数,json,oracle,jdbc,prepared-statement,Json,Oracle,Jdbc,Prepared Statement,我正在尝试使用Oracle的json_值函数和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, '{

我正在尝试使用Oracle的json_值函数和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运行此查询时:

字符串sql= 选择*\n+ 来自foo\n+ 其中json_valuepayload,?=?; PreparedStatement pstmt=conction.prepareStatementsql; pstmt.setString1,$.data.k1; pstmt.setString2,1; 结果集rs=pstmt.executeQuery; 我从示例中删除了所有错误检查以保持简单

这导致:

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

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

当我用字符串常量json_valuepayload,'$.data.k1'=仅替换第一个参数时?事先准备好的声明很有效

在一次绝望的尝试中,我还尝试在参数pstmt.setString1,'$.data.k1'中包含单引号,但毫不奇怪,Oracle也不会接受相同的错误消息

我还尝试使用json_valuepayload,concat'$?并且只传递data.k1作为参数-结果相同

因此,问题是:

如何使用PreparedStatement参数将JSON路径表达式传递给Oracle的JSON_值函数? 有什么想法吗?这是驱动程序中的错误还是Oracle中的错误?我在Oracle支持上找不到任何内容

或者这仅仅是一个没有实施的案例

环境:

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

这不是驱动因素-你会得到同样的结果:

它并不是一个真正的bug,它的重点是:

JSON_基本路径_表达式

使用此子句指定SQL/JSON路径表达式。该函数使用路径表达式计算expr并查找与路径表达式匹配或满足该表达式的标量JSON值。路径表达式必须是文本文本。有关JSON_basic_path_表达式的完整语义,请参阅

因此,不幸的是,您必须,而不是绑定它:

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();
这显然不是你想要做的,但似乎没有其他选择。除了将查询转换为函数并将path变量传递给该函数之外,该函数还必须使用动态SQL,因此效果大致相同——但这样可能更容易处理SQL注入问题


*我知道你知道如何用嵌入的方式来实现这一点,并且知道你想使用绑定变量,因为这是正确的做法;我已经为其他访问者详细说明了这一点*8-

感谢您确认我没有在手册中发现这一部分,这是Oracle JSON支持的另一个令人讨厌的限制
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();