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

时间:2018-05-22 09:53:51

标签: postgresql spring-data-jpa jsonb

Postgres定义additional jsonb Operators,例如?|

但是,使用Spring JpaRepository查询构建器时,询问字符始终被视为参数,我无法想象如何转义它(除了单引号字符串,但查询无效)。

示例:

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

错误:

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)

有没有办法逃避它,或者是一种不同的解决方案,可以使用这些包含?字符的postgres原生运算符。

尝试用??避开它或\?|目前不起作用。

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

库:

  • hibernate 5.2.16
  • hibernate-jpa 2.1
  • spring-data-jpa 2.0.6.RELEASE
  • postgresql 42.2.2

感谢您的回复!

3 个答案:

答案 0 :(得分:6)

如果无法转义?,您可以创建具有不同名称的重复运算符。

新操作员

Postgres中creating operators的语法:

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

如果?|中使用了jsonb,则会:

CREATE OPERATOR ^|(
  PROCEDURE = jsonb_exists_any,
  LEFTARG = jsonb,
  RIGHTARG = _text,
  RESTRICT = contsel,
  JOIN = contjoinsel);

我使用^|作为替代名称。它可以是此列表中的任何序列:+ - * / < > = ~ ! @ # % ^ & |?`。

您可以通过查询pg_catalog.pg_operator表找到您感兴趣的运营商的当前定义。

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

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

启用索引

如果要为此“new”运算符使用索引,则需要创建新的运算符类和可选的族。在我们的例子中,我们需要两者,因为我们无法将它添加到现有族中,因为默认运算符已经在使用strategy slot

就像操作员一样,建议使用像pgAdmin这样的GUI工具来浏览操作符类,然后复制并粘贴它。

首先,我们采用我们制作的副本的OID:

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

对于运营商系列(我们将从operator class table获取),我们正在寻找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

然后我们得到strategy used by operator我们重复:

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

然后functions used by class

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

这将我们带到operator class。如果它已经不存在,它将创建运算符族。

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

现在您只需要使用创建的运营商名称创建索引,例如:

CREATE INDEX ON jsonb_table USING gin(jsonb_column jsonb_ops_custom)

你应该能够使用索引:

SET enable_seqscan = off;
EXPLAIN ANALYZE
SELECT * FROM jsonb_table WHERE jsonb_column ^| array['b', 'c'];

答案 1 :(得分:1)

作为该特定案例的解决方法,我创建了一个自定义运算符:

CREATE OPERATOR ~~~| (
    LEFTARG = jsonb,
    RIGHTARG = _text,
    PROCEDURE = pg_catalog.jsonb_exists_any
)

然后在我的查询中:WHERE u.authorities ~~~| array['ROLE_1', 'ROLE_2']

@ŁukaszKamiński在他的答案中详述了这个解决方法:https://stackoverflow.com/a/50488457/1097926

答案 2 :(得分:0)

您可以直接调用函数jsonb_exists_any()。 因此,您的情况应该是

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