使用 JPA/EJB3 进行批量插入

Posted

技术标签:

【中文标题】使用 JPA/EJB3 进行批量插入【英文标题】:Batch inserts with JPA/EJB3 【发布时间】:2010-10-01 16:22:11 【问题描述】:

JPA/EJB3 框架是否提供标准方式来执行批量插入操作...? 我们使用hibernate作为持久化框架,所以我可以回退到Hibernate Session并使用组合session.save()/session.flush()实现批量插入。但是想知道 EJB3 是否支持这个...

【问题讨论】:

【参考方案1】:

JPA 和 Hibernate 都没有为批量插入提供特别的支持,使用 JPA 进行批量插入的习惯用法与使用 Hibernate 相同:

EntityManager em = ...;
EntityTransaction tx = em.getTransaction();
tx.begin();

for ( int i=0; i<100000; i++ ) 
    Customer customer = new Customer(.....);
    em.persist(customer);
    if ( i % 20 == 0 )  //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        em.flush();
        em.clear();
    


tx.commit();
session.close();

在这种情况下使用 Hibernate 的专有 API 并没有提供任何 IMO 优势。

参考文献

JPA 1.0 规范 第 4.10 节“批量更新和删除操作” Hibernate Core 参考指南 Chapter 13. Batch processing

【讨论】:

只是为了进行完整性检查,您的 if 语句应该是 i &gt; 0 &amp;&amp; i % 20 == 0 否则它会在添加第一个元素后立即刷新(并清除)。【参考方案2】:

具体针对hibernate,全程chapter 13 of the core manual讲解方法。

但是你说你想要通过 Hibernate 的 EJB 方法,所以实体管理器文档也有一章关于 here。我建议您同时阅读(核心和实体管理器)。

在 EJB 中,它只是关于使用 EJB-QL(有一些限制)。如果您需要更多灵活性,Hibernate 会提供更多机制。

【讨论】:

【参考方案3】:

对于中等记录数,您可以这样使用:

em.getTransaction().begin();
for (int i = 1; i <= 100000; i++) 
     Point point = new Point(i, i);
     em.persist(point);
     if ((i % 10000) == 0) 
          em.flush();
          em.clear();
     

em.getTransaction().commit();

但如果记录数很大,您应该在多个事务中执行此任务:

em.getTransaction().begin();
for (int i = 1; i <= 1000000; i++) 
      Point point = new Point(i, i);
      em.persist(point);
      if ((i % 10000) == 0) 
          em.getTransaction().commit();
          em.clear();          
          em.getTransaction().begin();
      

em.getTransaction().commit();

参考:JPA Batch Store

【讨论】:

【参考方案4】:

是的,如果您愿意,您可以回滚到您的 JPA 实现以获得您定义的控制。

JPA 1.0 对 EL-HQL 的支持很丰富,但对 Criteria API 的支持比较少,不过这已在 2.0 中得到解决。

Session session = (Session) entityManager.getDelegate();
session.setFlushMode(FlushMode.MANUAL);

【讨论】:

【参考方案5】:

帕斯卡

在您插入 100000 条记录的示例中,它是在单个事务中完成的,因为 commit() 仅在最后被调用。它会给数据库带来很大压力吗?再者,万一有回滚,代价也太大了..

下面的方法会更好吗?

EntityManager em = ...;
for ( int i=0; i<100000; i++ ) 
   if(!em.getTransaction().isActive()) 
      em.getTransaction().begin();
   
   Customer customer = new Customer(.....);
   em.persist(customer);
   if ((i+1) % 20 == 0 )  //20, same as the JDBC batch size
      //flush and commit of inserts and release memory:
      em.getTransaction().commit(); 
      em.clear();
   


session.close();

【讨论】:

以上是关于使用 JPA/EJB3 进行批量插入的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NamedParameterJdbcTemplate 进行批量插入

在 NpgSql 中使用 BeginBinaryImport 插入位数据类型进行批量数据插入

使用 Node.js/Sequelize 进行批量插入时 PostgreSQL 崩溃

使用 JDBC 和唯一约束进行批量插入

使用 Django REST 框架进行批量插入的最佳设计模式是啥?

批量插入数据