19

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

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

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

编辑

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

4

4 回答 4

13

JPA API 并没有为您提供所有选项来使其达到最佳状态。根据您想要执行此操作的速度,您将不得不寻找 ORM 特定选项 - 在您的情况下为 Hibernate。

检查事项:

  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 开销(更少的对象创建/垃圾收集等) - 所以我不会忽略该选项。

So yes, this doesn't answer your question but might help your search for more ORM specific batch insert tweaks.

于 2011-04-14T04:20:21.127 回答
6

我认为一种常见的方法是使用交易。如果您开始一个新事务然后持久化大量对象,那么在您提交事务之前,它们实际上不会被插入到数据库中。如果您要提交大量项目,这可以提高您的效率。

查看EntityManager.getTransaction

于 2011-04-13T13:21:23.840 回答
5

为了让它运行得更快,至少在 Hibernate 中,你会在一定数量的插入之后执行 flush() 和 clear()。我已经为数百万条记录完成了这种方法,并且它有效。它仍然很慢,但比不做要快得多。基本结构是这样的:

int i = 0;
for(MyThingy thingy : lotsOfThingies) {

    dao.save(thingy.toModel())

    if(++i % 20 == 0) {
        dao.flushAndClear();
    }

}
于 2011-04-13T13:29:39.233 回答
4

您可以使用经典的 SQL 插入语句将它们直接写入数据库。

@see EntityManager.createNativeQuery

于 2011-04-13T12:53:19.670 回答