如何坚持很多实体(JPA)

时间:2011-04-13 12:39:46

标签: java jpa entitymanager

我需要处理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 个答案:

答案 0 :(得分:12)

JPA API没有为您提供实现此优化的所有选项。根据您想要的速度,您将不得不寻找特定于ORM的选项 - 在您的情况下使用Hibernate。

要检查的事项:

  1. 检查您是否正在使用单笔交易(是的,显然您确定这一点)
  2. 检查您的JPA提供程序(Hibernate)是否正在使用JDBC批处理API(请参阅:hibernate.jdbc.batch_size)
  3. 检查是否可以绕过获取生成的密钥(取决于db / jdbc驱动程序从中获得多少好处 - 参考:hibernate.jdbc.use_getGeneratedKeys)
  4. 检查是否可以绕过级联逻辑(只有最小的性能优势)
  5. 所以在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特定的批量插入调整。

答案 1 :(得分:5)

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

查看EntityManager.getTransaction

答案 2 :(得分:3)

您可以将经典SQL插入语句直接写入数据库。

@see EntityManager.createNativeQuery

答案 3 :(得分:3)

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

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

    dao.save(thingy.toModel())

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

}