Hibernate, postgres CTE и собственное сопоставление запросов SQL ARRAY [] :: BYTEA [] с addScalar - PullRequest
0 голосов
/ 11 декабря 2018

Я пытаюсь выполнить следующее общее табличное выражение как собственный SQL-запрос в спящем режиме.Хотя это CTE, начинающийся с предложения WITH, я видел другие примеры, где это работало с собственным SQL.Я выполняю этот запрос в таблице с именем node со следующим 2 полем

@Id 
@Type(type = "uuid-binary")
@Column(name="ID", unique = true, nullable=false)
protected UUID id;



@Type(type = "uuid-binary")
@Column(name="PARENT_ID", nullable=true)
private UUID parentId;

Этот запрос был взят из официальной вики PostgreSQL из здесь
и только немного измененбит, т.е. я снял ограничение

 "WHERE parent_id IS NULL".

Если я выполню этот запрос в pgAdmin4, я получу успех и знаю, что он работает так, как должен.т.е. я получаю ожидаемый вывод, который выглядит следующим образом:

enter image description here

Обратите внимание, что тип второго столбца, называемого предками, - "bytea []" (Непутать с байтом []).

Кажется, что я не могу отобразить это, то есть мне кажется, что проблема заключается в отображении массива bytea, например, "ARRAY [] :: BYTEA []" и следующих .addScalar ("предки")

Session session = getSessionFactory().openSession();
session.beginTransaction();

String sql_query = 
                  " WITH RECURSIVE tree AS ("
                + " SELECT id, ARRAY[]::BYTEA[] AS ancestors"
                + " FROM node "

                + " UNION ALL"

                + " SELECT node.id, tree.ancestors || node.parent_id"
                + " FROM node, tree" 
                + " WHERE node.parent_id = tree.id"

                + " ) SELECT * FROM tree WHERE decode(:Argument1,  'hex') = ANY(tree.ancestors)"
                ;

Query<Object[]> query = session.createNativeQuery(sql_query)
        .addScalar("id", org.hibernate.type.StandardBasicTypes.BINARY.INSTANCE)
        .addScalar("ancestors")
        ;



String nodeIdHex = UUIDOperations.uuidToHex(node.getID() );
query.setParameter("Argument1", nodeIdHex, org.hibernate.type.StringType.INSTANCE);

List<Object[]> objectCollection = (List<Object[]>) query.list();


session.getTransaction().commit();
session.close();

return objectCollection;

Когда я выполняю это, я получаю следующую ошибку:

Hibernate:  WITH RECURSIVE tree AS ( SELECT id, ARRAY[]:BYTEA[] AS ancestors FROM node  UNION ALL SELECT node.id, tree.ancestors || node.parent_id FROM node, tree WHERE node.parent_id = tree.id ) SELECT * FROM tree decode(?,  'hex') = ANY(tree.ancestors)
2018-12-11 09:11:38.200  WARN pc --- [           main] o.h.e.j.s.SqlExceptionHelper             : SQL Error: 0, SQLState: 42601
2018-12-11 09:11:38.202 ERROR pc --- [           main] o.h.e.j.s.SqlExceptionHelper             : ERROR: syntax error at or near ":"
  Position: 45



javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1514)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy238.findAllSubNodesRecursively(Unknown Source)
....
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
....
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
....

at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1505)
    ... 58 more
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near ":"
  Position: 45
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:143)
    at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:106)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)


org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:230)
    at org.springframework.orm.hibernate5.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:102)
    at org.springframework.orm.hibernate5.HibernateExceptionTranslator.translateExceptionIfPossible(HibernateExceptionTranslator.java:77)
....
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
    ... 49 more
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near ":"
  Position: 45
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:143)
    at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:106)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)
    ... 72 more

Я подозреваю, что проблема в отображении "ARRAY [] :: BYTEA []", но япросто не знаю, как правильно отобразить это.

Я попытался создать специальный тип пользователя, который поможет мне отобразить это, но, похоже, не поможет мне в этом случае, или я реализовал его неправильно.

Type byteArrayType = new CustomType(new ByteaArrayUserType());
        Query<Object[]> query = session.createNativeQuery(sql_query)
                .addScalar("id", org.hibernate.type.StandardBasicTypes.BINARY.INSTANCE)
                .addScalar("ancestors", byteArrayType)
                ;

вот класс отображения ниже:

import java.io.Serializable;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;

public class ByteaArrayUserType implements UserType {



    protected static final int[] SQL_TYPES = { Types.ARRAY };



    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return this.deepCopy(cached);
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (byte[][]) this.deepCopy(value);
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {

        if (x == null) {
            return y == null;
        }
        return x.equals(y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    @Override
    public boolean isMutable() {
        return true;
    }



    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return original;
    }

    @Override
    public Class<byte[][]> returnedClass() {
        return byte[][].class;
    }

    @Override
    public int[] sqlTypes() {
        return new int[] { Types.ARRAY };
    }

    @Override
    public Object nullSafeGet(
            ResultSet resultSet, 
            String[] names, 
            SharedSessionContractImplementor session, Object owner
            )throws HibernateException, SQLException {
        if (resultSet.wasNull()) {
            return null;
        }
        if (resultSet.getArray(names[0]) == null) {
            return new byte[0];
        }

        Array array = resultSet.getArray(names[0]);
        byte[][] javaArray = (byte[][]) array.getArray();
        return javaArray;
    }

    @Override
    public void nullSafeSet(PreparedStatement statement, 
            Object value, int index, 
            SharedSessionContractImplementor session
            )throws HibernateException, SQLException {

        Connection connection = statement.getConnection();
        if (value == null) {
            statement.setNull(index, SQL_TYPES[0]);
        } else {
            byte[][] castObject = (byte[][]) value;
            Array array = connection.createArrayOf("byte", castObject);
            statement.setArray(index, array);
        }


    }

}

Причина, по которой я считаю, что это связано с ARRAY [] :: BYTEA, заключается в том, что если я упросту запрос до чего-топросто как:

          SELECT id, ARRAY[]::BYTEA[] AS ancestors
          FROM node 

Я все еще получаю ту же ошибку, однако на этот раз она говорит

[           main] o.h.e.j.s.SqlExceptionHelper             : SQL Error: 0, SQLState: 42601
[           main] o.h.e.j.s.SqlExceptionHelper             : ERROR: syntax error at or near ":"
  Position: 20
javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not extract ResultSet

, так что не так с этим двоеточием ":".На этот раз отличается только позиция, т. Е. 20 вместо 45 в предыдущем исключении

...