如何使用包含问号"?"的PostgreSQL JSON(B)运算符 通过JDBC

Luk*_*der 9 java sql postgresql json jdbc

PostgreSQL知道一些在其名称中使用问号字符的时髦ASCII艺术运算符,例如这些JSON运算符:

  • ? 字符串是否作为JSON值中的顶级键存在?
  • ?| 这些数组字符串中是否存在顶级键?
  • ?& 所有这些数组字符串都作为顶级键存在吗?

问题是官方PostgreSQL JDBC驱动程序似乎没有正确解析包含此类运算符的SQL字符串.它假定问号是普通的JDBC绑定变量.以下代码......

try (PreparedStatement s = c.prepareStatement("select '{}'::jsonb ?| array['a', 'b']");
     ResultSet rs = s.executeQuery()) {
     ...
}
Run Code Online (Sandbox Code Playgroud)

...引发异常:

org.postgresql.util.PSQLException: Für den Parameter 1 wurde kein Wert angegeben.
    at org.postgresql.core.v3.SimpleParameterList.checkAllParametersSet(SimpleParameterList.java:225)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:190)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:424)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:161)
    at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114)
Run Code Online (Sandbox Code Playgroud)

我该如何使用此运算符?

Luk*_*der 17

有两种可能的解决方法:

使用静态语句而不是预处理语句

这是最简单的解决方法,但是您将失去预准备语句的所有好处(性能,SQL注入保护等).但是,这将有效

try (Statement s = c.createStatement();
     ResultSet rs = s.executeQuery("select '{}'::jsonb ?| array['a', 'b']")) {
     ...
}
Run Code Online (Sandbox Code Playgroud)

避免操作员.改为使用函数(注意:可能不使用索引)

运算符只是存在于支持函数中的语法糖pg_catalog.以下是如何查找这些函数的名称:

SELECT 
  oprname, 
  oprcode || '(' || format_type(oprleft,  NULL::integer) || ', ' 
                 || format_type(oprright, NULL::integer) || ')' AS function
FROM pg_operator 
WHERE oprname = '?|';
Run Code Online (Sandbox Code Playgroud)

以上产量:

oprname  function
----------------------------------------------------------------------------------
?|       point_vert(point, point)
?|       lseg_vertical(-, lseg)
?|       line_vertical(-, line)
?|       jsonb_exists_any(jsonb, text[])    <--- this is the one we're looking for
?|       exists_any(hstore, text[])
Run Code Online (Sandbox Code Playgroud)

因此,最简单的解决方法是不使用运算符,而是使用相应的函数:

try (PreparedStatement s = c.prepareStatement(
         "select jsonb_exists_any('{}'::jsonb, array['a', 'b']");
     ResultSet rs = s.executeQuery()) {
     ...
}
Run Code Online (Sandbox Code Playgroud)

  • @MarkRotteveel:Intersting,非常感谢指针.我个人并不太喜欢"逃避战略".我确信在某些时候,一些PostgreSQL扩展将增加对`??`运算符的支持.那会发生什么? (3认同)
  • PostgreSQL中没有人会添加?? 运营商......我们都很清楚这一点?现在问题. (3认同)
  • 值得注意的是,当使用jsonb_exists时,任何GIN索引都将被忽略,而问号运算符将考虑索引. (3认同)
  • 我相信他们最近在 PostgreSQL JDBC 驱动程序中添加了一个解决方法。另请参阅http://stackoverflow.com/questions/14779896/does-the-jdbc-spec-prevent-from-being-used-as-an-operator-outside-of-quotes/14786469(尤其是http:// stackoverflow.com/a/30517450/466862) (2认同)