Hibernate 批量更新 - 实体不更新

Hibernate Batch Update - Entities are not updated

我有一个批处理过程,它正在为一组实体重新计算数据。实体列表由休眠从数据库中获取:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recalculateUserData(Long userId){
    List<Entity> data = repository.getAllPendingRecalculation(userId);

    List<Entity> recalculated = new LinkedList();

    for (Entity entity : data){
        recalculateEntity(entity, recalculated);
        recalculated.add(entity);
        flushIfNeeded(recalculated); //every 10 records
    }
}

private void recalculateEntity(Entity entity, List<Entity> recalculated){
    //do logic
}

private void flush(){
    getSession().flush();
    getSession().clear();
}

private void flushIfNeeded(List<Entity> data) {
    int flushSize = 10
    if (data.size() % flushSize == 0){
        flush();
    }
}

当进程运行时,一些实体似乎正在分离,导致两种症状:

  1. 尝试获取惰性数据时出现异常:org.hibernate.LazyInitializationException - could not initialize proxy - no Session
  2. 当不需要惰性加载时 - 尽管 flushIfNeeded(...) 工作正常,但数据库中仅更新前 10 条记录。

第一次尝试时,我尝试通过在 recalculateEntity(...) 中调用 session#refresh(entity) 来解决它 - 这解决了惰性初始化问题,但问题 2 仍然出现:

private void recalculateEntity(Entity entity){
    getSession().refresh(entity);
    //do logic
}

由于这还没有解决我考虑使用 attach(entity) 而不是 refresh(entity) 的问题:

private void recalculateEntity(Entity entity){
    getSession().attach(entity);
    //do logic
}

这似乎可行,但我的问题是:为什么这些实体首先会分离?

(我使用的是 Hibernate 3.6.10)


更新

正如@galovics 所解释的:

The problem is that you are clearing the whole session which holds all your managed entities, making them detached.

Hibernate batch processing documentation 表示应该使用 ScrollableResults 执行批量更新(这解决了这些问题),但在这种情况下,我必须在处理之前获取所有数据,因为实体计算可能取决于已经计算的实体。例如,计算 Entity#3 可能需要为 Entity#1 和 Entity#2 计算数据。

对于这样的情况,使用Session#attach(entity)会更好吗(如代码所示),使用Session#flush()而不使用Session#clear()或者是有更好的解决方案吗?

问题是您正在清除包含所有托管实体的整个会话,从而使它们分离。

如果您只处理部分数据,请确保只获取它们,然后您可以轻松清除整个会话并获取下一批数据并进行相同的计算。

Article on LazyInitializationException 澄清一下。