Hibernate:同时存在两个或多个事务:事务已处于活动状态
Hibernate: Two or more transactions at the same time: Transaction already active
我有一个 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)
它们在不同的 classes 上,因此没有暗示全局属性。
失败的是这一行:
HibernateSession hs = new HibernateSession();
hs.createTransaction(); // Crash
它指的是我的 class 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();
Returns 200 没问题,但 GET 可能会因上述异常而崩溃。
当我正在创建 HibernateSession 的 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);
}
}
}
尝试将代码重写为
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
.
的每个引用都需要进行偏离当然的空值检查
根据您的 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() returns 绑定到上下文的会话 - 您不需要关闭它。
实际上你的实际代码有很多缺点:
- 每次调用代码时,您都在创建
HibernateSession
class 的新实例,因此您将拥有多个实例,试图访问同一个会话。
HibernateSession
的每个实例都将在您调用 hs.createTransaction();
时尝试创建一个新事务,这就是您在这一行收到异常的原因,因为已经有打开的事务你正在尝试打开一个新的,因为你每次都在调用 session.beginTransaction();
而没有调用 transaction.close();
.
你在这里可以做的是让你的 HibernateSession
class 单例,所以只有一个实例可用,你可以查看这个 Singleton Pattern implementation 以了解更多关于如何实现它的细节.
并且在 createTransaction
方法中,最好将其命名为 getTransaction
而不是仅仅调用 session.beginTransaction();
您需要获取当前交易(如果存在)您可以使用 session.getTransaction()
方法并确保将代码包装在 try ...catch
块中,您可以查看此 Hibernate session tutorial 了解更多详细信息。
您有一些与设计相关的缺陷:
- 你不应该使用
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();
}
- 您应该遵循 CRUD 模式
为每个操作(创建、读取、更新、删除)在您的 class 中创建一个方法。每个方法都应创建它自己的 会话和交易,因为如 this answer 中所述:
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"
您的方法有些有效,但也可以查看 this tutorial
中创建的 DAO 对象
解法:
您的代码失败的原因很简单,因为您之前与数据库的交互仍在进行中。这就是为什么你做一个 finally{ session.close() }
- 使 确定 在离开方法时会话关闭。我假设在某些时候你的 commit()
没有成功并且让你的休眠状态留在 Transaction/Session 中,此后一直没有关闭。
要解决这个问题,您应该在代码中插入一次 session.close(),执行它,然后实施我建议的 try-catch-finally 块以确保它在将来关闭。
我刚改了
session = HibernateUtil.getSessionFactory().getCurrentSession();
和
session = HibernateUtil.getSessionFactory().openSession();
我正在访问已经打开的 Factory 会话。
我有一个 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)
它们在不同的 classes 上,因此没有暗示全局属性。
失败的是这一行:
HibernateSession hs = new HibernateSession();
hs.createTransaction(); // Crash
它指的是我的 class 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();
Returns 200 没问题,但 GET 可能会因上述异常而崩溃。 当我正在创建 HibernateSession 的 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);
}
}
}
尝试将代码重写为
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
.
根据您的 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() returns 绑定到上下文的会话 - 您不需要关闭它。
实际上你的实际代码有很多缺点:
- 每次调用代码时,您都在创建
HibernateSession
class 的新实例,因此您将拥有多个实例,试图访问同一个会话。 HibernateSession
的每个实例都将在您调用hs.createTransaction();
时尝试创建一个新事务,这就是您在这一行收到异常的原因,因为已经有打开的事务你正在尝试打开一个新的,因为你每次都在调用session.beginTransaction();
而没有调用transaction.close();
.
你在这里可以做的是让你的 HibernateSession
class 单例,所以只有一个实例可用,你可以查看这个 Singleton Pattern implementation 以了解更多关于如何实现它的细节.
并且在 createTransaction
方法中,最好将其命名为 getTransaction
而不是仅仅调用 session.beginTransaction();
您需要获取当前交易(如果存在)您可以使用 session.getTransaction()
方法并确保将代码包装在 try ...catch
块中,您可以查看此 Hibernate session tutorial 了解更多详细信息。
您有一些与设计相关的缺陷:
- 你不应该使用
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();
}
- 您应该遵循 CRUD 模式
为每个操作(创建、读取、更新、删除)在您的 class 中创建一个方法。每个方法都应创建它自己的 会话和交易,因为如 this answer 中所述:
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"
您的方法有些有效,但也可以查看 this tutorial
中创建的 DAO 对象解法:
您的代码失败的原因很简单,因为您之前与数据库的交互仍在进行中。这就是为什么你做一个 finally{ session.close() }
- 使 确定 在离开方法时会话关闭。我假设在某些时候你的 commit()
没有成功并且让你的休眠状态留在 Transaction/Session 中,此后一直没有关闭。
要解决这个问题,您应该在代码中插入一次 session.close(),执行它,然后实施我建议的 try-catch-finally 块以确保它在将来关闭。
我刚改了
session = HibernateUtil.getSessionFactory().getCurrentSession();
和
session = HibernateUtil.getSessionFactory().openSession();
我正在访问已经打开的 Factory 会话。