diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java index 674153ea17..96f26f4641 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.batch.item; +import java.util.function.Function; + import org.springframework.lang.NonNull; /** @@ -37,6 +38,7 @@ * @author Lucas Ward * @author Taeik Lim * @author Mahmoud Ben Hassine + * @author Stefano Cordio */ @FunctionalInterface public interface ItemWriter { @@ -50,4 +52,27 @@ public interface ItemWriter { */ void write(@NonNull Chunk chunk) throws Exception; + /** + * Adapts an {@code ItemWriter} accepting items of type {@link U} to one accepting + * items of type {@link T} by applying a mapping function to each item before writing. + *

+ * The {@code mapping()} item writers are most useful when used in combination with a + * {@link org.springframework.batch.item.support.CompositeItemWriter}, where the + * mapping function in front of the downstream writer can be a getter of the input + * item or a more complex transformation logic. + *

+ * This adapter mimics the behavior of + * {@link java.util.stream.Collectors#mapping(Function, java.util.stream.Collector)}. + * @param the type of the input items + * @param type of items accepted by downstream item writer + * @param mapper a function to be applied to the input items + * @param downstream an item writer which will accept mapped items + * @return an item writer which applies the mapping function to the input items and + * provides the mapped results to the downstream item writer + * @since 6.0 + */ + static ItemWriter mapping(Function mapper, ItemWriter downstream) { + return chunk -> downstream.write(new Chunk<>(chunk.getItems().stream().map(mapper).toList())); + } + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemWriterIntegrationTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemWriterIntegrationTests.java new file mode 100644 index 0000000000..1acbd557be --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemWriterIntegrationTests.java @@ -0,0 +1,33 @@ +package org.springframework.batch.item; + +import org.junit.jupiter.api.Test; +import org.springframework.batch.item.support.CompositeItemWriter; +import org.springframework.batch.item.support.ListItemWriter; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.batch.item.ItemWriter.mapping; + +class ItemWriterIntegrationTests { + + @Test + void testMappingWithCompositeItemWriter() throws Exception { + ListItemWriter nameItemWriter = new ListItemWriter<>(); + ListItemWriter ageItemWriter = new ListItemWriter<>(); + + record Person(String name, int age) { + } + + ItemWriter personItemWriter = new CompositeItemWriter<>(List.of( // + mapping(Person::name, nameItemWriter), // + mapping(Person::age, ageItemWriter))); + + personItemWriter.write(Chunk.of(new Person("Foo", 42), new Person("Bar", 24))); + personItemWriter.write(Chunk.of(new Person("Baz", 21), new Person("Qux", 12))); + + assertThat(nameItemWriter.getWrittenItems()).containsExactly("Foo", "Bar", "Baz", "Qux"); + assertThat(ageItemWriter.getWrittenItems()).containsExactly(42, 24, 21, 12); + } + +}