使用条件API在JPA 2.1中批量删除

时间:2014-05-24 15:16:06

标签: hibernate jpa eclipselink criteria jpa-2.1

给出以下代码,删除批处理中的行。

List<City> list = ...
int i=0;

for(City city:list)
{
    if(++i%49==0)
    {
        entityManager.flush();
    }

    entityManager.remove(city);
}

JPA 2.1条件API提供CriteriaDelete来执行批量删除。因此,以下代码执行DELETE FROM city WHERE id IN(...)查询。

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaDelete<City> criteriaDelete = criteriaBuilder.createCriteriaDelete(City.class);

Root<City> root = criteriaDelete.from(City.class);
criteriaDelete.where(root.in(list));
entityManager.createQuery(criteriaDelete).executeUpdate();

但这不应该等同于第一种情况。第一种情况相当于什么?它应该批量执行删除。

2 个答案:

答案 0 :(得分:1)

等效的是执行查询以删除for循环中的单个实体,如:

for(City city:list)
{
    if(++i%49==0)
    {
        entityManager.flush();
    }

    CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
    CriteriaDelete<City> criteriaDelete = criteriaBuilder.createCriteriaDelete(City.class);

    Root<City> root = criteriaDelete.from(City.class);
    criteriaDelete.where(root.equal(city));
    entityManager.createQuery(criteriaDelete).executeUpdate();
}

这50个语句中的每一个都可能不会被放入同一批次 - 这取决于您的JPA提供程序是否支持批处理。循环似乎效率较低,因为您最终使用X语句,而不是使用原始批量删除执行的单个DELETE FROM city WHERE id IN(...)

答案 1 :(得分:0)

在您的情况下,可能Criteria API 2.1 Bulk Delete效果更好:

CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaDelete<City> criteriaDelete = criteriaBuilder.createCriteriaDelete(City.class);

Root<City> root = criteriaDelete.from(City.class);
criteriaDelete.where(root.in(list));
entityManager.createQuery(criteriaDelete).executeUpdate();

对于批量删除,您根本不需要Criteria API。实际上,您可以保持代码不变:

List<City> list = ...
int i=0;

for(City city:list {
    if(++i%49==0) {
        entityManager.flush();
    }
    entityManager.remove(city);
}

您需要做的是enable JDBC batch updates这样:

<property name="hibernate.jdbc.batch_size" value="50"/>

但是,实体级批处理示例需要一些改进。正如我在this articlethe best way to do batch processing with JPA and Hibernate中解释的那样,如果您处理数百个实体,那么在批处理期间提交事务也是一个好主意:

int entityCount = 50;
int batchSize = 25;

EntityManager entityManager = null;
EntityTransaction transaction = null;

try {
    entityManager = entityManagerFactory()
        .createEntityManager();

    transaction = entityManager.getTransaction();
    transaction.begin();

    for ( int i = 0; i < entityCount; ++i ) {
        if ( i > 0 && i % batchSize == 0 ) {
            entityManager.flush();
            entityManager.clear();

            transaction.commit();
            transaction.begin();
        }

        Post post = new Post( 
            String.format( "Post %d", i + 1 ) 
        );
        entityManager.persist( post );
    }

    transaction.commit();
} catch (RuntimeException e) {
    if ( transaction != null && 
         transaction.isActive()) {
        transaction.rollback();
    }
    throw e;
} finally {
    if (entityManager != null) {
        entityManager.close();
    }
}

这样,您将避免长时间运行的事务,这可能会损害基于2PL和MVCC的关系数据库中的性能。

更多,考虑到你需要更新10K条目并删除3k行,你真的想要回滚所有内容只是因为最后一个SQL语句失败了吗?

atomicity in ACID非常适合OLTP,您只需触摸一小部分数据。对于OLAP或批量处理,最好使用批量更新和删除。