千家信息网

怎么使用Hibernate在Java中批量更新或插入数据库表

发表于:2025-02-23 作者:千家信息网编辑
千家信息网最后更新 2025年02月23日,这篇文章主要介绍怎么使用Hibernate在Java中批量更新或插入数据库表,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Hibernate 是最流行的 ORM 框架,用于在
千家信息网最后更新 2025年02月23日怎么使用Hibernate在Java中批量更新或插入数据库表

这篇文章主要介绍怎么使用Hibernate在Java中批量更新或插入数据库表,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

Hibernate 是最流行的 ORM 框架,用于在 Java 中与数据库进行交互。在本文中,我们将探讨使用批量选择和更新的各种方法以及在 Java 中使用 Hibernate 框架时最有效的方法。

我尝试了三种方式,分别如下:

  • 使用 Hibernate 的 Query.list() 方法。

  • 在 FORWARD_ONLY 滚动模式下使用 ScrollableResults。

  • 在 StatelessSession 中使用具有 FORWARD_ONLY 滚动模式的 ScrollableResults。

为了确定哪一种为我们的用例提供最佳性能,我使用上面列出的三种方法进行了以下测试。

  • 选择并更新 1000 行。

下面我们将上述三种方式一一应用到上述操作中,看看代码和结果。

使用 Hibernate 的 Query.list() 方法

执行的代码:

java:

