Я разрабатываю приложение 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();
Это тоже не ошибся! Может кто-нибудь помочь мне выяснить, что я делаю неправильно?
В сущности, я смог решить свою проблему.
Я думаю, что я мог сообщить неправильную ошибку во втором подходе. Мне в основном не повезло в борьбе с репортерами, но используя 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 для решения проблемы (очень сложно найти решение, хотя),
FETCH
собственных запросов, а не ожидаете, что они воспримут их как наборы результатов.