Как использовать функцию Oracle JSON_VALUE с PreparedStatement - PullRequest
2 голосов
/ 09 июля 2019

Я пытаюсь выполнить SQL-запрос, используя функцию Oracle json_value(), используя PreparedStatement.

Примите следующие настройки таблицы:

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" в качестве параметра - тот же результат.

Итак, вопрос:

  • Как передать выражение пути JSON в функцию Oracle json_value, используя параметр PreparedStatement?

Есть идеи? Это ошибка в драйвере или в Oracle? (Я не смог найти что-нибудь в Службе поддержки Oracle)

Или это просто случай "не реализован"?


Окружающая среда:

Я использую Oracle 18.0
Я попробовал 18 * и 19.3 версию драйвера ojdbc10.jar вместе с OpenJDK 11.

1 Ответ

2 голосов
/ 09 июля 2019

Это не драйвер - вы получаете то же самое с динамическим 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

И это не совсем ошибка, это задокументировано (выделение добавлено):

JSON_basic_path_expression

Используйте это предложение, чтобы указать выражение пути SQL / JSON. Функция использует выражение пути для оценки expr и поиска скалярного значения JSON, которое соответствует или удовлетворяет выражению пути. Выражение пути должно быть текстовым литералом. См. Руководство разработчика JSON Oracle Database для полной семантики JSON_basic_path_expression .

Таким образом, вам придется встраивать путь буквально , а не связывать его, к сожалению:

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 -)

...