In this post under Spring Batch, I will explain with example the purpose of CompositeItemWriter and how to use it.
In all the previous post’s examples under Spring Batch, I have used only one writer bean. The writer bean’s job was to write the items read to a file as shown below
<bean id="writer" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:FileOutput.txt"/>
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="id,name,status,salary"/>
</bean>
</property>
</bean>
</property>
</bean>
As you can see from the above xml code snippet, one instance of FlatFileItemWriter can be used to output data to one file or one destination.
What if you want to output the same data to multiple files or destination. In other words having multiple copies of data. This is where CompositeItemWriter comes into
picture.
CompositeItemWriter contains a collection of writers and it passes the data to the collection of writers one after another in the order they are created.
Below is the xml snippet showing how to create CompositeItemWriter bean
1 <bean id="writer1" class="org.springframework.batch.item.file.FlatFileItemWriter">
2 <property name="resource" value="file:FileOutput1.txt"/>
3 <property name="lineAggregator">
4 <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
5 <property name="fieldExtractor">
6 <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
7 <property name="names" value="id,name,status,salary"/>
8 </bean>
9 </property>
10 </bean>
11 </property>
12 </bean>
13
14 <bean id="writer2" class="org.springframework.batch.item.file.FlatFileItemWriter">
15 <property name="resource" value="file:FileOutput2.txt"/>
16 <property name="lineAggregator">
17 <bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
18 <property name="fieldExtractor">
19 <bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
20 <property name="names" value="id,name,status,salary"/>
21 </bean>
22 </property>
23 </bean>
24 </property>
25 </bean>
26
27 <bean id="compositeItemWriter" class="org.springframework.batch.item.support.CompositeItemWriter">
28 <property name="delegates">
29 <list>
30 <ref bean="writer1"/>
31 <ref bean="writer2"/>
32 </list>
33 </property>
34 </bean>
In the above xml code snippet, from line 1 to 12, I have created a new Writer bean named “writer1” which will output data to FileOutput1.txt file.
From line 14 to 25, I have created another Writer bean named “writer2” which will output the same data received by “writer1” to FileOutput2.txt file.
From line 27 to 34, I have created CompositeItemWriter bean named “compositeItemWriter”. It’s “delegates” property is of type List which contains “writer1” and “writer2”.
The data read is sent to each Writer added in “delegates” property of CompositeItemWriter bean.
Next we integrate the compositeItemWriter bean to Spring Batch job as shown in the below xml snippet
1 <batch:job id="importEmployees">
2 <batch:step id="readWriteEmployees">
3 <batch:tasklet>
4 <batch:chunk reader="reader" writer="compositeItemWriter" commit-interval="50"/>
5 </batch:tasklet>
6 </batch:step>
7 </batch:job>
At line 4, we set the “writer” attribute to refer to “compositeItemWriter” bean.
I hope you guys understand the purpose of CompositeItemWriter.
Below is the complete xml configuration for your reference
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd">
<bean id="employee" class="package33.Employee" scope="prototype"/>
<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="file:FileInput.txt"/>
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="id,name,status,salary"/>
</bean>
</property>
<property name="fieldSetMapper">
<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="employee"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="writer1" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:FileOutput1.txt"/>
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="id,name,status,salary"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="writer2" class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" value="file:FileOutput2.txt"/>
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="id,name,status,salary"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="compositeItemWriter" class="org.springframework.batch.item.support.CompositeItemWriter">
<property name="delegates">
<list>
<ref bean="writer1"/>
<ref bean="writer2"/>
</list>
</property>
</bean>
<batch:job id="importEmployees">
<batch:step id="readWriteEmployees">
<batch:tasklet>
<batch:chunk reader="reader" writer="compositeItemWriter" commit-interval="50"/>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
</beans>