使用 JPA 实体管理器进行批处理插入

2022-09-01 06:46:29

有没有办法通过JPA EntityManager使用批处理插入。我知道没有直接的方法来实现这一点,但必须有某种方法来实现这种机制。

实际上,对于每个插入操作,我需要300ms,我想使用批量插入而不是单个插入来减少。

以下是我目前正在为单个插入执行的代码

        @PersistenceContext(unitName = "testing")
        EntityManager eM;

        Query querys = this.eM.createNativeQuery(insertQuery);
        for (String s : someList) {
            //setting parameters
            querys.executeUpdate();
        }

提前致谢。


答案 1

根据事务是否包含循环,批处理通常已经发生在您的案例中。

JPA 将在其 L1 缓存中收集所有更新,并且通常在事务提交时将其全部批量写入数据库。这与 JDBC 中的批处理没有什么不同,在 JDBC 中,您添加的每个批处理项也暂时在内存中,直到您调用更新方法。

潜在的问题是,您根本没有硬性保证JPA确实执行此批处理,如果在事务提交或达到阈值时执行此操作,但我发现在实践中,特别是在涉及如此简单的更新循环的情况下,它确实可以进行批处理。

一个问题是,即使JPA确实已经进行了批处理,您仍然可能想要控制批处理大小。其他答案链接的文章为此提供了非常有用的信息。

最后,您应该知道 L1 缓存在循环中不断增长,因此,如果更新数量确实很大,请定期清除它。或者,如果您的业务逻辑可以维持它,请在多个事务中执行部分更新。例如,事务 1 中的项目 0 到 100.000,事务 2 中的项目 100.001 到 200.000,依此类推。


答案 2

我知道这是一个相当古老的问题,有一个公认的答案。尽管如此,我想给这个非常具体的主题“JPA批量插入”一个新的答案。

@PersistenceContext
private EntityManager entityManager;

@Value("${hibernate.jdbc.batch_size}")
private int batchSize;

public <T extends MyClass> Collection<T> bulkSave(Collection<T> entities) {
  final List<T> savedEntities = new ArrayList<T>(entities.size());
  int i = 0;
  for (T t : entities) {
    savedEntities.add(persistOrMerge(t));
    i++;
    if (i % batchSize == 0) {
      // Flush a batch of inserts and release memory.
      entityManager.flush();
      entityManager.clear();
    }
  }
  return savedEntities;
}

private <T extends MyClass> T persistOrMerge(T t) {
  if (t.getId() == null) {
    entityManager.persist(t);
    return t;
  } else {
    return entityManager.merge(t);
  }
}

资料来源:http://frightanic.com/software-development/jpa-batch-inserts/


推荐