Spring JpaRepository如何转义问号(?)字符

Nic*_*oub 5 postgresql spring-data-jpa jsonb

Postgres定义了其他jsonb运算符,例如?|

但是,使用Spring JpaRepository查询构建器,总是将询问字符视为参数,并且我无法弄清楚如何对其进行转义(单个引号字符串中的字符除外,但随后查询无效)。

例:

@Query(value = "SELECT * FROM public.user u WHERE u.authorities ?| array['ROLE_1', 'ROLE_2']", nativeQuery = true)
Run Code Online (Sandbox Code Playgroud)

错误:

java.lang.IllegalArgumentException: Unable to resolve given parameter name [1] to QueryParameter reference
    at org.hibernate.query.internal.QueryParameterBindingsImpl.resolveQueryParameter(QueryParameterBindingsImpl.java:520)
    at org.hibernate.query.internal.QueryParameterBindingsImpl.getQueryParameterListBinding(QueryParameterBindingsImpl.java:498)
    at org.hibernate.query.internal.AbstractProducedQuery.setParameterList(AbstractProducedQuery.java:560)
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以逃脱它,或者可以使用其他包含?字符的postgres本机运算符的解决方案。

试图用?? |逃脱它。或\?| 目前无法正常运作。

注意:我也尝试使用自定义方言功能,但是它以相同的问题结束。

图书馆:

  • 冬眠5.2.16
  • 休眠JPA 2.1
  • spring-data-jpa 2.0.6.RELEASE
  • PostgreSQL 42.2.2

谢谢你们的回应!

Łuk*_*ski 7

如果?无法转义,则可以使用不同的名称创建重复的运算符。

新运营商

在Postgres中创建运算符的语法:

CREATE OPERATOR name (
    PROCEDURE = function_name
    [, LEFTARG = left_type ] [, RIGHTARG = right_type ]
    [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]
    [, RESTRICT = res_proc ] [, JOIN = join_proc ]
    [, HASHES ] [, MERGES ]
)
Run Code Online (Sandbox Code Playgroud)

在的情况下,?|在使用jsonb这将是:

CREATE OPERATOR ^|(
  PROCEDURE = jsonb_exists_any,
  LEFTARG = jsonb,
  RIGHTARG = _text,
  RESTRICT = contsel,
  JOIN = contjoinsel);
Run Code Online (Sandbox Code Playgroud)

^|以一个示例为例,备用名称。它可以是此列表中的任何序列:+ - * / < > = ~ ! @ # % ^ & |?`。

通过查询pg_catalog.pg_operator表,可以找到感兴趣的运算符的当前定义。

SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');
Run Code Online (Sandbox Code Playgroud)

您还可以使用pgAdmin之类的GUI工具并浏览pg_catalog以准备重新使用SQL定义。

启用索引

如果要为该“新”运算符使用索引,则需要创建新的运算符类和(可选)家族。在我们的例子中,我们都需要,因为我们不能将其添加到现有系列中,因为默认运算符已经在使用策略slot

与操作员一样,建议使用pgAdmin之类的GUI工具浏览操作员类,然后将其复制并粘贴。

首先,我们将复制了以下内容的运算符的OID用作:

SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');
Run Code Online (Sandbox Code Playgroud)

对于运算符族来说,这也是一样(我们将从运算符类表中获取它),我们正在寻找gin类,因为这是支持的类?|opcdefault之所以使用,是因为存在jsonb_path_ops不支持此运算符的可选类:

SELECT opcfamily
  FROM pg_opclass
 WHERE opcintype = (SELECT oid FROM pg_type WHERE typname = 'jsonb')
   AND opcmethod = (SELECT oid FROM pg_am WHERE amname = 'gin')
   AND opcdefault
Run Code Online (Sandbox Code Playgroud)

然后我们得到重复的运算符使用的策略

SELECT amopstrategy,
       (SELECT typname FROM pg_type WHERE oid = amoplefttype) AS left_t, 
       (SELECT typname FROM pg_type WHERE oid = amoprighttype) AS right_t,*
FROM pg_amop
WHERE amopfamily = 4036 --family oid
  AND amopopr = 3248 --operator oid
Run Code Online (Sandbox Code Playgroud)

然后是类使用的函数

SELECT amprocnum, amproc::text, pg_get_function_identity_arguments(amproc::oid) AS args,
      (SELECT typname FROM pg_type WHERE oid = amproclefttype) AS left_t,
      (SELECT typname FROM pg_type WHERE oid = amprocrighttype) AS right_t,*
FROM pg_amproc
WHERE amprocfamily = 4036 --op family
Run Code Online (Sandbox Code Playgroud)

这将我们带入此运算符类。如果尚不存在,它将创建操作员系列。

CREATE OPERATOR CLASS jsonb_ops_custom
   FOR TYPE jsonb USING gin AS
   OPERATOR 10  ^|(jsonb, _text),
   FUNCTION 1  gin_compare_jsonb(text, text),
   FUNCTION 2  gin_extract_jsonb(jsonb, internal, internal),
   FUNCTION 3  gin_extract_jsonb_query(jsonb, internal, smallint, internal, internal, internal, internal),
   FUNCTION 4  gin_consistent_jsonb(internal, smallint, jsonb, integer, internal, internal, internal, internal),
   FUNCTION 6  gin_triconsistent_jsonb(internal, smallint, jsonb, integer, internal, internal, internal);
Run Code Online (Sandbox Code Playgroud)

现在,您只需要使用创建的操作员名称创建索引,例如:

CREATE INDEX ON jsonb_table USING gin(jsonb_column jsonb_ops_custom)
Run Code Online (Sandbox Code Playgroud)

并且您应该能够使用索引:

SET enable_seqscan = off;
EXPLAIN ANALYZE
SELECT * FROM jsonb_table WHERE jsonb_column ^| array['b', 'c'];
Run Code Online (Sandbox Code Playgroud)


小智 7

您可以使用直接调用函数jsonb_exists_any()。所以在你的情况下它会是

jsonb_exists_any(u.authorities::jsonb, array['ROLE_1', 'ROLE_2'])

  • 请注意,即使相应的运算符使用索引,大多数 jsonb 函数也不使用索引! (2认同)