2

给定以下批量删除行的代码。

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();

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

4

2 回答 2

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(...)原始批量删除时使用的单个语句。

于 2014-05-26T15:55:54.183 回答
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);
}

您需要做的是启用 JDBC 批量更新,如下所示:

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

但是,实体级批处理示例需要一些改进。正如我在本文中解释的那样,如果您处理数百个实体,使用 JPA 和 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 语句失败而回滚所有内容?

ACID中的原子性非常适合只接触一小部分数据的 OLTP。对于 OLAP 或批量处理,最好改为使用批量更新和删除。

于 2018-01-09T09:55:04.633 回答