如何持久化大量实体 (JPA)

2022-09-02 12:54:42

我需要处理一个CSV文件,并为每个记录(行)保留一个实体。现在,我这样做:

while ((line = reader.readNext()) != null) {
    Entity entity = createEntityObject(line);
    entityManager.save(entity);
    i++;
}

其中,该方法基本上只是一个调用。CSV 文件中大约有 20,000 个实体(行)。这是一种有效的方法吗?它似乎很慢。使用会更好吗?此解决方案是否有任何缺陷?save(Entity)EntityManager.merge()EntityManager.persist()

编辑

这是一个漫长的过程(超过400s),我尝试了这两种解决方案,使用和.两者完成所需的时间大致相同(459秒对443秒)。问题是,像这样逐个保存实体是否是最佳的。据我所知,Hibernate(这是我的JPA提供程序)确实实现了一些缓存/刷新功能,所以我不必担心这一点。persistmerge


答案 1

JPA API 并没有为您提供实现最佳状态的所有选项。根据您要执行此操作的速度,您将不得不寻找ORM特定的选项 - 在您的情况下休眠。

要检查的事项:

  1. 检查您是否正在使用单个交易(是的,显然您确定这一点)
  2. 检查您的 JPA 提供程序 (Hibernate) 是否正在使用 JDBC 批处理 API(参考:hibernate.jdbc.batch_size)
  3. 检查您是否可以绕过获取生成的密钥(取决于db / jdbc驱动程序从中获得多少好处 - 参考:hibernate.jdbc.use_getGeneratedKeys)
  4. 检查是否可以绕过级联逻辑(仅从中获得最小的性能优势)

因此,在Ebean ORM中,这将是:

    EbeanServer server = Ebean.getServer(null);

    Transaction transaction = server.beginTransaction();
    try {
        // Use JDBC batch API with a batch size of 100
        transaction.setBatchSize(100);
        // Don't bother getting generated keys
        transaction.setBatchGetGeneratedKeys(false);
        // Skip cascading persist 
        transaction.setPersistCascade(false);

        // persist your beans ...
        Iterator<YourEntity> it = null; // obviously should not be null 
        while (it.hasNext()) {
            YourEntity yourEntity = it.next();
            server.save(yourEntity);
        }

        transaction.commit();
    } finally {
        transaction.end();
    }

哦,如果你通过原始JDBC执行此操作,你可以跳过ORM开销(更少的对象创建/垃圾回收等) - 所以我不会忽略这个选项。

所以,是的,这并不能回答你的问题,但可能有助于你搜索更多ORM特定的批量插入调整。


答案 2

我认为一种常见的方法是交易。如果您开始一个新事务,然后保留大量对象,则在提交事务之前,它们实际上不会插入到数据库中。如果您要提交大量项目,这可以为您带来一些效率。

查看 EntityManager.getTransaction


推荐