У меня есть REST API, и когда я делаю POST и GET почти одновременно, я получаю это исключение:
SEVERE: The RuntimeException could not be mapped to a response, re-throwing to the HTTP container
java.lang.IllegalStateException: Transaction already active
at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:52)
at org.hibernate.internal.AbstractSharedSessionContract.beginTransaction(AbstractSharedSessionContract.java:409)
at sun.reflect.GeneratedMethodAccessor89.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:355)
at com.sun.proxy.$Proxy58.beginTransaction(Unknown Source)
at utils.HibernateSession.createTransaction(HibernateSession.java:15)
at api.ConversationsREST.getMessages(ConversationsREST.java:128)
Они находятся в разных классах, поэтому не подразумеваются глобальные атрибуты.
Строка, которая терпит неудачу, такова:
HibernateSession hs = new HibernateSession();
hs.createTransaction(); // Crash
Что относится к моему классу HibernateSession:
public class HibernateSession {
public Session session;
public void createTransaction() {
session = HibernateUtil.getSessionFactory().getCurrentSession(); //THIS WAS WRONG
//EDIT:session = HibernateUtil.getSessionFactory().openSession(); //THIS IS RIGHT
session.beginTransaction();
}
public void commitclose() {
session.getTransaction().commit();
session.close();
}
public void rollbackclose() {
try {
session.getTransaction().rollback();
session.close();
} catch (Exception hibernateexception) {
hibernateexception.printStackTrace();
}
}
}
Исключение на самом деле на линии session.beginTransaction()
Я всегда делаю hs.commitclose (), а в блоках catch () и 404 всегда делаю rollbackclose ();
Проблема в том, что когда я делаю такие сообщения POST:
HibernateSession hs = new HibernateSession();
hs.createTransaction();
hs.session.save(whatever);
hs.commitclose();
Возвращает 200, и все в порядке, но затем GET может дать сбой с исключением, указанным выше. Когда я создаю экземпляр новый для HibernateSession, почему Hibernate, кажется, пытается разделить эту транзакцию?
Это происходит только тогда, когда я выполняю оба запроса за очень короткое время (я думаю, запуск транзакции между началом и фиксацией другого). Итак, я предполагаю, что Hibernate думает, что атрибут сеанса статичен или что-то в этом роде ...
Обновлено: По запросу, HibernateUtil.java (проблема не здесь, но может помочь понять):
package utils;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sessionFactory;
public static SessionFactory getSessionFactory() {
if (sessionFactory == null) {
sessionFactory = build();
}
return sessionFactory;
}
private static SessionFactory build() {
try {
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed: " + ex);
throw new ExceptionInInitializerError(ex);
}
}
}
@ Ashish451 Конечно
Я добавил решение .. вы можете попробовать




