如何使用 Spring 无条件批量写入每个读取行的多个表

How to write to multiple tables per read row using Spring Batch with no conditions

我在这个论坛中找到了很多使用多个作者的例子。大多数(如果不是全部)答案都集中在 CompositeItemWriter 和 ClassifierItemWriter 上。

业务需求:从输入文件中读取一行。这一行会包含多个字段(超过50个)需要写到自己的数据库表中(理论上代表不同的classes)。

                               ----- claimwriter(write to claim table)
                              /
                             /
claimlineitemprocessor  -----
                             \
                              \
                               ----- pharmacywriter(write to pharmacy table)

我使用字段集映射器创建了表示索赔行 (ClaimLine) 的对象。大多数字段是对文件中数据的简单映射,但少数需要更改格式或相关字段映射逻辑。

基本项目编写器代码如下所示:

@SuppressWarnings({ "unchecked", "rawtypes" })
@Bean
public ItemWriter<ClaimLine> writer() {
    CompositeItemWriter<ClaimLine> cWriter = new CompositeItemWriter<ClaimLine>();

    JdbcBatchItemWriter claimWriter = new JdbcBatchItemWriter();
    claimWriter.setItemSqlParameterSourceProvider(new ClaimItemSqlParameterSourceProvider());
    claimWriter.setSql( // would like to insert into pharmacy table);
    claimWriter.setDataSource(dataSource);
    claimWriter.afterPropertiesSet();

    JdbcBatchItemWriter pharmacyWriter = new JdbcBatchItemWriter();
    pharmacyWriter.setItemSqlParameterSourceProvider(new PharmacyItemSqlParameterSourceProvider());
    pharmacyWriter.setSql( // would like to insert into pharmacy table);
    pharmacyWriter.setDataSource(dataSource);
    pharmacyWriter.afterPropertiesSet();

    List<ItemWriter<? super ClaimLine>> mWriter = new ArrayList<ItemWriter<? super ClaimLine>>();
    mWriter.add(claimWriter); 
    mWriter.add(pharmacyWriter);
    cWriter.setDelegates(mWriter);

    // other code

    return cWriter;
};

在创建自定义源提供程序时,他们每个人似乎都在期待,因为那是 class 已经映射到输入行并包含我想发送到相应表的值。

这基本上就是我现在无法使用 CompositeItemWriter 的原因,因为我正在尝试将单个对象转换为两个不同的对象。 ClassifierCompositeItemWriter 的工作方式类似于路由器,并将其发送到特定于条件的路径,这不是我想要做的。

作为参考,我尝试用 Spring 集成做一些类似的事情,但也遇到了类似的障碍。

感谢任何帮助。

我相信@Hansjoerg 和@Luca 的评论对这个问题提供了有价值的回答,并且在研究之前和研究期间进行了研究以获得答案。

我能够通过继续使用 ItemSqlParameterSourceProvider 来解决这个问题,代码如下。当我最初探索如何使用这个 class 及其方法时,我的想法是我仍然只是在 ClaimLine class.

上操作

真正发生的是该方法正在接收作者的 class 并且您正在设置您使用 setSQL(String sql).通过使用 ItemSqlParameterSourceProvider,您正在为 put 语句使用 SQL 中的命名参数。以下代码仅显示索赔代码。药房也差不多。

public class ClaimItemSqlParameterSourceProvider implements ItemSqlParameterSourceProvider<ClaimLine> {

    @SuppressWarnings({ "serial"})
    @Override
    public SqlParameterSource createSqlParameterSource(final ClaimLine item) {
        return new MapSqlParameterSource(new HashMap<String, Object>() {
            {
                put("rxclaimid", item.getRxClaimID());
                ...
                // many more
            }
        });
    }
}

自定义项目作者可能也解决了这个问题,但似乎需要更多的编码来支持它。最后,对于这种情况,使用 ItemPreparedStatementSetter 或 ItemSqlParameterSourceProvider 应该没问题。我们选择后者的主要原因是因为参数被明确命名,而不是通过索引值(1、2、3 等)访问参数值并使用“?”在 setSQL 调用中。

可以用chain写多张表,

<int:chain input-channel="processTransactionChannel"
  output-channel="processedItems">
  <int:header-enricher>
   <int:header name="savePayload" expression="payload" />
  </int:header-enricher>

  <int-jpa:updating-outbound-gateway
   auto-startup="true"
   native-query="insert into  TableOne values( :transactionStatus ,bank_Reference_Number = :bankReferenceNumber )"
   entity-manager="entityManager" 
   use-payload-as-parameter-source="false">
   <int-jpa:transactional />
   <int-jpa:parameter name="transactionStatus"
    expression="payload['transactionStatus']" />
   <int-jpa:parameter name="bankReferenceNumber"
    expression="payload['bankReferenceNumber']" />

  </int-jpa:updating-outbound-gateway>

  <int:transformer expression="headers.savePayload" />

  
  <int-jpa:updating-outbound-gateway
   native-query="insert 
       into PARTNER_RESPONSE_DETAILS(PARTNER_ID,BANK_REFERENCE_NUMBER,REQUEST_STRING,RESPONSE_STRING)  
        values (:partnerId,:bankRefNumber,:requestString,:responseString)"
   entity-manager="entityManager">
   <int-jpa:transactional />
   <int-jpa:parameter name="partnerId" expression="payload['partnerId']" />
   <int-jpa:parameter name="bankRefNumber" expression="payload['bankRefNumber']" />
   <int-jpa:parameter name="requestString" expression="payload['requestString']" />
   <int-jpa:parameter name="responseString"
    expression="payload['responseString']" />
   <int-jpa:parameter name="transactionStatus"
    expression="payload['transactionStatus']" />
   <int-jpa:parameter name="bankReferenceNumber"
    expression="payload['bankReferenceNumber']" />

  </int-jpa:updating-outbound-gateway>

  <int:transformer expression="headers.savePayload" />


 </int:chain>

这段代码更新了 2 个表,对我有用。

也许我的回答是你的例外。我也看到了同样的问题,我用 classifierCompositeWriter 来解决它。我在我的项目中定义了我的分类器,如你所见,你可以决定你应该在你的逻辑中使用哪个编写器。例如,在我的逻辑中

if(baseEntity instanceof Product){ 
    return productItemWriter;
}else {
    return otherItemWriter;
}

祝你好运。