我有一个Spring Batch作业,它读取XML文件并将其写入数据库。直到今天,一切都运转良好。但是,现在该作业将具有null属性的ProgramElement对象返回给处理器。我正在使用StaxEventItemReader和自定义分区程序。知道为什么读者会返回null属性吗?
编辑:我应该在将Java从版本102更新到112之后发生这种情况。
工作环境文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:task="http://www.springframework.org/schema/task" xmlns:file="http://www.springframework.org/schema/integration/file"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<import resource="data-source-context.xml" />
<import resource="job-launcher-context.xml" />
<batch:job id="spa_r2_data_load">
<batch:step id="loadData">
<batch:partition step="loadDataStep" partitioner="filePartitioner" />
</batch:step>
</batch:job>
<batch:step id="loadDataStep">
<batch:tasklet>
<batch:chunk reader="xmlItemReader" processor="peProcessor"
writer="dbWriter" commit-interval="1" />
</batch:tasklet>
<batch:listeners>
<batch:listener ref="listener"/>
</batch:listeners>
</batch:step>
<bean id="listener" class="com.mason.seor.listener.FileListener" scope="step">
<property name="fileName" value="#{stepExecutionContext['file.location']}"/>
</bean>
<bean id="filePartitioner" class="com.mason.seor.partitioner.FilePartitioner">
<property name="fileLocationsProp" value="${file.location}" />
<property name="fileNamesProp" value="${file.names:#{null}}" />
</bean>
<bean id="xmlItemReader" class="org.springframework.batch.item.xml.StaxEventItemReader" scope="step">
<property name="resource" value="#{stepExecutionContext['file.location']}" />
<property name="fragmentRootElementName" value="ProgramElement" />
<property name="unmarshaller">
<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>com.mason.seor.model.r2.ProgramElement</value>
<value>com.mason.seor.model.r2.ProgramElementFunding</value>
</list>
</property>
</bean>
</property>
</bean>
<bean id="peProcessor" class="com.mason.seor.processor.R2Processor"
scope="step"/>
<bean id="dbWriter"
class="org.springframework.batch.item.database.HibernateItemWriter">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
文件分区程序:
package com.mason.seor.partitioner;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.item.ExecutionContext;
public class FilePartitioner implements Partitioner {
String fileLocationsProp;
String fileNamesProp;
@Override
public Map<String, ExecutionContext> partition(int arg0){
Map<String, ExecutionContext> partitionMap = new HashMap<>();
String[] fileNames;
if (fileNamesProp == null) {
File filesDir = new File(fileLocationsProp.replace("file:", ""));
if (!filesDir.isDirectory()) {
throw new IllegalStateException("HEY! " + fileLocationsProp + " is not a directory!!!!!!!");
}
// create new filename filter
FilenameFilter fileNameFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if(name.lastIndexOf('.')>0)
{
// get last index for '.' char
int lastIndex = name.lastIndexOf('.');
// get extension
String str = name.substring(lastIndex);
// match path name extension
if(str.equals(".xml"))
{
return true;
}
}
return false;
}
};
fileNames = filesDir.list(fileNameFilter);
} else {
fileNames = fileNamesProp.split(",");
}
int counter = 0;
for (String fileName : fileNames) {
//trimXmlTags(fileLocationsProp,fileName);
ExecutionContext context = new ExecutionContext();
context.put("file.location", fileLocationsProp + fileName);
context.put("file.name", fileName);
counter++;
partitionMap.put("file" + counter, context);
}
return partitionMap;
}
private void trimXmlTags(String fileLocation, String fileName){
File xml = new File(fileLocation.replace("file:", "")+fileName);
String charset = "UTF-8";
// need to get rid of these prefixes for the XML tags and attributes
String[] delete = {"r2:",":r2"};
String[] replace = {"",""};
// we don't need these lines
String throwOut = "jb:";
try {
File temp = File.createTempFile("temp", ".xml", xml.getParentFile());
//open file for reading
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(xml), charset));
//open temp file for writing
PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(temp), charset));
for (String line; (line = reader.readLine()) != null;) {
line = StringUtils.replaceEach(line, delete, replace);
if(!line.contains(throwOut)){
writer.println(line);
}
}
reader.close();
writer.close();
xml.delete();
temp.renameTo(xml);
} catch (IOException e) {
System.out.println("file location not found: "+xml.getParentFile());
System.exit(1);
}
}
public void setFileLocationsProp(String fileLocationsProp) {
this.fileLocationsProp = fileLocationsProp;
}
public void setFileNamesProp(String fileNamesProp) {
this.fileNamesProp = fileNamesProp;
}
}
ProgramElement类:
package com.mason.seor.model.r2;
import java.util.Date;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.mason.seor.data.SubmissionDateConverter;
@XmlRootElement(name = "ProgramElement", namespace="http://www.dtic.mil/comptroller/xml/schema/022009/r2")
public class ProgramElement {
String monetaryUnit;
String programElementNumber;
String programElementTitle;
Integer r1LineNumber;
Integer budgetYear;
String budgetCycle;
Date submissionDate;
String serviceAgencyName;
String AppropriationCode;
String appropriationName;
Integer budgetActivityNumber;
String budgetActivityTitle;
ProgramElementFunding programElementFunding;
@XmlAttribute(name = "MonetaryUnit")
public String getMonetaryUnit() {
return monetaryUnit;
}
public void setMonetaryUnit(String monetaryUnit) {
this.monetaryUnit = monetaryUnit;
}
@XmlElement(name = "ProgramElementNumber")
public String getProgramElementNumber() {
return programElementNumber;
}
public void setProgramElementNumber(String programelementNumber) {
this.programElementNumber = programelementNumber;
}
@XmlElement(name = "ProgramElementTitle")
public String getProgramElementTitle() {
return programElementTitle;
}
public void setProgramElementTitle(String programElementTitle) {
this.programElementTitle = programElementTitle;
}
@XmlElement(name = "R1LineNumber")
public Integer getR1LineNumber() {
return r1LineNumber;
}
public void setR1LineNumber(Integer r1LineNumber) {
this.r1LineNumber = r1LineNumber;
}
@XmlElement(name = "BudgetYear")
public Integer getBudgetYear() {
return budgetYear;
}
public void setBudgetYear(Integer budgetYear) {
this.budgetYear = budgetYear;
}
@XmlElement(name = "BudgetCycle")
public String getBudgetCycle() {
return budgetCycle;
}
public void setBudgetCycle(String budgetCycle) {
this.budgetCycle = budgetCycle;
}
@XmlJavaTypeAdapter(type = Date.class, value = SubmissionDateConverter.class)
@XmlElement(name = "SubmissionDate")
public Date getSubmissionDate() {
return submissionDate;
}
public void setSubmissionDate(Date submissionDate) {
this.submissionDate = submissionDate;
}
@XmlElement(name = "ServiceAgencyName")
public String getServiceAgencyName() {
return serviceAgencyName;
}
public void setServiceAgencyName(String serviceAgencyName) {
this.serviceAgencyName = serviceAgencyName;
}
@XmlElement(name = "AppropriationCode")
public String getAppropriationCode() {
return AppropriationCode;
}
public void setAppropriationCode(String appropriationCode) {
AppropriationCode = appropriationCode;
}
@XmlElement(name = "AppropriationName")
public String getAppropriationName() {
return appropriationName;
}
public void setAppropriationName(String appropriationName) {
this.appropriationName = appropriationName;
}
@XmlElement(name = "BudgetActivityNumber")
public Integer getBudgetActivityNumber() {
return budgetActivityNumber;
}
public void setBudgetActivityNumber(Integer budgetActivityNumber) {
this.budgetActivityNumber = budgetActivityNumber;
}
@XmlElement(name = "BudgetActivityTitle")
public String getBudgetActivityTitle() {
return budgetActivityTitle;
}
public void setBudgetActivityTitle(String budgetActivityTitle) {
this.budgetActivityTitle = budgetActivityTitle;
}
@XmlElement(name = "ProgramElementFunding")
public ProgramElementFunding getProgramElementFunding() {
return programElementFunding;
}
public void setProgramElementFunding(ProgramElementFunding programElementFunding) {
this.programElementFunding = programElementFunding;
}
}
ProgramElementFunding类:
package com.mason.seor.model.r2;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlType(name = "ProgramElementFunding")
public class ProgramElementFunding {
Double priorYear;
Double currentYear;
Double budgetYearOne;
Double budgetYearOneBase;
Double budgetYearTwo;
Double budgetYearThree;
Double budgetYearFour;
Double budgetYearFive;
@XmlElement(name = "PriorYear")
public Double getPriorYear() {
return priorYear;
}
public void setPriorYear(Double priorYear) {
this.priorYear = priorYear;
}
@XmlElement(name = "CurrentYear")
public Double getCurrentYear() {
return currentYear;
}
public void setCurrentYear(Double currentYear) {
this.currentYear = currentYear;
}
@XmlElement(name = "BudgetYearOne")
public Double getBudgetYearOne() {
return budgetYearOne;
}
public void setBudgetYearOne(Double budgetYearOne) {
this.budgetYearOne = budgetYearOne;
}
@XmlElement(name = "BudgetYearOneBase")
public Double getBudgetYearOneBase() {
return budgetYearOneBase;
}
public void setBudgetYearOneBase(Double budgetYearOneBase) {
this.budgetYearOneBase = budgetYearOneBase;
}
@XmlElement(name = "BudgetYearTwo")
public Double getBudgetYearTwo() {
return budgetYearTwo;
}
public void setBudgetYearTwo(Double budgetYearTwo) {
this.budgetYearTwo = budgetYearTwo;
}
@XmlElement(name = "BudgetYearThree")
public Double getBudgetYearThree() {
return budgetYearThree;
}
public void setBudgetYearThree(Double budgetYearThree) {
this.budgetYearThree = budgetYearThree;
}
@XmlElement(name = "BudgetYearFour")
public Double getBudgetYearFour() {
return budgetYearFour;
}
public void setBudgetYearFour(Double budgetYearFour) {
this.budgetYearFour = budgetYearFour;
}
@XmlElement(name = "BudgetYearFive")
public Double getBudgetYearFive() {
return budgetYearFive;
}
public void setBudgetYearFive(Double budgetYearFive) {
this.budgetYearFive = budgetYearFive;
}
}
XML示例:
<?xml version="1.0" encoding="UTF-8"?>
<ProgramElementList xmlns="http://www.dtic.mil/comptroller/xml/schema/022009/r2" targetSchemaVersion="1.0">
<ProgramElement monetaryUnit="Millions" classification="UNCLASSIFIED">
<ProgramElementNumber>0601110D8Z</ProgramElementNumber>
<ProgramElementTitle>Basic Research Initiatives</ProgramElementTitle>
<R1LineNumber>3</R1LineNumber>
<BudgetYear>2017</BudgetYear>
<BudgetCycle>PB</BudgetCycle>
<SubmissionDate>2016-02</SubmissionDate>
<ServiceAgencyName>Office of the Secretary Of Defense</ServiceAgencyName>
<AppropriationCode>0400</AppropriationCode>
<AppropriationName>Research, Development, Test Evaluation, Defense-Wide</AppropriationName>
<BudgetActivityNumber>1</BudgetActivityNumber>
<BudgetActivityTitle>Basic Research</BudgetActivityTitle>
<ProgramElementFunding>
<PriorYear>41.054</PriorYear>
<CurrentYear>71.940</CurrentYear>
<BudgetYearOne>36.654</BudgetYearOne>
<BudgetYearOneBase>36.654</BudgetYearOneBase>
<BudgetYearTwo>40.649</BudgetYearTwo>
<BudgetYearThree>42.988</BudgetYearThree>
<BudgetYearFour>45.607</BudgetYearFour>
<BudgetYearFive>46.500</BudgetYearFive>
<CostToComplete>Continuing</CostToComplete>
<TotalCost>Continuing</TotalCost>
</ProgramElementFunding>
</ProgramElement>
答案 0 :(得分:0)
显然问题在于XML中的命名空间;由于某种原因,命名空间变得无法访问,我想这会导致读者出现故障。我通过使用XStreamMarshaller的自定义实现来解决这个问题: