Spring Batch解组XML问题,ItemReader和ItemWriter

时间:2018-07-02 12:17:03

标签: xml spring-batch unmarshalling

目前,我可以毫无问题地解组下面的XML,但是在阅读文档时,我得到的是 PublicFigure 对象和 Associate 项目列表。

下面是我当前的实现方式。

我的Xml

<PublicFigure id="101">
    <Associate id="102" ex="No" code="66"/>
    <Associate id="103" ex="No" code="22"/>
</PublicFigure>
<PublicFigure id="102">
    <Associate id="144" ex="No" code="56"/>
    <Associate id="155" ex="No" code="45"/>
</PublicFigure>

PublicFigure Java类

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "associate"
})
@XmlRootElement(name = "PublicFigure")
public class PublicFigure {

    @XmlElement(name = "Associate", required = true)
    protected List<Associate> associate;
    @XmlAttribute(name = "id", required = true)
    protected BigInteger id;

    //getters and setters

关联Java类

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "")
    @XmlRootElement(name = "Associate")
    public class Associate {

        @XmlAttribute(name = "id", required = true)
        protected BigInteger id;
        @XmlAttribute(name = "code", required = true)
        protected BigInteger code;
        @XmlAttribute(name = "ex", required = true)
        protected String ex;

        //getter and setters

是否有任何需要将XML编组为以下对象的对象。换句话说,要从那里结束关联对象和PublicFigure对象,我可以在需要时获取ID。在下面的示例中,我将获得关联对象,但PublicFigure将为NULL

解组到类似下面的内容

@XmlRootElement(name = "Associate")
public class Associate {

    @XmlAttribute(name = "id", required = true)
    protected BigInteger id;
    @XmlAttribute(name = "code", required = true)
    protected BigInteger code;
    @XmlAttribute(name = "ex", required = true)
    protected String ex;

    @XmlElement(name = "PublicFigure", required = true)
    protected PublicFigure publicFigure;

由于Spring Batch ItemReader和ItemWriter,您可能会问我为什么要这样做。我的ItemWriter希望我读取10个关联对象的块(因为它们必须进入数据库),在每个对象中我都需要引用公共图形ID。用 PublicFigure 对象解组和结束,每个对象都包含一个 Associates 列表,但是我不确定如何处理ItemPreparedStatementSetter,这样我将以10个块结尾而不是Associates的PublicFigures。在每个PublicFigure都有20个关联的列表的情况下(为便于示例,我们假设),在这种情况下,由10个插入块组成的块现在将变为10 * 20 = 200个插入-因为我必须插入关联列表。家有道理。

@Bean
    ItemReader<PublicFigure> customerItemReader() {
        StaxEventItemReader<PublicFigure> xmlFileReader = new StaxEventItemReader<>();
        xmlFileReader.setResource(new ClassPathResource("/data/Test_file.xml"));
        xmlFileReader.setFragmentRootElementName("PublicFigure");

        Jaxb2Marshaller publicFigMarshaller = new Jaxb2Marshaller();
        publicFigMarshaller.setClassesToBeBound(PublicFigure.class);

        xmlFileReader.setUnmarshaller(publicFigMarshaller);
        return xmlFileReader;
    }


@Bean
    ItemWriter<Associate> associateItemWriter(DataSource dataSource, NamedParameterJdbcTemplate jdbcTemplate) {
        JdbcBatchItemWriter<Associate> databaseItemWriter = new JdbcBatchItemWriter<>();
        databaseItemWriter.setDataSource(dataSource);
        databaseItemWriter.setJdbcTemplate(jdbcTemplate);

        databaseItemWriter.setSql(QUERY_INSERT);

        ItemPreparedStatementSetter<Associate> associateItemPreparedStatementSetter = new PersonAssociationsPreparedStatementSetter();
        databaseItemWriter.setItemPreparedStatementSetter(associateItemPreparedStatementSetter);

        return databaseItemWriter;
    }


    @Bean
    Step associateXmlFileToDatabaseStep() {
        return stepBuilderFactory.get("associateXmlFileToDatabaseStep")
                .<Associate, Associate>chunk(10)
                .reader(associateItemReader())
                .writer(associateItemWriter(super.dataSource, super.namedParameterJdbcTemplate))
                .build();
    }

1 个答案:

答案 0 :(得分:0)

我将尝试以相反的方式工作:我将使用PublicFigure而不是使用Associate作为根对象。
Associate对象包含对PublicFigure父对象的引用,但此引用是使用Jax2Marshaller.Listener注入的。
侦听器应该很容易编写,因为您会收到未编组的对象及其父对象作为参数

class PublicFigureAssociateListener implements Listener {
  afterUnmarshal(Object target, Object parent) {
    if(target instanceof Associate) {
      ((Associate) target).publicFigure = (PublicFigure) parent;
    }
  }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "PublicFigure")
public class PublicFigure {
    @XmlAttribute(name = "id", required = true)
    protected BigInteger id;
    @XmlElement(name = "Associate", required = true)
    protected List<Associate> associate;    
}

@XmlRootElement(name = "Associate")
public class Associate {
    @XmlAttribute(name = "id", required = true)
    protected BigInteger id;
    @XmlAttribute(name = "code", required = true)
    protected BigInteger code;
    @XmlAttribute(name = "ex", required = true)
    protected String ex;
    /* This propery will be injected using Marshaller.Listener` */
    public PublicFigure publicFigure;
}

因此,您的代码将根据以下内容进行更改:

@Bean
ItemReader<PublicFigure> publicFigureItemReader() {
      StaxEventItemReader<PublicFigure> xmlFileReader = new 
      xmlFileReader.setResource(new ClassPathResource("/data/Test_file.xml"));
      xmlFileReader.setFragmentRootElementName("PublicFigure");
      Jaxb2Marshaller publicFigMarshaller = new Jaxb2Marshaller();
      publicFigMarshaller.setClassesToBeBound(Associate.class, PublicFigure.class);
      publicFigMarshaller.setUnmarshallerListener(new PublicFigureAssociateListener());
      xmlFileReader.setUnmarshaller(publicFigMarshaller);
}

@Bean
ItemReader<Associate> customerItemReader() {
  return new AssociateItemReader(new publicFigureItemReader());
}

编辑

侦听器运行不正常,因此您可以通过以下方式使用委托ItemReader

class AssociateItemReader implements ItemReader<Associate> {
  private final ItemReader<PublicFigure> delegate;
  private ListItemReader l;

  AssociateItemReader(ItemReader<PublicFigure> delegate) {
    this.delegate = delegate;
    this.l = new ListItemReader(Collections.emptyList());
  }
  Associate read() {
    Associate next = l.read();
    if(next == null) {
      PublicFigure pf = delegate.read();
      if(pf != null) {
        l = new ListItemReader(pf.getAssociates());
        next = l.read();
      }
    }
    return next;
  }
}

请记住将publicFigureItemReader()作为流注册到associateXmlFileToDatabaseStep()中,以便SB可以处理读取器的生命周期。