我需要从EJB Timer中删除表中的数百万行。 问题是计时器的事务超时为90秒,所以我应该将工作分成一口大小的块。
由于我不知道在90秒内可以删除多少行,因此算法应该循环并一次删除一些行,直到时间快到。
问题是: 如何在JPA中优雅地限制要删除的行数? 删除是在时间戳早于特定日期的所有行上进行的。
我想有可能找到第1000个最老的行和DELETE WHERE timestamp <= {1000th-oldest-row.timestamp}
然而,这不是很优雅,我必须到达1000的最后一行来获取时间戳。
其次,如果表在90秒后不干净,计时器应立即再次触发。这很容易解决,但又不是很优雅。
答案 0 :(得分:2)
您仍将面临使用您的解决方案的交易过期问题。
诀窍是在单独的事务中执行每个块,如下面的pesudo代码所示。
@Entity
@NamedQueries ( value = {
@NamedQuery (
name = pagedDeleteExpiredItems
query= DELETE FROM MyTable
WHERE (<table key>) IN (
SELECT <table key> FROM (
SELECT ROWNUM AS row_num, <table key> FROM MyTable
WHERE timestamp <= :currentTime
)
WHERE row_num < :pageSize
)
)
})
public class MyEntity {
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
int doPagedDeleteExpiredItems(Date currentTime, int pageSize) {
Query query = em.createNamedQuery("pagedDeleteExpiredItems");
query.setParameter("currentTime", currentTime);
query.setParameter("pageSize", pageSize);
int deleteCount = query.executeUpdate();
return deleteCount;
}
}
@EJBTimer
public class DeleteExpiredItemsTimer {
@EJB(beanName = "MyEntity")
MyEntity myEntity;
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
void handleTimeout(Timer timer) {
Date currentTime = getCurrentTime()
int pageSize = 100
int deleteCount;
do {
myEntity.doPagedDeleteExpiredItems(currentTime, pageSize);
} while(deleteCount>0);
}
}
答案 1 :(得分:1)
我们有类似的要求,这就是我们如何解决它。我使用的是EJB 3.0。
ServletContextListener
。0ms
计划下一次超时。它,事务已提交,计时器在新事务中再次触发。90sec
中计划下一个超时。 如果有250行,则计时器按顺序触发三次。如果正好要处理100行,那么只有一个小问题,在这种情况下,计时器按顺序触发两次,但第二次触发实际上没有任何处理。但总而言之,它工作正常。
答案 2 :(得分:1)
我在SQL中使用的一个技巧是删除TOP 1000(或100或10000,取决于页面中的平均行数),如下所示:
DELETE top 1000 WHERE timestamp <= @ExpirationDate
重复调用此操作,直到没有删除任何行(使用@@ rowcount检查)或者您的时间不足。这种技术可以在JPA中实现吗?
答案 3 :(得分:0)
通过获取符合条件的行的排序列表并使用setFirstResult(int)与setMaxResults(int)相同来解决问题。通过这种方式,我可以从最旧的项目中获得大约maxCount步骤的项目顺序。
Query expired = dm.createNamedQuery("getExpiredElements");
expired.setParameter("currentTime", getCurrentTime());
expired.setMaxResults(maxCount);
expired.setFirstResult(maxCount);
@SuppressWarnings("unchecked")
List<Item> expiredChunk = (List<Item>) expired.getResultList();
long lastChunkEndTime = expiredChunk.get(0).getEndTime();
Query query = em.createNamedQuery("deleteExpiredItems");
query.setParameter("currentTime", lastChunkEndTime);
int result = query.executeUpdate();
return result >= maxCount;
如果应该再次执行该函数,则该函数返回true(至少)。