如何为条件jpa2(休眠)创建自定义谓词?

时间:2018-02-05 19:19:58

标签: postgresql hibernate jpa-2.0 hibernate-criteria jpa-2.1

我尝试从旧版本迁移到JPA2 Criteria API(Hibernate作为JPA提供程序)。我有PostgreSQL JSONB类型的自定义标准的问题。在之前的Criteria实现中,可以通过创建SQLCriterion来完成,我可以在本机SQL上直接操作。在JPA2实现中我找不到类似的解决方案,所以我决定扩展AbstractSimplePredicate。

这里简单的版本:

   class JsonEqual<Y> extends AbstractSimplePredicate {

    private static final String JSON_NESTED_FIELD_EXTRACTOR = "->>";
    private static final String JSON_EQUAL_OPERATOR = "=";
    private final Expression<?> field;
    private final Expression<?> value;
    private final Path<Y> path;

    public JsonEqual(CriteriaBuilderImpl cb, Path<Y> path, Expression<?> field, Expression<?> value) {
        super(cb);
        this.path = path;
        this.field = field;
        this.value = value;
    }

    @Override
    public void registerParameters(ParameterRegistry registry) {
        Helper.possibleParameter(path, registry);
        Helper.possibleParameter(field, registry);
        Helper.possibleParameter(value, registry);
    }

    @Override
    public String render(boolean isNegated, RenderingContext renderingContext) {
        return ((Renderable) path).render(renderingContext)
                        + JSON_NESTED_FIELD_EXTRACTOR
                        + ((Renderable) field).render(renderingContext)
                        + JSON_EQUAL_OPERATOR
                        + ((Renderable) value).render(renderingContext);
    }
}

将jpa2条件编译为HQL时出现问题。由谓词呈现的查询部分格式不正确(&#39; - &gt;&gt;&#39;运算符无法识别)。

org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: > near line 1, column 515 [select new com.hibernate.VenueEntity(generatedAlias0.id) from com.hibernate.VenueEntity as generatedAlias0 where ( generatedAlias0.metadata->>:param3=:param4 ) order by generatedAlias0.created asc]
at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:74) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:91) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:291) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:186) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:3324) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.query.criteria.internal.CriteriaQueryImpl$1.buildCompiledQuery(CriteriaQueryImpl.java:318) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.query.criteria.internal.compile.CriteriaCompiler.compile(CriteriaCompiler.java:149) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:3617) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:203) ~[hibernate-core-5.2.12.Final.jar:5.2.12.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_152]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_152]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_152]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_152]

在这种情况下,是否有可能为JPA2实现自定义谓词?

1 个答案:

答案 0 :(得分:0)

我没有尝试用JSONB做这样的事情,但是我使用了数组和&&运算符。 所以,我认为,以下解决方案将解决您的情况:

  1. 创建并注册自定义Postgres方言,扩展标准方言。
  2. 方言 - registerFunction("json_extract", new SQLFunctionTemplate(HibernateTypeWhichYouNeed.INSTANCE, "(?1 ->> ?2)"));
  3. 在客户端代码中 - cb.equal( cb.function(json_extract, JavaClassWhatYouNeed.class, expr1, expr2), expr3);