spring-batch icon indicating copy to clipboard operation
spring-batch copied to clipboard

Type safe alternative for `PropertyExtractingDelegatingItemWriter`

Open scordio opened this issue 11 months ago • 2 comments

I've recently encountered the need for an adapter to use an existing ItemWriter<SimpleType> in a step that works with ComplexType items, where a ComplexType instance encapsulates a SimpleType one (imagine a ComplexType::getSimpleType getter available).

While PropertyExtractingDelegatingItemWriter seems to be a solution, I didn't find it type-safe due to its underlying dependency on the MethodInvoker. I also looked at #4672, but they don't seem to help my use case.

Taking inspiration from java.util.stream.Collectors.mapping(Function, Collector), I came up with a custom ItemWriter adapter for this purpose, statically imported as mapping(Function, ItemWriter), which allows writing a step definition like the following:

factory.get("step")
  .<ComplexType, ComplexType>chunk(10)
  .reader(/* an ItemReader<ComplexType> */)
  .writer(mapping(ComplexType::getSimpleType, new SimpleTypeItemWriter()))
  .build()

I heavily simplified the example to give a feeling about the core idea. Arguably, that example can also be solved with a processor:

factory.get("step")
  .<ComplexType, SimpleType>chunk(10)
  .reader(/* an ItemReader<ComplexType> */)
  .processor(/* an ItemProcess<ComplexType, SimpleType> */)
  .writer(new SimpleTypeItemWriter())
  .build()

However, my real-life use case also involves a CompositeItemWriter where each delegate expects items of a different type, all encapsulated by ComplexType. Something like the following:

factory.get("step")
  .<ComplexType, ComplexType>chunk(10)
  .reader(/* an ItemReader<ComplexType> */)
  .writer(new CompositeItemWriterBuilder<>()
    .delegates(
      mapping(ComplexType::getSimpleType, new SimpleTypeItemWriter())),
      mapping(ComplexType::getAnotherType, new AnotherTypeItemWriter())),
      mapping(ComplexType::getOneMoreType, new OneMoreTypeItemWriter())),
      /* etc */
    )
    .build())
  .build()

Would Spring Batch be interested in introducing such an adapter, maybe as a static factory method under ItemWriter for better discoverability?

If yes, I would be happy to provide a PR for it.

scordio avatar Dec 17 '24 12:12 scordio

This can also be relevant for ItemReader and ItemProcessor.

scordio avatar Jun 13 '25 13:06 scordio

While PropertyExtractingDelegatingItemWriter seems to be a solution

After some experimentation, PropertyExtractingDelegatingItemWriter is not a suitable solution for my use case, as it attempts to invoke the delegate method (in this case, write(Chunk)) with each mapped value, rather than building up a new chunk with the mapped values.

This is just to confirm that I was unable to find an out-of-the-box solution for my use case.

scordio avatar Jun 15 '25 13:06 scordio

This can also be relevant for ItemReader and ItemProcessor.

After checking better, CompositeItemReader and CompositeItemProcessor have a different delegation pattern compared to CompositeItemWriter, and I don't see the usefulness of adding such an adapter to ItemReader or ItemProcessor.

scordio avatar Jul 19 '25 14:07 scordio

Hi @scordio , this is interesting! Thank you for the feature request and the PR!

However, my real-life use case also involves a CompositeItemWriter where each delegate expects items of a different type, all encapsulated by ComplexType.

At first, I thought this is a classification case (ie achievable with a ClassifierCompositeItemWriter), but it isn't. It's actually a mapping use case.

This is a welcome addition, I will add a comment on the PR.

fmbenhassine avatar Oct 07 '25 10:10 fmbenhassine

Resolved with #4890 . Many thanks for this addition 🙏

fmbenhassine avatar Oct 20 '25 08:10 fmbenhassine