【问题标题】:Can you do bulk delete in one transaction using JPA and Hibernate?您可以使用 JPA 和 Hibernate 在一个事务中进行批量删除吗?
【发布时间】:2014-09-14 20:48:15
【问题描述】:

所以,我一直在努力尝试加快从数据库中删除的过程。我对 Hibernate 比较陌生,我想知道是否可以在一个事务中执行多个操作。

我不太担心交易删除失败,所以回滚所有删除的想法并不困扰我。

如果提供表格描述有帮助,我也可以这样做。我的想法是我必须先删除每个 for 循环中的内容,然后再删除它之外的内容。

我的代码

  private void deleteTrackItems() {
        Session session = m_databaseConnection.getSession();
        Transaction tx = null;
        try {

            final List<TrackItem> trackItems = loadAllTrackItemByTrackID(
                    track.getId(), session);

            // Deletes all Track Items for the given track
            List<Long> trackItemIds = new ArrayList();
            
            for (TrackItem trackItem : trackItems) {
                trackItemIds.add(trackItem.getId());

                // this is test code 189 through 214
                Set<TrackPoint> trackPoints = trackItem.getTrackPoints();

                for (TrackPoint trackPoint : trackPoints) {

                    // load track
                    final List<TrackPointDetail> trackPointDetails = loadAllTrackPointDetailByTrackID(
                            trackPoint.getId(), session);

                    for (TrackPointDetail trackPointDetail : trackPointDetails) {
                        // delete track point details
                        deleteObject(trackPointDetail, session);
                    }

                    // delete track point
                    deleteObject(trackPoint, session);
                }

                // get the track informations
                final Set<TrackInformation> trackInformations = trackItem
                        .getTrackInformations();

                // for each track information
                for (TrackInformation trackInformation : trackInformations) {
                    deleteTrackInformation(trackInformation, session);
                }
                deleteObject(trackItem, session);
            }
        }
        catch (Exception ex) {
            System.out.println(ex);
            tx.rollback();
        }
        finally {
            if (session != null) {
                session.close();
            }
        }
    }
    
/**
 * @param p_objectToDelete The object to delete
 * @param p_session the hibernate session
 * @return Returns the {@link Runnable}
 */
protected void deleteObject(Object p_objectToDelete, final Session p_session) {

    // get the transaction
    final Transaction transaction = p_session.getTransaction();
    try {
        LOGGER.info("Deleting object: " + p_objectToDelete.getClass());
        transaction.begin();
        p_session.delete(p_objectToDelete);
        transaction.commit();
        LOGGER.info("Deleted object: " + p_objectToDelete.getClass());
    }
    catch (Exception exception) {
        transaction.rollback();
        LOGGER.error(exception);
    }
}

因为这些操作中的每一个都在每次删除后执行提交,所以目前我想知道是否可以在更多的批量操作中执行此操作。

我正在考虑做的是将我的提交从我的 deleteObject 函数中拉出来,并将它放在一个对每个对象调用 delete 的 for 循环的末尾。

所以

final Transaction tx = session.getTransaction();
transaction.begin();
try{
   for(Object object : objects){
      session.delete(object);
   }
   tx.commit();
}catch(Exception ex){
   System.out.println(ex);
   tx.rollback();
}

我的另一个批量删除解决方案是使用休眠 SQLQuery,我只需将每个对象的 id 存储在一个列表中,然后在一个查询中以这种方式进行批量删除。但是,我觉得当我在 (?,?,?,?,?) 列表中查询 where id 时,由于序列扫描需要更多时间。

我觉得应该有一种简单的方法可以使用 hibernate session.delete() 进行批量删除。任何想法都非常感谢。

【问题讨论】:

  • 你有时间吗?我非常怀疑这种查询方法可能比上面的 N+1 查询策略花费更长的时间。

标签: java hibernate jpa session transactions


【解决方案1】:

使用 Hibernate 和 JPA 执行批量删除的常用方法与您在上一个解决方案中提出的完全一样 - HQL/JPQL 中的批量删除查询

DELETE FROM MyEntity e WHERE e.id IN (:ids)

这应该是迄今为止最有效的一个,因为 Hibernate 不需要加载和实例化实体。

考虑将批量删除放入它自己的事务方法中,并尽快在其他方法中调用该方法 - 持久性上下文不会随查询结果更新,因此您希望防止持久性上下文包含过时的数据。

【讨论】:

  • Hibernate 仍会将实体加载到内存中并处理它们的生命周期事件(级联等)。
  • 处理级联将违反服务器端删除的 JPA 规范:A delete operation only applies to entities of the specified class and its subclasses. It does not cascade to related entities.,删除也应绕过已加载的实体,但这可能与版本相关,或者可以通过设置来控制休眠。
【解决方案2】:

在使用 JPA 时有多种方法可以执行 BUlk Delete 语句。

使用 JPQL 批量删除

如果要删除其记录的表被映射为实体,则可以使用 JPQL [bulk delete][1] 语句,如下所示:

int deleteCount = entityManager.createQuery("""
    delete from Post
    where 
        status = :status and
        updatedOn <= :validityThreshold
    """)
.setParameter("status", PostStatus.SPAM)
.setParameter(
    "validityThreshold",
    Timestamp.valueOf(
        LocalDateTime.now().minusDays(7)
    )
)
.executeUpdate();

executeUpdate 方法返回一个 int 值,表示受 DML 语句影响的记录数。在我们的例子中,它是被删除的行数。

使用 Criteria API 批量删除

如果要动态构建批量删除语句,则可以使用 JPA CriteriaDelete 查询:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
CriteriaDelete<T> delete = builder.createCriteriaDelete(postModerateClass);

Root<T> root = delete.from(postModerateClass);

int daysValidityThreshold = (Post.class.isAssignableFrom(postModerateClass)) ? 7 : 3;

delete.where(
    builder.and(
        builder.equal(
            root.get("status"), 
            PostStatus.SPAM
        ),
        builder.lessThanOrEqualTo(
            root.get("updatedOn"), 
            Timestamp.valueOf(
                LocalDateTime
                .now()
                .minusDays(daysValidityThreshold)
            )
        )
    )
);

int deleteCount = entityManager.createQuery(delete).executeUpdate();

使用 SQL 批量删除

另一种选择是使用原生 SQL 执行批量删除语句:

int deleteCount = entityManager.createNativeQuery("""
    delete from post
    where 
        status = :status and
        updated_on <= :validityThreshold
    """)
.setParameter("status", PostStatus.SPAM.ordinal())
.setParameter(
    "validityThreshold",
    Timestamp.valueOf(
        LocalDateTime.now().minusDays(7)
    )
)
.executeUpdate();

【讨论】:

    猜你喜欢
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 1970-01-01
    • 2022-12-15
    • 2023-03-25
    • 2011-12-14
    相关资源
    最近更新 更多