我在源数据库中有一系列表,我需要将它们复制到目标数据库中。此过程具有以下要求:
在查看Spring Batch的核心 ItemReaders 和 ItemWriters 时,我可以选择使用JDBC,JPA,甚至是以Hibernate为中心的实现。我的目标是尽可能利用这些表的现有实体定义,因此JPA或Hibernate将是可能的理想选择。
我从JPA实现开始,希望尽可能地定制它,以便在适用于性能的批量操作中使用Hibernate Stateless Session API。
所以我按如下方式定义了 JpaPagingItemReader (s):
<bean id="jpaPagingItemReader" class="org.springframework.batch.item.database.JpaPagingItemReader" abstract="true">
<property name="entityManagerFactory" ref="srcEntityManagerFactory" />
<property name="transacted" value="false" />
<property name="pageSize" value="15000" />
<property name="saveState" value="false" />
</bean>
<bean id="table1Reader" parent="jpaPagingItemReader" scope="step">
<property name="queryString" value="FROM Item1 i WHERE i.facility = '#{stepExecutionContext['facilityId']}" />
</bean>
<bean id="table2Reader" parent="jpaPagingItemReader" scope="step">
<property name="queryString" value="FROM Item2 i WHERE i.facility = '#{stepExecutionContext['facilityId']}" />
</bean>
每个阅读器都定义了步骤范围,因为我需要能够传递特定的步骤执行参数,以根据从分区程序确定的值来过滤阅读器查询。
我有两个处理器,其定义如下:
<bean id="table1Processor" class="com.comp.batch.transform.Table1Processor" />
<bean id="table2Processor" class="com.comp.batch.transform.Table2Processor" />
这两个类只是实例化Writer必须使用的实体类,然后将读取器项的类属性复制并转换为目标实体类属性,仅此而已(如下所示)。这只是一个例子,但实际的代码遵循这种方法。
public class Table1Processor implements ItemProcessor<Item1, Item1Result> {
@Override
public Item1Result process(Item1 item) {
Item1Result result = new Item1Result();
result.setProperty1(item.getProperty1());
result.setCalculatedValue(2.5d * item.getMeasurement());
return result;
}
}
对于 JpAItemWriter :
<bean id="jpaItemWriter" class="org.springframework.batch.item.database.JpaItemWriter">
<property name="entityManagerFactory" ref="dstEntityManagerFactory" />
</bean>
作业定义为
<bean id="job" job-repository="jobRepository" restartable="false">
<batch:split id="import" task-executor="taskExecutor">
<batch:flow>
<batch:step id="importTable1">
<batch:partition step="importTable1Partitioned" partitioner="facilityPartitioner">
<batch:handler task-executor="taskExecutor" />
</batch>
</batch>
</batch:flow>
<batch:flow>
<batch:step id="importTable2">
<batch:partition step="importTable2Partitioned" partitioner="facilityPartitioner">
<batch:handler task-executor="taskExecutor" />
</batch>
</batch>
</batch:flow>
</batch:split>
</bean>
<batch:step id="importTable1Partitioned">
<batch:tasklet transaction-manager="jtaTransactionManager" task-executor="taskExecutor">
<batch:chunk reader="table1Reader" processor="table1Processor" writer="jpaItemWriter" commit-interval="15000" />
</batch:tasklet>
</batch:step>
<batch:step id="importTable2Partitioned">
<batch:tasklet transaction-manager="jtaTransactionManager" task-executor="taskExecutor">
<batch:chunk reader="table2Reader" processor="table2Processor" writer="jpaItemWriter" commit-interval="15000" />
</batch:tasklet>
</batch:step>
我的第一个问题是,由于我在多个执行流程中使用面向块的处理引入了多个线程,有没有人看到以这种方式使用 JpaPagingItemReader 的任何问题?
其次,我在哪里可以在配置中实现流程指示器更新,以便成功复制的复制行被标记为这样,以便后面的步骤可以进行批量删除?我可能会弄错,但我不相信在相同的处理步骤事务中可以简单地删除源行而不影响分页算法?
最后,是否可以采用 JpaItemWriter 的doWrite()方法并简单地将EntityManager打包到Hibernate的会话中,并使用相同的底层连接打开无状态会话,并执行插入操作而不必烦恼L1缓存和JPA提供程序的其他有状态机制?
如果我的配置或方法严重偏离,并且目前有更理想的方法,请不要犹豫,因为我对Spring Batch的经验仍然很绿。