Hibernate 在事务开始函数时冻结
Hibernate freezes at transaction begin function
我在我的服务器上使用 Hibernate(tomcat8、hibernate、postgresql)。
每天结束时我的代码 运行s(使用 Quartz)一些代码,它在存储过程(休眠)中调用:
public void execute(JobExecutionContext context)
throws JobExecutionException {
log.info("=========Start daily update==========");
long startTime = System.currentTimeMillis();
boolean transactionCompleted = false;
int retryCount = HibernateUtil.RETRY_COUNT;
HibernateUtil.closeCurrentEntityManager();
EntityManager em = HibernateUtil.currentEntityManager();
while (!transactionCompleted)
{
try {
em.getTransaction().begin();
dailyUpdateDao.dailyUpdate();
em.getTransaction().commit();
transactionCompleted = true;
} catch (PersistenceException ex) {
if (!HibernateUtil.isDeadlockException(ex) || retryCount == 0) {
log.error("non deadlock error", ex);
throw ex;
}
log.error("deadlock detected. Retrying {}", HibernateUtil.RETRY_COUNT - retryCount);
retryCount--;
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
try {
Thread.sleep(HibernateUtil.sleepIntervalWhenDeadlockDetected(retryCount));
} catch(Exception sleepex) {
log.error("non deadlock sleep ex", ex);
throw ex;
}
}
}
log.info("===========Daily Update Job Completed ============== It took {} ms", System.currentTimeMillis() - startTime);
}
上面代码中的 dailyUpdate 函数正在执行以下操作:
public void dailyUpdate() {
String sql = "select count(*) FROM daily_update()";
EntityManager em = HibernateUtil.currentEntityManager();
em.createNativeQuery(sql).getSingleResult();
}
(通过休眠使用本机 sql 调用存储过程)。
当我 运行 服务器时,它会正常执行前 2 或 3 个调用。下一个电话永远不会结束。我在本地重现了这个问题,而不是每天我都安排每 1 分钟开始一次任务。它向我显示了这样的日志:
Daily Update Job Completed ============== It took 7338 ms
Daily Update Job Completed ============== It took 6473 ms
...
Daily Update Job Completed ============== It took 183381 ms
所以延迟增加了,我决定看看里面发生了什么。
在上面的代码中,当它尝试执行
em.getTransaction().begin();
它永远不会完成,堆栈跟踪如下图所示:
是什么原因以及如何解决问题?
编辑 1: currentEntityManager 和 closeCurrentEntityManager 代码:
public class HibernateUtil {
...
private static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENT_UNIT);
public static EntityManager currentEntityManager(EntityManagerFactory emf)
throws HibernateException {
EntityManager em = (EntityManager) entityManager.get();
if (em == null||!em.isOpen()) {
em = emf.createEntityManager();
entityManager.set(em);
}
return em;
}
public static void closeCurrentEntityManager() {
EntityManager s = (EntityManager) entityManager.get();
try {
if (s != null) {
if (s.getTransaction().isActive()) {
if (s.getTransaction().getRollbackOnly()) {
s.getTransaction().rollback();
} else {
s.getTransaction().commit();
}
}
s.close();
}
} finally {
entityManager.remove();
}
}
好的,完美,在发布了关于什么是 HibernateUtil
、closeCurrentEntityManager()
和 currentEntityManager()
是如何工作的以及这个 [=43 中的 entityManager
字段是什么的详细信息之后=], 一切都清楚了。
看看你的代码。您首先关闭 "current" 实体管理器:
HibernateUtil.closeCurrentEntityManager();
但是您应该考虑到 quartz scheduller
在不同线程中启动其任务这一事实。所以
public static void closeCurrentEntityManager() {
EntityManager s = (EntityManager) entityManager.get();
将return null
(如果是新线程)。
下一步调用
EntityManager em = HibernateUtil.currentEntityManager();
这也将创建新的 EntityManager
因为它是新线程:
em = emf.createEntityManager();
现在看看您的屏幕截图:您的 beginTransaction()
方法正在等待新连接。这里发生的事情是你创建了新的 entitmanager 来打开新的连接,但不要关闭它。所以基本上你的池中没有可用连接。
只需尝试将 HibernateUtil.closeCurrentEntityManager();
移动到 final { ... }
块中并再次测试。
我在我的服务器上使用 Hibernate(tomcat8、hibernate、postgresql)。 每天结束时我的代码 运行s(使用 Quartz)一些代码,它在存储过程(休眠)中调用:
public void execute(JobExecutionContext context)
throws JobExecutionException {
log.info("=========Start daily update==========");
long startTime = System.currentTimeMillis();
boolean transactionCompleted = false;
int retryCount = HibernateUtil.RETRY_COUNT;
HibernateUtil.closeCurrentEntityManager();
EntityManager em = HibernateUtil.currentEntityManager();
while (!transactionCompleted)
{
try {
em.getTransaction().begin();
dailyUpdateDao.dailyUpdate();
em.getTransaction().commit();
transactionCompleted = true;
} catch (PersistenceException ex) {
if (!HibernateUtil.isDeadlockException(ex) || retryCount == 0) {
log.error("non deadlock error", ex);
throw ex;
}
log.error("deadlock detected. Retrying {}", HibernateUtil.RETRY_COUNT - retryCount);
retryCount--;
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
try {
Thread.sleep(HibernateUtil.sleepIntervalWhenDeadlockDetected(retryCount));
} catch(Exception sleepex) {
log.error("non deadlock sleep ex", ex);
throw ex;
}
}
}
log.info("===========Daily Update Job Completed ============== It took {} ms", System.currentTimeMillis() - startTime);
}
上面代码中的 dailyUpdate 函数正在执行以下操作:
public void dailyUpdate() {
String sql = "select count(*) FROM daily_update()";
EntityManager em = HibernateUtil.currentEntityManager();
em.createNativeQuery(sql).getSingleResult();
}
(通过休眠使用本机 sql 调用存储过程)。
当我 运行 服务器时,它会正常执行前 2 或 3 个调用。下一个电话永远不会结束。我在本地重现了这个问题,而不是每天我都安排每 1 分钟开始一次任务。它向我显示了这样的日志:
Daily Update Job Completed ============== It took 7338 ms
Daily Update Job Completed ============== It took 6473 ms
...
Daily Update Job Completed ============== It took 183381 ms
所以延迟增加了,我决定看看里面发生了什么。 在上面的代码中,当它尝试执行
em.getTransaction().begin();
它永远不会完成,堆栈跟踪如下图所示:
是什么原因以及如何解决问题?
编辑 1: currentEntityManager 和 closeCurrentEntityManager 代码:
public class HibernateUtil {
...
private static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENT_UNIT);
public static EntityManager currentEntityManager(EntityManagerFactory emf)
throws HibernateException {
EntityManager em = (EntityManager) entityManager.get();
if (em == null||!em.isOpen()) {
em = emf.createEntityManager();
entityManager.set(em);
}
return em;
}
public static void closeCurrentEntityManager() {
EntityManager s = (EntityManager) entityManager.get();
try {
if (s != null) {
if (s.getTransaction().isActive()) {
if (s.getTransaction().getRollbackOnly()) {
s.getTransaction().rollback();
} else {
s.getTransaction().commit();
}
}
s.close();
}
} finally {
entityManager.remove();
}
}
好的,完美,在发布了关于什么是 HibernateUtil
、closeCurrentEntityManager()
和 currentEntityManager()
是如何工作的以及这个 [=43 中的 entityManager
字段是什么的详细信息之后=], 一切都清楚了。
看看你的代码。您首先关闭 "current" 实体管理器:
HibernateUtil.closeCurrentEntityManager();
但是您应该考虑到 quartz scheduller
在不同线程中启动其任务这一事实。所以
public static void closeCurrentEntityManager() {
EntityManager s = (EntityManager) entityManager.get();
将return null
(如果是新线程)。
下一步调用
EntityManager em = HibernateUtil.currentEntityManager();
这也将创建新的 EntityManager
因为它是新线程:
em = emf.createEntityManager();
现在看看您的屏幕截图:您的 beginTransaction()
方法正在等待新连接。这里发生的事情是你创建了新的 entitmanager 来打开新的连接,但不要关闭它。所以基本上你的池中没有可用连接。
只需尝试将 HibernateUtil.closeCurrentEntityManager();
移动到 final { ... }
块中并再次测试。