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();
}
}
当进程运行时,一些实体似乎正在分离,导致两种症状:
- 尝试获取惰性数据时出现异常:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
。
- 当不需要惰性加载时 - 尽管
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()
或者是有更好的解决方案吗?
问题是您正在清除包含所有托管实体的整个会话,从而使它们分离。
如果您只处理部分数据,请确保只获取它们,然后您可以轻松清除整个会话并获取下一批数据并进行相同的计算。
我有一个批处理过程,它正在为一组实体重新计算数据。实体列表由休眠从数据库中获取:
@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();
}
}
当进程运行时,一些实体似乎正在分离,导致两种症状:
- 尝试获取惰性数据时出现异常:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
。 - 当不需要惰性加载时 - 尽管
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()
或者是有更好的解决方案吗?
问题是您正在清除包含所有托管实体的整个会话,从而使它们分离。
如果您只处理部分数据,请确保只获取它们,然后您可以轻松清除整个会话并获取下一批数据并进行相同的计算。