意外令牌:使用休眠、JPQL 和 postgres 函数时出现“<function_name>”错误

Posted

技术标签:

【中文标题】意外令牌:使用休眠、JPQL 和 postgres 函数时出现“<function_name>”错误【英文标题】:unexpected token: '<function_name>' error when using hibernate, JPQL and postgres function 【发布时间】:2021-09-07 15:57:23 【问题描述】:

我将PostgreSQL 9.6Hibernate 5.4.8Java 8 和Spring 框架一起使用。我需要调用一个 postgres 函数

CREATE OR REPLACE FUNCTION function_that_return_array(givenIds character varying(255)) RETURNS int[] AS
'
BEGIN
    RETURN string_to_array($1,'','');
END
' LANGUAGE plpgsql;

在 JPQL 查询中

private static final String JPQL_QUERY =
    " SELECT NEW com.package.CustomProjection( " +
    "  e.id, " +
    "  e.value " +
    " ) " +
    " FROM SomeEntity e " +
    " WHERE e.id = ANY(function_that_return_array(:ids))";

并使用实体管理器:

@Autowired
private final EntityManager entityManager;

// ...

this.entityManager.createQuery(JPQL_QUERY, CustomProjection.class)
                .setParameter("ids", "1,2,3")
                .getResultList();

它会导致以下异常:

antlr.NoViableAltException: unexpected token: function_that_return_array
    at org.hibernate.hql.internal.antlr.HqlBaseParser.selectFrom(HqlBaseParser.java:1055) [hibernate-core-5.4.8.Final.jar:5.4.8.Final]
    at org.hibernate.hql.internal.antlr.HqlBaseParser.queryRule(HqlBaseParser.java:748) [hibernate-core-5.4.8.Final.jar:5.4.8.Final]
    at org.hibernate.hql.internal.antlr.HqlBaseParser.subQuery(HqlBaseParser.java:3910) [hibernate-core-5.4.8.Final.jar:5.4.8.Final]
    at org.hibernate.hql.internal.antlr.HqlBaseParser.quantifiedExpression(HqlBaseParser.java:3515) [hibernate-core-5.4.8.Final.jar:5.4.8.Final]
    at org.hibernate.hql.internal.antlr.HqlBaseParser.unaryExpression(HqlBaseParser.java:3373) [hibernate-core-5.4.8.Final.jar:5.4.8.Final]
...
antlr.MismatchedTokenException: expecting EOF, found ')'
    at antlr.Parser.match(Parser.java:211) ~[antlr-2.7.7.jar:?]
    at org.hibernate.hql.internal.antlr.HqlBaseParser.statement(HqlBaseParser.java:215) [hibernate-core-5.4.8.Final.jar:5.4.8.Final]

上面的例子非常简单,但它正确地代表了生产问题。当我在本机 SQL 中调用上述函数时,它可以完美运行:

select *
from some_entity e 
where e.id = ANY(function_that_return_array('1,2,3,4'))

有谁知道如何在 JPQL 和 Hibernate 中调用 postgres 函数,或者可以指出我做错了什么?我正在阅读许多文章 like this one,SO 问题,我尝试了 数十种 组合,但到目前为止没有成功。提前致谢。

【问题讨论】:

【参考方案1】:

在 Hibernate 方言中,您不能直接调用未注册的自定义数据库函数。 明确的例外,hibernate 对您的功能一无所知:

unexpected token: function_that_return_array

这里有两种选择:

    通过自定义函数的通用机制调用您的函数:

使用

function('function_that_return_array', '1,2,3,4')

而不是

function_that_return_array('1,2,3,4')
    第二个选项是注册你的函数: https://docs.jboss.org/hibernate/orm/5.1/javadocs/org/hibernate/dialect/Dialect.html#registerFunction-java.lang.String-org.hibernate.dialect.function.SQLFunction-

例子:

public class MyDialect extends PostgreSQLXXDialect 
    public MyDialect() 
        super();
        registerFunction("function_that_return_array", new StandardSQLFunction("function_that_return_array"));
    

【讨论】:

谢谢,不幸的是,当使用通用机制时,它会抛出与消息unexpected token: function 相同的异常。我也尝试过使用方言注册的第二种方法。断点已在构造函数中触发,因此我的 postgres 函数确实已注册。此外,我调用了静态 getFunctions() 方法,它正确返回了我们的 postgres 函数。但它仍然抛出相同的异常。也许这是库版本或缺少依赖项的问题? 或者是因为ANY操作符内部的函数调用?还是需要一些特殊的引号? function_that_return_array 返回整数数组,因此您可以只使用 IN (...) 运算符,这相当于“ANY” 当我使用 IN 运算符时,它会引发以下错误:SQL Error [42883]: ERROR: operator does not exist: integer = integer[]。我尝试使用 TABLE 返回类型而不是 ARRAY 但休眠也失败了。我尝试了几十种组合,但由于某种原因,hibernate 无法将 JPQL 转换为原生 SQL。它仍然会抛出 unexpected tokenunexpected end of subtree 取决于我尝试的方法。作为比较,当我通过同一个实体管理器使用本机查询时,它可以正常工作。 ANY是HQL中的特殊量词,所以不能这样使用。您必须将ANY 打包到您注册的函数中。

以上是关于意外令牌:使用休眠、JPQL 和 postgres 函数时出现“<function_name>”错误的主要内容,如果未能解决你的问题,请参考以下文章

休眠异常:意外令牌:HAVING

休眠错误消息:意外令牌:NULLS(脚本文件行中的错误:13 意外令牌:NULLS)

使用 JPQL 连接不相关的实体在休眠 5.3.2 中无法在 Spring 数据中工作

Hibernate 无法使用 Postgres 在 jpql 中定义空参数

JPQL 意外 AST 节点:围绕“coalesce”

org.hibernate.hql.internal.ast.QuerySyntaxException:意外令牌:FROM,当尝试删除与自身连接的表时