当前位置: 首页 > news >正文

使用JPA和Hibernate进行批量处理的最佳方式

Tips
原文作者:Vlad Mihalcea
原文地址:The best way to do batch processing with JPA and Hibernate

https://unsplash.com/search/photos/northern-light?photo=mfSY12Au5o4

在本文中,你将了解什么是批处理,为什么要使用它,以及如何在JPA和Hibernate中正确使用它。

批处理

在编写企业应用程序时,通常将工作分配在服务于典型OLTP(在线事务处理)传输的前端系统,和一个或多个批处理用于ETL(Extract,Transform,Load)操作。

批处理器,顾名思义,将要处理的数据分成几个块,因此具有以下优点:

  • 每个块可以通过单独的工作线程进行处理,因此增加了吞吐量并减少了整个处理时间。
  • 每个块都可以使用自己的数据库事务,所以如果有一个失败,就不用丢掉我们所做的所有工作,只是针对当前事务的变化。

JPA 批处理

当使用JPA时,假设要插入50个Post实体,那么应该这样做:

int entityCount = 50;
int batchSize = 25;
 
EntityManager entityManager = null;
EntityTransaction transaction = null;
 
try {
    entityManager = entityManagerFactory()
        .createEntityManager();
 
    transaction = entityManager.getTransaction();
    transaction.begin();
 
    for ( int i = 0; i < entityCount; ++i ) {
        if ( i > 0 && i % batchSize == 0 ) {
            entityManager.flush();
            entityManager.clear();
 
            transaction.commit();
            transaction.begin();
        }
 
        Post post = new Post( 
            String.format( "Post %d", i + 1 ) 
        );
        entityManager.persist( post );
    }
 
    transaction.commit();
} catch (RuntimeException e) {
    if ( transaction != null && 
         transaction.isActive()) {
        transaction.rollback();
    }
    throw e;
} finally {
    if (entityManager != null) {
        entityManager.close();
    }
}

每个开始操作都会开启事务,因为每个实体状态转换必须在数据库事务的范围内执行。

for循环一次会持久化一个Post实体。 但是,由于实体状态转换仅在执行flush方法更新数据库时执行,因此我们可以将多个SQL 插入语句分组到到要给单一PreparedStatement执行中,该执行需要多个参数。

每次迭代计数器(变量i)已达到batchSize临界值的倍数,我们可以刷新EntityManager并提交数据库事务。 通过在每次批处理执行后提交数据库事务,我们获得以下优点:

  1. 避免了长期运行的事务,这对MVCC关系数据库系统是不利的。
  2. 我们确保如果执行失败,不会丢失以前成功执行的批处理作业完成的工作。

EntityManager在每次批量执行后被清除,这样就不会继续累积可能导致管理实体的几个问题:

  1. 如果要持久化的实体数量庞大,那么存在内存不足的风险。
  2. 在持久化上下文中累积的实体越多,flush越慢。 所以,最好的做法是确保持久性上下文尽可能的短小。

如果抛出异常,我们必须确保回滚当前正在运行的数据库事务。 否则可能会导致许多问题,因为数据库可能仍然认为事务处于打开状态,锁可能会被持有,直到事务超时或由DBA结束。

最后,我们需要关闭EntityManager,以便可以清除上下文并释放Session级的资源。

虽然这是使用JPA进行批处理的正确方法,但还没有完成。 如前所述,也可以从JDBC批量更新中受益。 为此,我们需要提供以下Hibernate配置属性:

<property
    name="hibernate.jdbc.batch_size"
    value="25"
/>
 
<property
    name="hibernate.order_inserts"  
    value="true"
/>
 
<property
    name="hibernate.order_updates"  
    value="true"
/>

这些属性允许我们将多个SQL语句批处理为单个PreparedStatement执行,这需要单个数据库往返。 选择值25以匹配EntityManager批处理作业的临界值。

执行以前的测试用例时,Hibernate生成2个SQL INSERT语句:

INSERT INTO post (title, id) values (?, ?)"], 
Params:[
    (Post 1, 1), (Post 2, 2), (Post 3, 3), 
    (Post 4, 4), (Post 5, 5), (Post 6, 6), 
    (Post 7, 7), (Post 8, 8), (Post 9, 9), 
    (Post 10, 10), (Post 11, 11), (Post 12, 12), 
    (Post 13, 13), (Post 14, 14), (Post 15, 15), 
    (Post 16, 16), (Post 17, 17), (Post 18, 18), 
    (Post 19, 19), (Post 20, 20), (Post 21, 21), 
    (Post 22, 22), (Post 23, 23), (Post 24, 24), 
    (Post 25, 25)
]
 
INSERT INTO post (title, id) values (?, ?)"], 
Params:[
    (Post 26, 26), (Post 27, 27), (Post 28, 28), 
    (Post 29, 29), (Post 30, 30), (Post 31, 31), 
    (Post 32, 32), (Post 33, 33), (Post 34, 34), 
    (Post 35, 35), (Post 36, 36), (Post 37, 37), 
    (Post 38, 38), (Post 39, 39), (Post 40, 40), 
    (Post 41, 41), (Post 42, 42), (Post 43, 43), 
    (Post 44, 44), (Post 45, 45), (Post 46, 46), 
    (Post 47, 47), (Post 48, 48), (Post 49, 49), 
    (Post 50, 50)
]

结论

了解如何正确设计批处理作业在开发企业应用程序时非常重要。 幸运的是,使用JPA和Hibernate,这个任务很容易实现。 最重要的方面是考虑到如何让关系数据库工作效果最好,这就是驱动数据访问设计决策的因素。

转载于:https://www.cnblogs.com/IcanFixIt/p/7042977.html

相关文章:

  • Linux系统下GDB调试
  • 【安卓9】SimpleCursorAdapter、在列表中展示数据
  • 查看windows进程,并删除
  • 阿里云上部署开源PaaS平台Cloud Foundry实战
  • 页码生成算法
  • C++内联函数
  • 收缩数据文件
  • Flask 扩展 表单
  • openfalcon-0.2 配置
  • elasticsearch从入门到出门-08-Elasticsearch容错机制:master选举,replica容错,数据恢复...
  • [Reprinted] 使用Spring Data Redis操作Redis(一) 很全面
  • border:none;和border:0;的区别
  • win server2012 r2 服务器共享文件夹设置
  • 页头开发
  • mysql主从同步(5.1版本)
  • [iOS]Core Data浅析一 -- 启用Core Data
  • Cookie 在前端中的实践
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • extjs4学习之配置
  • Fastjson的基本使用方法大全
  • JavaScript设计模式之工厂模式
  • jQuery(一)
  • node学习系列之简单文件上传
  • sessionStorage和localStorage
  • SQLServer之创建显式事务
  • Vue 动态创建 component
  • vue的全局变量和全局拦截请求器
  • webgl (原生)基础入门指南【一】
  • 时间复杂度与空间复杂度分析
  • 使用putty远程连接linux
  • 小程序测试方案初探
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • ​flutter 代码混淆
  • ​secrets --- 生成管理密码的安全随机数​
  • ​插件化DPI在商用WIFI中的价值
  • "无招胜有招"nbsp;史上最全的互…
  • #include
  • #pragma multi_compile #pragma shader_feature
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (Java数据结构)ArrayList
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • (转)http-server应用
  • .net core 6 集成和使用 mongodb
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .net6+aspose.words导出word并转pdf
  • .net中调用windows performance记录性能信息
  • .project文件
  • // an array of int
  • /bin/bash^M: bad interpreter: No such file or directory
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解