hibernate 休眠:同时执行两个或多个事务:交易已激活

yzuktlbb  于 2023-01-05  发布在  其他
关注(0)|答案(6)|浏览(139)

我有一个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可能会崩溃,出现上面的异常。当我正在创建一个Hibernate会话的new示例时,为什么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);
        }

    }

}
f8rj6qna

f8rj6qna1#

按照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()**返回一个绑定到上下文的会话-您不需要关闭它。

fzsnzjdm

fzsnzjdm2#

你有一些设计上的缺陷:
1.不应使用getCurrentSession()
正如jboss documentation中所述,您应该使用以下习惯用法:

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();
 }

1.您应该遵循CRUD模式
对于每个操作(创建、读取、更新、删除),在类中创建一个方法。每个方法将创建它自己的会话和事务,因为如this answer中所述:
会话不是线程安全对象-不能由多个线程共享。应始终使用"每个请求一个会话"或"每个事务一个会话"
您的方法在某种程度上是有效的,但还需要查看一下在this tutorial中创建的DAO对象

    • 解决方案:**

你的代码失败的原因很简单,因为事务仍然在进行中,从你以前的交互数据库。这就是为什么你做一个finally{ session.close() }-以确保确保会话关闭时,离开方法。我假设在某个时候你的commit()是不成功的,并离开你的事务/会话休眠尚未关闭。
要解决这个问题,您应该在代码中插入一次session. close(),执行它,然后实现我建议的try-catch-finally块,以确保它在将来关闭。

hmmo2u0o

hmmo2u0o3#

实际上,在您的实际代码中有许多缺点:

  • 每次调用代码时,您都在创建HibernateSession类的一个新示例,因此您将拥有它的许多示例,试图访问同一个会话。
  • 当你调用hs.createTransaction();时,HibernateSession的每个示例都将尝试创建一个新的事务,这就是为什么你在此行得到异常,因为已经有打开的事务,你正在尝试打开一个新的,因为你每次都调用session.beginTransaction();,而没有调用transaction.close();

这里你可以做的是使你的HibernateSession类成为单例类,所以只有一个示例可用,你可以查看这个Singleton Pattern implementation来获得关于如何实现它的更多细节。
createTransaction方法中,应该更好地命名为getTransaction,而不是只调用session.beginTransaction();,如果当前事务存在,您需要获取它,您可以使用session.getTransaction()方法检查它,并确保您的代码 Package 在try ...catch块中,您可以检查此Hibernate session tutorial以了解更多细节。

qeeaahzv

qeeaahzv4#

我刚换了衣服

session = HibernateUtil.getSessionFactory().getCurrentSession();

session = HibernateUtil.getSessionFactory().openSession();

我正在访问工厂的会话已经打开。

ahy6op9u

ahy6op9u5#

我得到了同样的错误。在我的例子中,会话创建后,我忘记了指令:

session.beginTransaction();
p1iqtdky

p1iqtdky6#

尝试将代码重写为

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的每个引用都需要空校验。

相关问题