Попробуйте переписать код как
public class HibernateSession {
public Session session;
int id;
Transaction t = null;
public void createTransaction() {
session = HibernateUtil.getSessionFactory().getCurrentSession();
t = session.beginTransaction();
}
public void commitclose() {
t.commit();
session.close();
}
public void rollbackclose() {
try {
t.rollback();
session.close();
} catch (Exception hibernateexception) {
hibernateexception.printStackTrace();
}
}
}
Конечно, для каждой ссылки t требуются нулевые проверки.
Вызвано то же исключение, но вместо session.beginTransaction (); это было на t = session.beginTransaction ();
Последняя попытка. Заменить t = session.beginTransaction(); на t = session.getTransaction();
Но мне нужно это начать. Он должен быть неактивным, так как в первый раз я делаю GET. Я могу проверить, активен он или нет, но это всего лишь обходной путь, мне действительно нужно, чтобы транзакция была очень новым экземпляром каждый раз, когда создается экземпляр класса.
@ CarlosLópez, поэтому вам следует использовать фабрику сеансов! У вас есть только экземпляр один сеанса Hibernate, и он застрял внутри транзакции, потому что вы правильно его закрыли нет.
@ nwenz3l Я сделал, проблема была в getCurrentTransaction () вместо openTransaction, который я хотел сделать.
Есть много случаев использования, когда вы не получаете привилегии открывать новую транзакцию для каждой операции. В корпоративных средах вам часто приходится присоединяться к уже существующей транзакции, потому что транзакция очень дорогостоящая. Это действительно обходной путь. Учитывая ваш код, я чувствовал, что это не сильно повредит.
Согласно вашей логике createTransaction, вы получаете текущий сеанс от SessionFactory и запускаете из него транзакцию.
Вот в чем проблема.
Предположим, вы создали объект HibernateSession и начали транзакцию, но она все еще выполняется. Значит, вы еще не закрыли транзакцию.
Теперь вы создали еще один HibernateSession и попытаетесь запустить транзакцию, при этом будет сгенерировано исключение.
Итак, ваш этот код
public void createTransaction() {
session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
}
должно быть это
public void createTransaction() {
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
}
getSessionFactory (). openSession () всегда открывает новый сеанс, который необходимо закрыть после завершения операций.
getSessionFactory (). getCurrentSession () возвращает сеанс, привязанный к контексту - вам не нужно его закрывать.
Использование getSessionFactory().openSession() будет работать, но при этом будет открываться сеанс для каждого вызова, это плохая практика, постарайтесь этого не делать. Это просто убьет приложение, когда будет много звонков.
Я согласен с вами, но, согласно его мнению, он уже создает новый экземпляр HibernateSession. Я бы предпочел позволить контейнеру управлять сеансами.
@ cнŝdk потратил много времени, чтобы обойти его. opensession не регистрируется в threadlocal. ужасный дизайн. все на самом деле в спячке. Я решил это, используя свои собственные реализации, но мне интересно. когда вы действительно получаете новый сеанс с помощью openSession () ... и начинаете транзакцию ... будет ли весь новый код, такой как добавление элементов, принадлежать новой транзакции? когда мы его закроем, вернемся ли мы к предыдущему? потому что кто получает работу? что решает это? это стек, который зависит от того, когда транзакция начинается и заканчивается?
Да, это немного сбивает с толку и слишком сложная часть гибернации, но, как только вы закрываете Transaction, вы должны открыть новый, иначе вы не сможете сохранить данные, но когда вы откроете новый Transaction, вы должны убедиться, что чтобы закрыть его, а также да, все новые изменения кода (добавление, редактирование сущностей) будут храниться в этой транзакции, пока вы ее не commit.
Но всегда имейте в виду, что: Сеанс не является потокобезопасным и должен использоваться только одним потоком. Обычно это гарантируется SessionFactory, как указано в Глава Обработка сеансов и транзакций в документации Hibernate. И нет необходимости реализовывать свой собственный дизайн, если вы используете Spring или EJB, Transaction будет отлично обрабатываться этими Framework.
На самом деле в вашем реальном коде много недостатков:
HibernateSession каждый раз, когда вызываете свой код, поэтому у вас будет много его экземпляров, пытающихся получить доступ к одному и тому же сеансу.HibernateSession будет пытаться создать новую транзакцию, когда вы вызываете hs.createTransaction();, и поэтому вы получаете исключение в этой строке, потому что уже есть открытые транзакции, и вы пытаетесь открыть новую, потому что вы звонить session.beginTransaction(); каждый раз, не звоня в transaction.close();.Здесь вы можете сделать свой HibernateSessionclass синглтоном, чтобы был доступен только один экземпляр, вы можете проверить этот Реализация одноэлементного шаблона для получения дополнительных сведений о том, как его реализовать.
И в методе createTransaction, который лучше называть getTransaction, а не просто вызывать session.beginTransaction();, вам нужно получить текущую транзакцию, если она существует, вы можете проверить ее с помощью метода session.getTransaction() и убедиться, что вы заключили свой код в блок try ...catch, вы можете проверить это Сеанс гибернации учебник для получения дополнительной информации.
Как видно из его редактирования, у него есть Singleton Factory, которую он может использовать для создания новых сессий, что было бы совершенно нормально. Его настоящая ошибка состоит в том, что в его методе фиксации нет ни finally { session.close() }, ни даже try{ .. }. Это оставило его сеанс открытым в какой-то момент, и теперь он застрял.
@ rwenz3l Да, я знаю, что код за ним следует одноэлементному шаблону, но поскольку он создает новый класс для обработки этого, ему лучше сделать это и на уровне своего класса.
У вас есть несколько недостатков, связанных с дизайном:
getCurrentSession()Как указано в jboss документация, вы должны использовать следующую идиому:
Session sess = factory.openSession();
Transaction tx;
try {
tx = sess.beginTransaction();
//do some work
...
tx.commit();
}
catch (Exception e) {
if (tx!=null) tx.rollback();
throw e;
}
finally {
sess.close();
}
Для каждой операции (создание, чтение, обновление, удаление) создайте метод в своем классе. Каждый метод должен создать свой сеанс собственный и транзакцию для работы, потому что, как указано в этот ответ:
session is not a thread safe object - cannot be shared by multiple threads. You should always use "one session per request" or "one session per transaction"
Ваши методы несколько верны, но также посмотрите на объекты DAO, созданные в этот учебник
Решение:
Причина сбоя вашего кода заключается просто в том, что транзакция все еще выполняется после вашего предыдущего взаимодействия с базой данных. Вот почему вы делаете finally{ session.close() } - чтобы сделать Конечно, чтобы сессия закрывалась при выходе из метода. Я предполагаю, что в какой-то момент ваш commit() не был успешным и оставил вашу гибернацию в транзакции / сеансе, которая с тех пор не закрывалась.
Чтобы исправить это, вы должны один раз вставить session.close () в свой код, выполнить его, а затем реализовать предложенный мной блок try-catch-finally, чтобы убедиться, что он закрыт в будущем.
Я изменил getCurrentSession () на openSession ()
Я только что изменился
session = HibernateUtil.getSessionFactory().getCurrentSession();
с участием
session = HibernateUtil.getSessionFactory().openSession();
Я уже открывал сеанс Фабрики.
Это будет работать, но на самом деле это откроет сеанс для каждого вызова, это плохая практика, постарайтесь не делать этого.
Можете ли вы добавить HibernateUtil ...