2014-02-10 3 views
2

мне нужно сбросить содержание некоторых таблиц базы данных в файл XML с этой структуройПостроить нетривиальный файл XML с StaxEventItemWriter

<dump> 
    <table name="tableName1"> 
    <records> 
     <record> 
     <first column name>value</first column name> 
     <second column name>value</second column name> 
     <third column name>value</third column name> 
     </record> 
     <record>...</record> 
    </records> 
    </table> 
    <table name="tableName2">...</table> 
</dump> 

Реальным количеством записей для каждой таблицы в неизвестное, поэтому я не могу хранить все данные для одной таблицы в памяти и дамп в XML.
Моя работа теперь определяется как:


<job id="dump-database-job"> 
    <step id="dumpTables"> 
    <tasklet> 
    <chunk reader="dumpReader" processor="dumpProcessor" writer="dumpWriter" commit-interval="100" /> 
    </tasklet> 
    </step> 
</job> 

<bean name="dumpProcessor" class="RecordBeanToJaxbElementProcessor" /> 
<bean name="dumpReader" class="CompositeItemReader"> 
    <property name="delegates"> 
    <array> 
    <ref bean="TABLE_ONE_Reader" /> 
    <ref bean="TABLE_TWO_Reader" /> 
    <ref bean="TABLE_NTH_Reader" /> 
    <!-- Other delegates omitted,one for table,for brevity... --> 
    </array> 
    </property> 
    <property name="name" value="dumpReader" /> 
</bean> 

<bean name="TABLE_ONE_Reader" class="JdbcCursorItemReader"> 
    <property name="rowMapper"> 
    <bean name="rowMapper" class="RecordBeanRowMapper"> 
     <property name="tableName=" value="TABLE_ONE" /> 
    </bean> 
    </property> 
    <!--other mandatory property omitted --> 
</bean> 

<bean name="dumpWriter" class="StaxEventItemWriter" scope="step"> 
    <property name="resource" value="file:#{jobParameters['outfile']}" /> 
    <property name="shouldDeleteIfEmpty" value="true" /> 
    <property name="marshaller" ref="marshaller" /> 
    <property name="overwriteOutput" value="true" /> 
    <property name="rootTagName" value="dump" /> 
</bean> 

<bean name="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> 
    <property name="supportJaxbElementClass" value="true" /> 
    <property name="classesToBeBound"> 
    <array> 
     <value>RecordBean</value> 
    </array> 
    </property> 
</bean> 

public class RecordBeanRowMapper implements RowMapper<RecordBean> { 
    final static RowMapper<Map<String, Object>> columnMapRowMapper = new ColumnMapRowMapper(); 
    private String tableName; 

    public void setTableName(String tableName) { 
    this.tableName = tableName; 
    } 

