便携式JPA批量/批量插入

4
我刚刚看到一个由别人编写的功能,似乎效率稍低,但是我对JPA的了解不足以找到一个便携式解决方案,而不是Hibernate特定的方案。简言之,在循环内部调用的Dao方法来插入每个新实体都会执行"entityManager.merge(object);"。在JPA规范中是否有一种方法来传递实体列表到Dao方法,并进行批量/批量插入,而不是为每个单独对象调用合并?另外,由于Dao方法被注释为"@Transactional",我想知道每个单独的合并调用是否都发生在自己的事务中...这将无济于事。你有什么想法吗?
2个回答

7

在标准的JPA中,没有批量插入操作。

是的,每个插入操作将在自己的事务内进行。使用@Transactional注解(不带限定符)表示传播级别为REQUIRED(如果尚未存在事务,则创建一个事务)。假设您有:

public class Dao {
  @Transactional
  public void insert(SomeEntity entity) {
    ...
  }
}

你需要这样做:

public class Batch {
  private Dao dao;

  @Transactional
  public void insert(List<SomeEntity> entities) {
    for (SomeEntity entity : entities) {
      dao.insert(entity);
    }
  }

  public void setDao(Dao dao) {
    this.dao = dao;
  }
}

这样,整个插入组都被包含在一个事务中。如果你要处理大量的插入操作,你可能需要将它们分成1000、10000或其他适当的组,因为一个过于庞大的未提交事务可能会耗尽数据库资源,并由于大小而导致失败。

注意: @Transactional 是Spring注解。请参阅Spring参考文档中的Transactional Management


谢谢回复。在我的Dao方法中,使用Persist()而不是Merge()会有帮助吗?因为它们是新实体。 - Lancelot
merge() 和 persist() 具有不同的语义。这不是一个简单的问题,我强烈建议您阅读一些关于 JPA 的资料。 - cletus

0
如果你有点心机,你可以做的是:
@Entity
public class SomeEntityBatch {

    @Id
    @GeneratedValue
    private int batchID;
    @OneToMany(cascade = {PERSIST, MERGE})
    private List<SomeEntity> entities;

    public SomeEntityBatch(List<SomeEntity> entities) {
        this.entities = entities;
    }

}

List<SomeEntity> entitiesToPersist;
em.persist(new SomeEntityBatch(entitiesToPersist));
// remove the SomeEntityBatch object later

由于级联的存在,这将导致实体在单个操作中被插入。
我怀疑这样做与仅在循环中持久化单个对象相比没有任何实际优势。检查JPA实现发出的SQL并进行基准测试将是有趣的事情。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接