Hibernate 4 批处理

    在本文中,我将为您简要介绍 Hibernate 批处理。

    因为这比打开一个事务向数据库插入 1000000(一百万)个条目并最后提交或为每个相同容量的插入打开事务要好。

    批处理为您提供了管理此工具的正确工具:在 Hibernate 自动调用之后定义一个限制,以将您的数据持久存储在应用后面的数据库中。

    朴素的方法

    上面的代码做了我一开始所描述的:它打开一个事务并在插入 1000000 条记录后保存。 根据您的硬件,您可以到达循环的结尾并在结尾处调用一次提交。 如果您的内存不足,您的应用可能会抛出OutOfMemoryException,因为 Hibernate 将所有新的Book实例存储在内存中的第二级缓存中。

    为了进行测试,我将插入次数增加到 10000000(一千万),以查看应用崩溃之前需要花费多长时间。 凭借近三百万本新书,我达到了应用的 2GB 内存。

    为了使二级缓存的大小保持较低,您可以在hibernate.cfg.xml中引入批处理大小。 这将告诉 Hibernate 容器每隔 n 行要批量插入。 可以使用属性hibernate.jdbc.batch_size设置批处理大小。

    有趣的是,Hibernate 的文档不仅引入了此属性,而且还需要修改上面的朴素代码(我只会复制相关的循环):

    如果仔细看一下上面的代码,您会看到解决方案是将批处理大小大块中的会话flush()clear()保持为较低的二级缓存大小。

    很好的问题,我已经搜索了 Hibernate 的文档以找到有关此信息,但未找到任何信息。 但是,如果考虑到这一点,批处理可以通过将一堆语句组合在一起,从而使数据库有效地执行插入和更新语句。

    批次大小无法保证

    设置批处理限制并不能保证仅由于将数据刷新到数据库而使第二级缓存大小保持较小。

    一个示例是如果您使用GenerationType.IDENTITY,则 Hibernate 透明地禁用批处理。

    第二个示例是 Hibernate 查看要一起批处理的语句:如果当前语句与前一个相同,则在未达到batch_size的情况下将它们合并在一起。 在上面的示例中,语句相似,因此将它们批处理在一起。 但是,如果我们将“作者”和“书籍”一起添加到数据库中,则 Hibernate 会看到交替的语句,并且将从每个语句开始一个批处理组。 要解决此问题,可以使用和hibernate.order_updates属性。 这使 Hibernate 在插入之前对语句进行了排序,因此可以看到 50 个Book插入可以一起批处理,并且 50 个Authors可以一起批处理。

    我们已经解决了消耗大量内存的问题,但是插入时的异常又如何呢? 在大多数情况下,由于最后一个失败而回滚一百万次插入是不可行的。

    解决方案是手动调用事务的提交以及批处理大小:

    如果我们达到了batch_size,则上面的代码将提交事务,并在会话上开始新事务,因为前一个事务已因提交而失效。

    总结