Hibernate:同时有两个或多个事务:事务alredy active

时间:2018-06-15 11:00:26

标签: java hibernate rest jersey

我有一个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()块和404s中我总是做一个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);
        }

    }

}

5 个答案:

答案 0 :(得分:3)

根据您的SessionFactory逻辑,您将从HibernateSession获取当前会话并从中开始交易。

这是问题所在。

假设您已创建HibernateSession对象并启动了事务,但它仍在进行中。所以你还没有关闭交易。

现在你创建了另一个public void createTransaction() { session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); } 并尝试启动事务,这样做会抛出异常。

所以你的代码

public void createTransaction() {
    session = HibernateUtil.getSessionFactory().openSession();
    session.beginTransaction();
}

必须是这个

{{1}}

getSessionFactory()。openSession()始终打开一个新的会话,一旦完成操作,您必须关闭该会话。

getSessionFactory()。getCurrentSession()返回绑定到上下文的会话 - 您无需关闭它。

答案 1 :(得分:1)

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

  • 每次调用代码时,您都在创建HibernateSession类的新实例,因此您将拥有许多实例,尝试访问同一会话。
  • HibernateSession的每个实例都会在您调用hs.createTransaction();时尝试创建新的交易,这就是为什么您在此行获取异常的原因,因为已经有打开一个交易,你试图打开一个新的交易,因为你每次都打电话session.beginTransaction();而不打电话给transaction.close();

这里你可以做的是制作HibernateSession班级单身,因此只有一个实例可用,你可以查看Singleton Pattern implementation以获取有关如何实施它的更多细节。

createTransaction方法中,应该更好地命名为getTransaction,而不是仅仅调用session.beginTransaction();,如果它存在则需要获取当前事务,您可以使用{{1}进行检查方法并确保将代码包装在session.getTransaction()块中,您可以查看Hibernate session tutorial以获取更多详细信息。

答案 2 :(得分:1)

您有一些与设计相关的缺陷:

  1. 您不应该使用getCurrentSession()
  2. 如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模式
    2. 对于每个操作(创建,读取,更新,删除),在您的班级中创建一个方法。每个方法都应创建它的拥有会话和交易,因为如this answer中所述:

        

      会话不是线程安全对象 - 不能由多个共享   线程。您应该始终使用"每个请求一个会话"或者"一个   每次交易的会话"

      您的方法有些有效,但也可以查看在this tutorial中创建的DAO对象

      <强>解决方案:

      您的代码失败的原因仅仅是因为您之前与数据库的交互中的事务仍在进行中。这就是为什么你执行finally{ session.close() } - 使确定在离开方法时关闭会话的原因。我假设在某些时候你的commit()没有成功,并将你的休眠放在一个自那以后没有关闭的事务/会话中。

      要解决这个问题,你应该在你的代码中插入一次session.close(),然后执行它,然后实现我建议的try-catch-finally块,以确保它在将来关闭。

答案 3 :(得分:0)

我刚刚改变了

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

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

我正在访问工厂alredy开放的会议。

答案 4 :(得分:-1)

尝试将代码重写为

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的所有参考都需要进行空检查。