问题
I want to create a batch using Spring batch to read from an ini file and save the data in database but when I chekced the org.springframework.batch.item.file.FlatFileItemReader
class I didn't find a way to parse my data from the ini file , I tried to combine the ini4j
API with the spring batch but no result
my ini file :
[Cat]
a=1
b= 2
c= 3
d= 4
e= 5
f= 6
[Cat2]
a=11
b= 21
c= 31
d= 41
e= 51
f= 61
回答1:
What you can do is define your own ItemStreamReader
that wraps a delegate ItemStreamReader
, which is just a FlatFileItemReader
that uses a PatternMatchingCompositeLineMapper
as the line mapper. In your ItemStreamReader
, loop to read lines from your delegate and if the line is an instance of a Property
domain object then add it to a list in a Section
domain object. What the PatternMatchingCompositeLineMapper
allows you do do is check the line for a pattern match, and pass it to the right tokenizer and fieldSetMapper for the job.
Doing it this way will allow you to read multiple lines into one Section
domain object that holds a List<Property>
.
public class Section {
private String name;
private List<Property> properties;
// getters and setters
@Override
public String toString() {
StringBuilder sb = new StringBuilder(name);
for (Property prop: properties) {
sb.append("," + prop.getKey() + "=" + prop.getValue());
}
return sb.toString();
}
}
public class Property {
private String key;
private String value;
// getters and setters
}
For you custom ItemStreamReader
you would do this. You can see that the reading is delegated to another reader, which you will define later
import java.util.ArrayList;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
public class IniFileItemReader implements ItemStreamReader<Object> {
private Object curItem = null;
private ItemStreamReader<Object> delegate;
@Override
public Object read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
if (curItem == null) {
curItem = (Section) delegate.read();
}
Section section = (Section) curItem;
curItem = null;
if (section != null) {
section.setProperties(new ArrayList<Property>());
while (peek() instanceof Property) {
section.getProperties().add((Property) curItem);
curItem = null;
}
}
return section;
}
private Object peek() throws Exception {
if (curItem == null) {
curItem = delegate.read();
}
return curItem;
}
public void setDelegate(ItemStreamReader<Object> delegate) {
this.delegate = delegate;
}
@Override
public void close() throws ItemStreamException {
delegate.close();
}
@Override
public void open(ExecutionContext arg0) throws ItemStreamException {
delegate.open(arg0);
}
@Override
public void update(ExecutionContext arg0) throws ItemStreamException {
delegate.update(arg0);
}
}
Then in your config you define the deleagte reader with the PatternMatchingCompositeLineMapper
<bean id="inputFile" class="org.springframework.core.io.FileSystemResource"
scope="step">
<constructor-arg value="#{jobParameters[inputFile]}"></constructor-arg>
</bean>
<bean id="sectionFileReader"
class="com.underdogdevs.springbatch.reader.IniFileItemReader">
<property name="delegate" ref="trueSectionFileReader"></property>
</bean>
<bean id="trueSectionFileReader"
class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="lineMapper">
<bean
class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper">
<property name="tokenizers">
<map>
<entry key="[*" value-ref="sectionLineTokenizer">
</entry>
<entry key="*" value-ref="propertyLineTokenizer"></entry>
</map>
</property>
<property name="fieldSetMappers">
<map>
<entry key="[*" value-ref="sectionFieldSetMapper">
</entry>
<entry key="*" value-ref="propertyFieldSetMapper">
</entry>
</map>
</property>
</bean>
</property>
<property name="resource" ref="inputFile"></property>
</bean>
<bean id="sectionLineTokenizer"
class="com.underdogdevs.springbatch.tokenizer.SectionLineTokenizer">
</bean>
<bean id="sectionFieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="section"></property>
</bean>
<bean id="section" class="com.underdogdevs.springbatch.domain.Section"
scope="prototype">
</bean>
<bean id="propertyLineTokenizer"
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="delimiter" value="="></property>
<property name="names" value="key,value"></property>
</bean>
<bean id="propertyFieldSetMapper"
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="property"></property>
</bean>
<bean id="property" class="com.underdogdevs.springbatch.domain.Property"
scope="prototype">
</bean>
You see that I also used a custom LineTozenizer
. I probably could've just used a DelimitedLineTokenizer
, but by the time I realized it, I had already defined the class
import org.springframework.batch.item.file.transform.DefaultFieldSetFactory;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.batch.item.file.transform.FieldSetFactory;
import org.springframework.batch.item.file.transform.LineTokenizer;
public class SectionLineTokenizer implements LineTokenizer {
private final String nameField = "name";
private final FieldSetFactory fieldSetFactory = new DefaultFieldSetFactory();
@Override
public FieldSet tokenize(String line) {
String name = line.replaceAll("\\[", "").replaceAll("\\]", "").trim();
return fieldSetFactory.create(new String[] { name },
new String[] { nameField });
}
}
Using the following writer and job
<bean id="outputFile"
class="org.springframework.core.io.FileSystemResource" scope="step">
<constructor-arg value="#{jobParameters[outputFile]}"></constructor-arg>
</bean>
<bean id="outputFileWriter"
class="org.springframework.batch.item.file.FlatFileItemWriter">
<property name="resource" ref="outputFile"></property>
<property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.PassThroughLineAggregator">
</bean>
</property>
</bean>
<batch:step id="outputStep">
<batch:tasklet>
<batch:chunk commit-interval="10" reader="sectionFileReader"
writer="outputFileWriter">
<batch:streams>
<batch:stream ref="sectionFileReader" />
<batch:stream ref="trueSectionFileReader" />
</batch:streams>
</batch:chunk>
</batch:tasklet>
</batch:step>
<batch:job id="iniJob">
<batch:step id="step1" parent="outputStep"></batch:step>
</batch:job>
And using this ini file
[Cat]
a=1
b=2
c=3
d=4
e=5
f=6
[Cat2]
a=11
b=21
c=31
d=41
e=51
f=61
I get the following output, which is the format in my toString()
of the Section
class
Cat,a=1,b=2,c=3,d=4,e=5,f=6
Cat2,a=11,b=21,c=31,d=41,e=51,f=61
来源:https://stackoverflow.com/questions/24243596/how-to-read-ini-file-in-spring-batch-key-value