我有一个包含两个步骤的批处理作业
处理完所有这些文件后,所有文件的标记都会更新为PROCESSED。即全部或全部更新。
第1步还可以,工作非常顺利。
步骤2基本上是JpaPagingItemReader,pageSize = 4,处理器集(主要是http调用)和JpaItemWriter,commit-interval = 1。 (我知道建议将pageSize等于commit-interval,它就是我所拥有的)它也是一个多线程的步骤,有10个线程完成这项工作。
在第2步说我有两种疑问:
阅读:select * from ENTITY where processed=false order by id
嵌套在两个查询中,用于分页select ... from (select .. where rownum < M) where rownum >= N
写:update ENTITY set .. where id = ID
出于某种原因,当我有足够的实体时,我会臭名昭着:
Ora-01555,快照太旧:回滚段名为&#34;&#34; 太小了
我不知道该错误的确切原因(撤消统计数据没有显示任何不良内容,因此希望DBA很快就能找到罪魁祸首),但与此同时我认为读取查询的内容非常糟糕坏。无论如何,这样的分页查询对于数据库来说很难,但我想当你阅读并同时更新你读到的条目时,它可能会导致这种错误。
我想改变第2步中采用的方法,而不是在页面中阅读。我想将所有的id读入内存只有一次(即给我我需要处理的所有实体的id),然后给每个线程提供该列表中的id。链中的第一个处理器将通过JPA通过id获取实体。这样我就会一个接一个地更新和编写实体,同时我只读了一次我需要的ID。
我的问题是我无法找到这种读卡器的开箱即用解决方案。有什么我可以用的吗?
答案 0 :(得分:0)
嗯,我自己实施了解决方案,它基于this和this。事实上我没有直接使用它们,但我的实现非常接近。
基本上,这就是它的外观(我没有代码,所以使用我的记忆)
SELECT ca.item_id
,ca.FIELD_ID
,ca.attr_val
,ca.upd_dtt
,ca.upd_usr
,mf.[ITEM_NAME]
FROM contract_attr ca
left JOIN mfr mf on ca.attr_val = mf.[ITEM_PK]
我的存储库正在使用JPA,因此它使用public class MyUnprocessedIdReader extends AbstractItemCountingItemStreamItemReader<Long> {
private final Object lock = new Object();
private initialized = false;
private final MyObjectsRepository repo;
private List<Long> ids;
private int current = -1;
public MyUnprocessedIdReader(MyObjectsRepository repo) {
this.repo = repo;
}
public void doOpen() {
synchronized(lock) {
Assert.state(!initialized, "Cannot open an already opened ItemReader, call close first");
this.initialized = true;
this.ids = ImmutableList.copyOf(repo.findAllUnprocessed());
}
}
public Long doRead() {
synchronized(lock) {
if (ids == null || !initialized) {
throw new IllegalStateException("Have you opened the reader?");
}
++current;
if (current < ids.size()) {
return ids.get(current);
} else {
return null;
}
}
}
public void doClose() {
synchronized(lock) {
this.initialized = false;
this.current = -1;
this.ids = null;
}
}
}
此外,我还在链中添加了一个处理器:
entityManager.createQuery("select obj.id from Objects where obj.processed = false order by obj.id asc", Long.class).executeSelect()
有人可能会说它的可扩展性低于使用游标,而且读取方法也存在争议,但是这是一个非常简单的解决方案,可以很好地完成其工作,直到未处理的ID数量太大。处理线程也花费大量时间来调用外部REST服务,因此读取时的争用不会成为瓶颈。
P.S。稍后我会更新是否用ORA-01555解决了这个问题。