如何在StaxEventItemReader中为嵌套元素指定fragmentRootElementNames?

时间:2019-08-28 17:49:13

标签: spring-batch hibernate-mapping stax

使用Spring-batch 4.1.2来使用复杂的XML并写入数据库。 使用StaxEventItemReader读取XML数据。

在指定 fragmentRootElementNames 时,我遇到了嵌套元素的问题。

 <bean id="productFileItemReader" class="org.springframework.batch.item.xml.StaxEventItemReader" scope="step">
        <property name="resource" ref="inputResource" />
        <property name="fragmentRootElementNames" value="ProductFeatures,ProductFeaturesDetail,SomeOtherNon-NestedFragmentNames..." />
        <property name="unmarshaller" ref="productMarshaller" />
</bean>

    <bean id="productMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
            <list>
                <value>com.xyz.jaxb.ProductFeaturesType</value>
                <value>com.xyz.jaxb.ProductFeaturesDetailType</value>
                ...Some more types
            </list>
        </property>
    </bean>

<bean id="productFileItemProcessor" class="org.springframework.batch.item.support.ClassifierCompositeItemProcessor"
        scope="step">
        <property name="classifier" ref="prodItemProcessorclassifier" />
    </bean>

    <bean id="prodItemProcessorclassifier" class="org.springframework.classify.BackToBackPatternClassifier">
        <property name="routerDelegate">
            <bean class="com.xyz.batch.CustomProdTypeClassifier" />
        </property>
        <property name="matcherMap">
            <map>
                <entry key="ProductFeatures" value-ref="prodFeaturesProcessor" />
                <entry key="ProductFeaturesDetail" value-ref="prodFeaturesDetailProcessor" />
            </map>
        </property>
    </bean>

    <bean id="prodItemWriter" class="org.springframework.batch.item.support.ClassifierCompositeItemWriter"
        scope="step">
        <property name="classifier" ref="prodItemWriterclassifier" />
    </bean>

    <bean id="prodItemWriterclassifier" class="org.springframework.classify.BackToBackPatternClassifier">
        <property name="routerDelegate">
            <bean class="com.xyz.batch.CustomProdTypeClassifier" />
        </property>
        <property name="matcherMap">
            <map>
                <entry key="ProductFeaturesDetail" value-ref="prodHibernateItemWriter" />
                <entry key="ProductParameters" value-ref="myListItemWriter" />
            </map>
        </property>
    </bean>

    <bean id="myListItemWriter" class="com.xyz.writer.ListDelegateItemWriter">
        <property name="delegate" ref="prodHibernateItemWriter" />
    </bean>

    <bean id="prodHibernateItemWriter" class="org.springframework.batch.item.database.HibernateItemWriter">
        <property name="sessionFactory" ref="mySessionFactory" />
    </bean>

这是ItemProcessors的签名

public class ProductFeaturesProcessor implements ItemProcessor<ProductFeaturesType, List<ProductParamEntity>>{}
public class ProductFeaturesDetailProcessor implements ItemProcessor<ProductFeaturesDetailType, ProductFeaturesDetailEntity> {}

XML结构如下-

<ProductFeatures>
    <Version>12</Version>
    <MessageEN>Welcome</MessageEN>
    <MessageFR>Bienvenue</MessageFR>
    <ProductFeaturesDetail>
            <!-- Some elements here -->
    </ProductFeaturesDetail>
    <ProductFeaturesDetail>
            <!-- Some elements here -->
    </ProductFeaturesDetail>
    <ProductFeaturesDetail>
            <!-- Some elements here -->
    </ProductFeaturesDetail>
    <ProductFeaturesDetail>
            <!-- Some elements here -->
    </ProductFeaturesDetail>
</ProductFeatures>
  

此处,ProductFeatures的minOccurs = 1,maxOccurs = 1。   ProductFeaturesDetail是不受限制的。

每个版本,MessageEN,MessageFR都必须作为记录保留在表A中,该记录具有2列ParamName | ParamValue
每个ProductFeaturesDetail是要保留在表B中的记录。

  

即这3个元素分别映射到ProductParamEntity(即   ParamName =版本,ParamValue = 12; ParamName = MessageEN,   ParamValue = Welcome等。)

     

逻辑上,ProductFeatures不是直接映射到表,而是数据   存储在2个表中,作为列表和   列出到表PRODUCT_PARAM和   分别为PRODUCT_FEATURES_DETAIL。

这里是对象-JAXB和相应实体

@XmlRootElement(name = "ProductFeatures")
public class ProductFeaturesType {

    @XmlElement(name = "Version")
    protected String version;

    @XmlElement(name = "MessageEN")
    protected String messageEN;

    @XmlElement(name = "MessageFR")
    protected String messageFR;

    @XmlElement(name = "ProductFeaturesDetail")
    protected List<ProductFeaturesDetailType> prodFeaturesDetail;

}


@Entity
@Table(name="PRODUCT_PARAM")
public class ProductParamsEntity extends BaseEntity implements Serializable {

    @Id
    @Column(name="PARAM_NAME")
    private String              paramName;

    @Column(name="PARAM_VALUE")
    private String              paramValue;

}

@Entity
@Table(name="PRODUCT_FEATURES_DETAIL")
public class ProductFeaturesDetailEntity extends BaseEntity implements Serializable {
    @Id
    @Column(name="PROD_CATEGORY")
    private String              prodCategory;

    //---More attributes ---
}

这是我的作业配置:

<batch:job id="consumeProductFileJob" job-repository="jobRepository" restartable="true">
        <batch:step id="validateFile" parent="validateXMLSchema">
            <batch:next on="COMPLETED" to="persistData" />
            <batch:next on="FAILED" to="notifyException" />
        </batch:step>
        <batch:step id="persistData">
            <batch:tasklet transaction-manager="prodHibernateTransactionManager">
                <batch:chunk reader="productFileItemReader" processor="productFileItemProcessor" writer="prodItemWriter" commit-interval="500" >

                </batch:chunk>
                <batch:transaction-attributes isolation="DEFAULT" propagation="REQUIRED" />
            </batch:tasklet>
        </batch:step>
        <batch:step id="notifyException">
            -- Do something---
        </batch:step>
    </batch:job>
  

问题::如果我同时将其指定为fragmentRootElementNames,则仅保留ProductFeatures元素的Version,MessageEN,MessageFR   在表A中。 ProductFeaturesDetail被忽略。

     

如果我仅指定ProductFeaturesDetail,它可以正常工作。但是我不明白   ProductFeatures中的各个元素。

我想要两个元素的数据。实现此目的的方法是什么?

P.S。我正在使用HibernateItemWriter进行持久化。

0 个答案:

没有答案