неожиданный токен: ошибка » » при использовании функций hibernate, JPQL и postgres

#java #sql #postgresql #hibernate #jpql

Вопрос:

Я использую PostgreSQL 9.6 с Hibernate 5.4.8 , Java 8 и пружинный каркас. Мне нужно вызвать функцию 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))";
 

и использование entity manager:

 @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'))
 

Кто-нибудь знает, как вызвать функцию postgres в JPQL и перейти в спящий режим, или может указать мне, что я делаю неправильно? Я читал много статей, подобных этой, поэтому задавал вопросы и пробовал десятки комбинаций, но пока безуспешно. Заранее спасибо.

Ответ №1:

На диалекте гибернации вы не можете напрямую вызывать незарегистрированные пользовательские функции базы данных. Исключение в clear, hibernate ничего не знает о вашей функции:

 unexpected token: function_that_return_array
 

Здесь у вас есть два варианта:

  1. Вызовите свою функцию с помощью универсального механизма для пользовательских функций:

использовать

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

вместо

 function_that_return_array('1,2,3,4')
 
  1. Второй вариант-зарегистрировать свою функцию:
    https://docs.jboss.org/hibernate/orm/5.1/javadocs/org/hibernate/dialect/Dialect.html#registerFunction-java.lang.Строка-org.спящий режим.диалект.функция.Функция SQLFunction-

Пример:

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

Комментарии:

1. Спасибо, к сожалению, при использовании универсального механизма он выдает то же исключение с сообщением unexpected token: function . Я также попробовал второй подход с регистрацией диалекта. Точка останова была запущена внутри конструктора, так что моя функция postgres действительно была зарегистрирована. Кроме того, я вызвал статический getFunctions() метод, и он правильно вернул нашу функцию postgres. Но это все равно вызывает то же исключение. Может быть, это вопрос версии библиотеки или какой-то отсутствующей зависимости?

2. Или, может быть, это из-за вызова функции внутри ANY оператора? Или также требуются какие-то специальные расценки?

3. function_that_return_array возвращает массив целых чисел, поэтому вы можете просто использовать оператор IN ( … ), это эквивалент для «ЛЮБОГО».

4. когда я использую IN оператор, он выдает следующую ошибку: SQL Error [42883]: ERROR: operator does not exist: integer = integer[] . Я пытался использовать TABLE тип возврата вместо ARRAY , но спящий режим также не удается. Я перепробовал десятки комбинаций, но по какой-то причине hibernate не может преобразовать JPQL в собственный SQL. Это все равно бросает unexpected token или unexpected end of subtree зависит от подхода, который я пытаюсь. Для сравнения, когда я использую собственный запрос через тот же менеджер сущностей, он работает правильно.

5. ANY это специальный квантор в HQL, поэтому вы не можете использовать его таким образом. Вам придется упаковать ANY его в функцию, которую вы регистрируете.