我正在尝试使用json_value()
使用Oracle的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') = ?
替换第一个参数时,prepared语句可以正常工作。
我不顾一切地尝试,还尝试在参数pstmt.setString(1, "'$.data.k1'")
中包括单引号,但是毫不奇怪,Oracle也不会接受它(相同的错误消息)。
我还尝试使用json_value(payload, concat('$.', ?) )
,并且仅传递"data.k1"
作为参数-结果相同。
所以,问题是:
json_value
参数将JSON路径表达式传递给Oracle的PreparedStatement
函数? 有什么想法吗?这是驱动程序还是Oracle中的错误? (我在My Oracle Support上找不到任何内容)
还是这仅仅是“未实现”的情况?
环境:
我正在使用Oracle 18.0
我尝试了ojdbc10.jar
驱动程序的18.3和19.3版本以及OpenJDK11。
答案 0 :(得分:2)
它不是驱动程序-您得到的是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值。 路径表达式必须是文本文字。有关 JSON_basic_path_expression 的完整语义,请参见Oracle Database JSON Developer's Guide。
不幸的是,您将不得不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注入问题。
*,我知道您知道如何以嵌入式方式执行此操作,并且您知道要使用绑定变量,因为这是正确的操作;我已经说明了其他用户* em-以外的其他需求* 8-)