List rows;        Session session = getSession();        Transaction transaction = session.beginTransaction();        try {            Query query = session.createQuery("FROM PersonEntity WHERE id > :maxId ORDER BY id").setParameter("maxId",                    MAX_ID_VALUE);            query.setMaxResults(1000);            rows = query.list();            int count = 0;            for (Object row : rows) {                PersonEntity personEntity = (PersonEntity) row;                personEntity.setName(randomAlphaNumeric(30));                session.saveOrUpdate(personEntity);                //Always flush and clear the session after updating 50(jdbc_batch_size specified in hibernate.properties) rows                if (++count % 50 == 0) {                    session.flush();                    session.clear();                }            }        } finally {            if (session != null && session.isOpen()) {                transaction.commit();                session.close();            }        }

测试结果:

  • 耗时:360s到400s

  • 堆模式:- 从 13m 逐渐增加到 51m(从 jconsole)。

在 FORWARD_ONLY 滚动模式下使用 ScrollableResults。

有了这个,我们预计它应该比第一种方法消耗更少的内存。让我们看看下面的结果。

执行的代码:

java:

 Session session = getSession();        Transaction transaction = session.beginTransaction();        ScrollableResults scrollableResults = session                .createQuery("FROM PersonEntity WHERE id > " + MAX_ID_VALUE + " ORDER BY id")                .setMaxResults(1000).scroll(ScrollMode.FORWARD_ONLY);        int count = 0;        try {            while (scrollableResults.next()) {                PersonEntity personEntity = (PersonEntity) scrollableResults.get(0);                personEntity.setName(randomAlphaNumeric(30));                session.saveOrUpdate(personEntity);                if (++count % 50 == 0) {                    session.flush();                    session.clear();                }            }        } finally {            if (session != null && session.isOpen()) {                transaction.commit();                session.close();            }        }
检测结果:
  • 花费的时间:185秒到200秒。

  • 堆模式:- 从 13MB 逐渐增加到 41MB(使用 jconsole 测量相同)。

在 StatelessSession 中使用 ScrollableResults 和 FORWARD_ONLY 滚动模式

无状态会话不实现一级缓存,也不与任何二级缓存交互,也不实现事务性后写或自动脏检查,也不执行级联到关联实例的操作。无状态会话会忽略集合。通过无状态会话执行的操作绕过 Hibernate 的事件模型和拦截器。

在批量更新的情况下始终建议使用这种类型的会话,因为在这些类型的用例中我们确实不需要这些休眠功能的开销。

执行的代码:

java:

  StatelessSession session = getStatelessSession();        Transaction transaction = session.beginTransaction();        ScrollableResults scrollableResults = session                .createQuery("FROM PersonEntity WHERE id > " + MAX_ID_VALUE + " ORDER BY id")                .setMaxResults(TRANSACTION_BATCH_SIZE).scroll(ScrollMode.FORWARD_ONLY);        try {            while (scrollableResults.next()) {                PersonEntity personEntity = (PersonEntity) scrollableResults.get(0);                personEntity.setName(randomAlphaNumeric(20));                session.update(personEntity);            }        } finally {            if (session != null && session.isOpen()) {                transaction.commit();                session.close();            }        }

测试结果:

  • 花费的时间:185 秒到 200 秒。

  • 堆模式:- 从 13MB 逐渐增加到 39MB。

我还对 2000 行进行了相同的测试,得到的结果如下:

结果:

  • 使用 list():- 花费的时间:大约 750 秒,堆模式:从 13MB 逐渐增加到 74 MB。

  • 使用 ScrollableResultSet:花费的时间:大约 380s,堆模式:从 13MB 逐渐增加到 46MB

  • 使用 Stateless:花费的时间:大约 380 秒,堆模式:从 13MB 逐渐增加到 43MB

上述所有方法的拦截器问题

ScrollableResults 和 Stateless ScrollableResults 提供几乎相同的性能,这比 Query.list() 好得多。但是上述所有方法仍然存在一个问题。加锁,以上所有方法都在同一个事务中选择和更新数据,这意味着只要事务正在运行,已经执行更新的行将被锁定,任何其他操作都必须等待事务完成结束。

一个办法

为了解决上述问题,我们应该在这里做两件事:

  • 我们需要在不同的事务中选择和更新数据。

  • 并且,这些类型的更新应该分批进行

因此,我再次执行了与上述相同的测试,但这次更新是在一个不同的事务中执行的,该事务以 50 个为一组提交。

注意:在 Scrollable 和 Stateless 的情况下,我们还需要一个不同的会话,因为我们需要原始会话和事务来滚动结果。

使用批处理的结果

  • 使用list():花费的时间:大约400s,堆模式:从13MB逐渐增加到61MB。

  • 使用 ScrollableResultSet:花费的时间:大约 380 秒,堆模式:从 13MB 逐渐增加到 51MB。

  • 使用无状态:花费的时间:大约 190 秒,堆模式:从 13MB 逐渐增加到 44MB。

观察:ScrollableResults 的这个时间性能下降到几乎等于 Query.list(),但 Stateless 的性能几乎保持不变。

总结与结论

从以上所有的实验来看,在我们需要做批量选择和更新的情况下,从内存消耗和时间来看,最好的方法如下:

  • 在无状态会话中使用 ScrollableResults。

  • 以 20 到 50 的批次(批处理)在不同的事务中执行选择和更新(注意:批次大小可以视具体情况而定)。

最佳方法的示例代码

java:

StatelessSession session = getStatelessSession();        Transaction transaction = session.beginTransaction();        ScrollableResults scrollableResults = session                .createQuery("FROM PersonEntity WHERE id > " + MAX_ID_VALUE + " ORDER BY id")                .setMaxResults(TRANSACTION_BATCH_SIZE).scroll(ScrollMode.FORWARD_ONLY);        int count = 0;        try {            StatelessSession updateSession = getStatelessSession();            Transaction updateTransaction = updateSession.beginTransaction();            while (scrollableResults.next()) {                PersonEntity personEntity = (PersonEntity) scrollableResults.get(0);                personEntity.setName(randomAlphaNumeric(5));                updateSession.update(personEntity);                if (++count % 50 == 0) {                    updateTransaction.commit();                    updateTransaction = updateSession.beginTransaction();                }            }            updateSession.close();        } finally {            if (session != null && session.isOpen()) {                transaction.commit();                session.close();            }        }

以上是"怎么使用Hibernate在Java中批量更新或插入数据库表"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

0