我正在使用Spring Batch 3.0.7,EclipseLink 2.6.4和Oracle 11/12。
我在数据库中有一对多的关系。批处理读取器读取处理器读取多方(子)的一侧(父级)。 我确信它们都是由不同的应用程序以事务方式插入的。
批量阅读器派生自JdbcCursorItemReader
,只需设置rowMapper
和preparedStatementSetter
。
处理器是使用Spring Data JpaRepository的ItemProcessor
,并将数据添加到父级。
存储库有@Transactional(readOnly = true)
。
读者似乎始终使用相同的数据库会话,并且处理器始终使用不同的数据库会话。
通过此设置,我不时会遇到ORA-01555
(快照太旧)错误。
但是甚至有一个更大的问题:
如果作业运行之间有新的父子数据,则读者会找到父级,但处理器看不到任何子级。
只有第一个父/子插入工作(以某种方式处理器保留在Oracle数据的修复快照上)。
我为解决这个问题做了些什么:
在每个作业运行中,我关闭Spring Context然后重新创建它。
这解决了两个问题(ORA-01555
错误,并且没有看到处理器更新的数据。)
重新创建Spring Context的目的是获得一个新的数据库会话。我不知道这样做的方法比较简单。
我认为没有必要重新创建Context但我找不到这种行为的原因。
更新 在这里,您可以找到代码的骨架 https://github.com/th-e/SpringBatchDataPump
答案 0 :(得分:0)
您需要了解该事务包装read-process-write
序列。因此,如果之间发生其他一些数据库写入 - 此事务将失败。
读者似乎总是使用相同的数据库会话和 处理器总是使用不同的数据库会话。
这是一个奇怪的陈述,如果您确定这是真的,那么您必须再次检查您的配置。情况应该不是这样。
您可以检查您的交易属性。
<batch:tasklet>
<batch:transaction-attributes isolation="READ_COMMITTED" propagation="REQUIRES_NEW" timeout="200"/>
<batch:chunk reader="myItemReader" writer="myItemWriter" commit-interval="20"/>
</batch:tasklet>
例如,如果您不介意脏读 - 您可以将隔离更改为READ_UNCOMMITTED
另一种解决方案:您可以配置弹簧批提供的重试机制:
<batch:tasklet>
<batch:chunk reader="myItemReader" writer="myItemWriter" commit-interval="20" retry-limit="15">
<batch:retryable-exception-classes>
<batch:include class="com.stackoverflow.MyRetryableException" />
</batch:retryable-exception-classes>
</batch:chunk>
</batch:tasklet>