Сопоставить рефкурсор PostgreSQL с сущностью java с помощью Hibernate

1

Я разрабатываю приложение Java, и недавно коллега сообщил мне, что мое решение станет более аккуратным, если я буду использовать хранимые процедуры для некоторых из моих потребностей. Я начал читать о них, и они действительно кажутся многообещающими, но теперь у меня тяжелые времена, когда Hibernate отображал результат, возвращенный из такой хранимой процедуры в java bean.

Вот процедура, к которой я пытаюсь добраться:

CREATE OR REPLACE FUNCTION wrong_user_answers(testId INTEGER, userId INTEGER) RETURNS refcursor AS $wrong_user_answers$
   DECLARE
      ref refcursor;
   BEGIN
      OPEN ref FOR SELECT ua.*
      FROM t_tests as t
      JOIN t_user_answers as ua on ua.fk_test_id = t.pk_test_id
      WHERE t.pk_test_id = testId and ua.fk_user_id = userId and is_correct(ua) = false;
      RETURN ref;
   END;
   $wrong_user_answers$ LANGUAGE plpgsql;

Здесь is_correct(ua) - это еще одна хранимая процедура, которую я определил. Я пробовал эту процедуру в PGAdmin, и она возвращает именно то, что я ожидаю - refcursor, содержащий ответы пользователя.

Вот java часть кода, который, как я думал, должен получить все это:

Дополнительная аннотация (@NamedNativeQuery) в UserAnswerBean:

@NamedNativeQueries({ 
       @NamedNativeQuery(name = "getWrongUserAnswers", 
                         query = "select wrong_user_answers(:testId, :userId)", 
                         resultClass = UserAnswerBean.class) 
})
@Entity
@Table(name = "t_user_answers")
public class UserAnswerBean {

И код, который использует процедуру:

Query query = getEntityManager().createNamedQuery("getWrongUserAnswers");
query.setParameter("testId", testId);
query.setParameter("userId", userId);
List<UserAnswerBean> answers = query.getResultList();

Когда выполняется выполнение кода в последней строке выше следующего erorr:

org.postgresql.util.PSQLException: Имя столбца pk_user_answer_id не найдено в этом ResultSet. org.postgresql.jdbc2.AbstractJdbc2ResultSet.findColumn(AbstractJdbc2ResultSet.java:2728) org.postgresql.jdbc2.AbstractJdbc2ResultSet.getInt(AbstractJdbc2ResultSet.java:2589) org.hibernate.type.descriptor.sql.IntegerTypeDescriptor $ 2.doExtract(IntegerTypeDescriptor.java: 74) org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:64) org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:267) org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java: 263) org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253) org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:338) org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java: 784) org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:720) org.hibernate.loader.Loader.processResultSet(Loader.java:952)

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

CREATE OR REPLACE FUNCTION wrong_user_answers(testId INTEGER, userId INTEGER) RETURNS setof record AS $wrong_user_answers$
   SELECT ua.*
      FROM t_tests as t
      JOIN t_user_answers as ua on ua.fk_test_id = t.pk_test_id
      WHERE t.pk_test_id = testId and ua.fk_user_id = userId and is_correct(ua) = false;
   $wrong_user_answers$ LANGUAGE sql;

К сожалению, это не изменило ошибку.

И, наконец, я познакомился с аннотацией @NamedStoredProcedureQuery:

@NamedStoredProcedureQuery(name = "getWrongUserAnswers", 
                           procedureName = "wrong_user_answers", parameters = {
                           @StoredProcedureParameter(name = "testId", type = Integer.class, mode = ParameterMode.IN),
                           @StoredProcedureParameter(name = "userId", type = Integer.class, mode = ParameterMode.IN) },
                           resultClasses = UserAnswerBean.class)
@Entity
@Table(name = "t_user_answers")
public class UserAnswerBean {

С измененным кодом Java:

    Query query = getEntityManager().createNamedStoredProcedureQuery("getWrongUserAnswers");
    query.setParameter("testId", testId);
    query.setParameter("userId", userId);
    List<UserAnswerBean> answers = query.getResultList();

Это тоже не ошибся! Может кто-нибудь помочь мне выяснить, что я делаю неправильно?

  • 0
    Я был бы весьма удивлен, если бы Hibernate хорошо играл с рефкурсорами, за исключением, возможно, если вы делаете операторы FETCH собственных запросов, а не ожидаете, что они воспримут их как наборы результатов.
  • 0
    Смотрите мою вторую попытку - там не использовался рекурсор. Кстати, я думаю, что где-то видел, что Hibernate переваривает рекурсоры без проблем, вероятно, играя умно и извлекая их
Показать ещё 1 комментарий
Теги:
stored-procedures
hibernate

1 ответ

1
Лучший ответ

В сущности, я смог решить свою проблему.

Я думаю, что я мог сообщить неправильную ошибку во втором подходе. Мне в основном не повезло в борьбе с репортерами, но используя LANGUAGE sql дал мне то, что я хотел. Это хранимая процедура:

CREATE OR REPLACE FUNCTION wrong_user_answers(testId INTEGER, userId INTEGER) 
   RETURNS setof t_user_answers 
   AS $wrong_user_answers$
   SELECT ua.*
      FROM t_tests as t
      JOIN t_user_answers as ua on ua.fk_test_id = t.pk_test_id
      WHERE t.pk_test_id = testId and ua.fk_user_id = userId and is_correct(ua) = false;
   $wrong_user_answers$ LANGUAGE sql;

Единственное отличие от того, что я явно указал тип возвращаемого значения для setof t_user_answers. Аннотации и код Java точно так же вставляются в вопрос, и теперь все работает для меня.

PS: Для тех, кто задавался вопросом, у меня было еще труднее делать все, когда у меня был результат, не являющийся сущностью DB, но, наконец, я нашел аннотацию @SqlResultSetMapping с подандацией classes @ConstructorResult для решения проблемы (очень сложно найти решение, хотя),

Ещё вопросы

Сообщество Overcoder
Наверх
Меню