    @Override 
    public RecordBean mapRow(ResultSet rs, int rowNum) throws SQLException { 
    final RecordBean b = new RecordBean(); 

    b.setTableName(tableName); 
    b.setColumnValues(Maps.transformValues(columnMapRowMapper.mapRow(rs, rowNum), new Function<Object, String>() { 
     @Override 
     public String apply(Object input) { 
     return (input == null ? "NULL" : input.toString(); 
     } 
    } 
} 

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(namespace="") 
class RecordBean { 
    private String tableName; 
    @XmlJavaTypeAdapter 
    /* Entries are written using an adapter to write data as 
    * <key>value</key> 
    * <key>value</key> 
    * ... 
    */ 
    public Map<String,String> entries = new HashMap<String,String>(); 
} 

/* Use to build item node name using dynamic tableName as 
* <tableName> 
* <key>value</key> 
* <key>value</key> 
* </tableName> 
*/ 
public class RecordBeanToJaxbElementProcessor implements ItemProcessor<RecordBean, JAXBElement<?>> { 
    @Override 
    public JAXBElement<?> process(RecordBean item) throws Exception { 
    return new JAXBElement<RecordBean>(new QName(item.getTableName()), RecordBean.class, item); 
    } 
} 

Эта работа является неполным и не покрывает свои потребности, потому что вывод выглядит

<?xml version="1.0" encoding="UTF-8"?> 
<dump> 
    <!-- First record of table TABLE_ONE --> 
    <TABLE_ONE> 
    <code>Code one</code> 
    <description>A record</description> 
    <agomappedtable>xyz</agomappedtable> 
    <enumcode>NULL</enumcode> 
    <is_persistent>false</is_persistent> 
    <keep_history_data>false</keep_history_data> 
    </TABLE_ONE> 
    <!-- Other tons from TABLE_ONE --> 
    <!-- First record of table TABLE_TWO --> 
    <TABLE_TWO> 
    <code>Code 2</code> 
    <description>Another record</description> 
    <his_name>no_name</his_name> 
    </TABLE_TWO> 
    <!-- Other tons from TABLE_TWO --> 
    <!-- More tables... --> 
</dump> 

Думаю, мне нужно обогатить писателя и/или m компоненты arshaller для достижения своей цели, но я не нашел хороший способ, чтобы продолжить :(

Мои вопросы:
Как построить сложную структуру XML, как описано в начале и сделать работу полностью перезапускаемые и с небольшим использованием памяти?

+0

Вы пишете 3 таблицы в один файл и так далее. Так что для каждой строки во всех трех таблицах создается один файл? если да, то как связаны строки? –

+0

строки не связаны. это просто свалка базы данных. один ОГРОМНЫЙ файл для всех строк всех таблиц –

ответ

2

[Взято из spring.io forum]
Это не возможно со стандартным StaxEventItemWriter. Я должен был сделать что-то подобное для одного из моих проектов и написал пользовательский GroupingStaxEventItemWriter. См. Код ниже. Вам нужно будет изменить метод openGroup и closeGroup для вашего конкретного варианта использования. Обратите внимание, что тег группировки закрывается путем прямой записи в базовый java.io.Writer, а не через XMLEventWriter. Это необходимо для перезапуска.

public class GroupingStaxEventItemWriter<T> extends StaxEventItemWriter<T> { 

    private static final String GROUP_IDENTIFIER = "CURRENT_GROUP"; 

    private Classifier<T, String> classifier; 

    private String currentGroup; 

    private XMLEventWriter eventWriter; 

    private Writer writer; 

    @Override 
    public void write(List<? extends T> items) throws XmlMappingException, Exception { 
     Map<String, List<T>> itemsGroup = new LinkedHashMap<String, List<T>>(); 
     for (T item : items) { 
      String group = classifier.classify(item); 
      if (!itemsGroup.containsKey(group)) { 
       itemsGroup.put(group, new ArrayList<T>()); 
      } 
      itemsGroup.get(group).add(item); 
     } 
     for (String group : itemsGroup.keySet()) { 
      if (group == null || !group.equals(currentGroup)) { 
       if (currentGroup != null) { 
        closeGroup(currentGroup); 
       } 
       currentGroup = group; 
       if (currentGroup != null) { 
        openGroup(currentGroup); 
       } 
      } 
      super.write(itemsGroup.get(group)); 
     } 
    } 

    protected void openGroup(String group) throws XMLStreamException, FactoryConfigurationError { 
     String groupTagName = group; 
     String groupTagNameSpacePrefix = ""; 
     String groupTagNameSpace = null; 
     if (groupTagName.contains("{")) { 
      groupTagNameSpace = groupTagName.replaceAll("\\{(.*)\\}.*", "$1"); 
      groupTagName = groupTagName.replaceAll("\\{.*\\}(.*)", "$1"); 
      if (groupTagName.contains(":")) { 
       groupTagNameSpacePrefix = groupTagName.replaceAll("(.*):.*", "$1"); 
       groupTagName = groupTagName.replaceAll(".*:(.*)", "$1"); 
      } 
     } 
     XMLEventFactory xmlEventFactory = createXmlEventFactory(); 
     eventWriter.add(xmlEventFactory.createStartElement(groupTagNameSpacePrefix, groupTagNameSpace, groupTagName)); 
    } 

    protected void closeGroup(String group) 
      throws XMLStreamException, FactoryConfigurationError { 
     String groupTagName = group; 
     String groupTagNameSpacePrefix = ""; 
     if (groupTagName.contains("{")) { 
      groupTagName = groupTagName.replaceAll("\\{.*\\}(.*)", "$1"); 
      if (groupTagName.contains(":")) { 
       groupTagNameSpacePrefix = groupTagName.replaceAll("(.*):.*", "$1") + ":"; 
       groupTagName = groupTagName.replaceAll(".*:(.*)", "$1"); 
      } 
     } 
     try { 
      writer.write("</" + groupTagNameSpacePrefix + groupTagName + ">"); 
     } catch (IOException ioe) { 
      throw new DataAccessResourceFailureException("Unable to close group: [" + group + "]", ioe); 
     } 
    } 

    @Override 
    protected XMLEventWriter createXmlEventWriter(XMLOutputFactory outputFactory, Writer writer) 
      throws XMLStreamException { 
     this.writer = writer; 
     this.eventWriter = super.createXmlEventWriter(outputFactory, writer); 
     return eventWriter; 
    } 

    @Override 
    public void open(ExecutionContext executionContext) { 
     if (executionContext.containsKey(getExecutionContextKey(GROUP_IDENTIFIER))) { 
      currentGroup = executionContext.getString(getExecutionContextKey(GROUP_IDENTIFIER)); 
     } 
     super.open(executionContext); 
    } 

    @Override 
    public void update(ExecutionContext executionContext) { 
     executionContext.putString(getExecutionContextKey(GROUP_IDENTIFIER), currentGroup); 
     super.update(executionContext); 
    } 

    @Override 
    public void close() { 
     if (currentGroup != null) { 
      try { 
       closeGroup(currentGroup); 
      } catch (XMLStreamException e) { 
       throw new ItemStreamException("Failed to write close tag for element: " + currentGroup, e); 
      } catch (FactoryConfigurationError e) { 
       throw new ItemStreamException("Failed to write close tag for element: " + currentGroup, e); 
      } 
     } 
     super.close(); 
    } 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     super.afterPropertiesSet(); 
     Assert.notNull(classifier, "Missing required property 'classifier'"); 
    } 

    public void setClassifier(Classifier<T, String> classifier) { 
     this.classifier = classifier; 
    } 

} 
Смежные вопросы