DbUnit测试数据XML格式常用的就两种,FlatXmlDataset和XmlDataSet:

<dataset>
  <USER USER_ID="1" USER_NAME="ZhuTou" PASSWORD="zt"/>
</dataset>
<dataset>
  <table name="USER">
    <column>USER_ID</column>
    <column>USER_NAME</column>
    <column>PASSWORD</column>
    <row>
      <value>1</value>
      <value>ZhuTou</value>
      <value>zt</value>
    </row>
  </table>
</dataset>

第一种比较简洁,某些空的字段可以直接不写,但是不支持CDATA,如果有些数据较长或是二进制需要使用BASE64编码,就用不了了。

第二种就是支持CDATA,缺点是空的字段也要写一个<NULL/>或<NONE/>,不能少,顺序也要和column定义一致,不能乱。这样不但烦琐(特别是某些表很多字段可能为空的情况),还容易出错,一堆数据很难看出哪行是对应哪一个字段的。

我期望的格式是

<dataset>
  <table name="user">
    <column>user_id</column>
    <column>user_name</column>
    <column>password</column>
    <column>address</column>
    <column>remark</column>
    <row>
      <value name="user_id">1</value>
      <value name="user_name">ZhuTou</value>
      <value name="password"><![CDATA[jfl3<0d/;]]></value>
    </row>
    ...
  </table>
<dataset>

而且row里的的顺序可以乱排。

看了DBUNIT部分代码(主要是org.dbunit.dataset.xml.XmlProducer和org.dbunit.dataset.xml.XmlDataSet),只要自己实现一个XmlProducer就可以了,不过因为XmlDataSet里无法指定producer所以只要把XmlDataSet也重写一个。

CustomXmlProducer:

  1 import java.io.IOException;
  2 import java.io.InputStream;
  3 import java.util.HashMap;
  4 import java.util.LinkedList;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 import javax.xml.parsers.ParserConfigurationException;
  9 import javax.xml.parsers.SAXParserFactory;
 10 
 11 import org.dbunit.dataset.Column;
 12 import org.dbunit.dataset.DataSetException;
 13 import org.dbunit.dataset.DefaultTableMetaData;
 14 import org.dbunit.dataset.ITableMetaData;
 15 import org.dbunit.dataset.datatype.DataType;
 16 import org.dbunit.dataset.stream.DefaultConsumer;
 17 import org.dbunit.dataset.stream.IDataSetConsumer;
 18 import org.dbunit.dataset.stream.IDataSetProducer;
 19 import org.slf4j.Logger;
 20 import org.slf4j.LoggerFactory;
 21 import org.xml.sax.Attributes;
 22 import org.xml.sax.ContentHandler;
 23 import org.xml.sax.ErrorHandler;
 24 import org.xml.sax.InputSource;
 25 import org.xml.sax.SAXException;
 26 import org.xml.sax.SAXParseException;
 27 import org.xml.sax.XMLReader;
 28 import org.xml.sax.helpers.DefaultHandler;
 29 
 30 public class CustomXmlProducer extends DefaultHandler implements
 31         IDataSetProducer, ContentHandler, ErrorHandler {
 32 
 33     /**
 34      * Logger for this class
 35      */
 36     private static final Logger logger = LoggerFactory
 37             .getLogger(CustomXmlProducer.class);
 38 
 39     private static final IDataSetConsumer EMPTY_CONSUMER = new DefaultConsumer();
 40 
 41     private static final String DATASET = "dataset";
 42     private static final String TABLE = "table";
 43     private static final String NAME = "name";
 44     private static final String COLUMN = "column";
 45     private static final String ROW = "row";
 46     private static final String VALUE = "value";
 47 //    private static final String NULL = "null";
 48 //    private static final String NONE = "none";
 49 
 50     private static final String COLUMN_ATTR_NAME = "name";
 51 
 52     private final InputSource _inputSource;
 53     private boolean _validating = false;
 54 
 55     private IDataSetConsumer _consumer = EMPTY_CONSUMER;
 56 
 57     private String _activeTableName;
 58     private ITableMetaData _activeMetaData;
 59 
 60     private List<String> _activeColumnNames;
 61     private StringBuffer _activeCharacters;
 62 //    private List _activeRowValues;
 63 
 64     private Map<String, String> _activeRowValues;
 65     private String _activeColumnName;
 66     private List<String> __activeColumnNames;
 67 
 68     public CustomXmlProducer(InputSource inputSource) {
 69         _inputSource = inputSource;
 70     }
 71 
 72     private ITableMetaData createMetaData(String tableName, List<String> columnNames) {
 73         logger.debug("createMetaData(tableName={}, _columnNames={}) - start",
 74                 tableName, columnNames);
 75 
 76         Column[] columns = new Column[columnNames.size()];
 77         for (int i = 0; i < columns.length; i++) {
 78             String columnName = (String) columnNames.get(i);
 79             columns[i] = new Column(columnName, DataType.UNKNOWN);
 80         }
 81         DefaultTableMetaData metaData = new DefaultTableMetaData(tableName,
 82                 columns);
 83         return metaData;
 84     }
 85 
 86     public void setValidating(boolean validating) {
 87         _validating = validating;
 88     }
 89 
 90     // //////////////////////////////////////////////////////////////////////////
 91     // IDataSetProducer interface
 92 
 93     public void setConsumer(IDataSetConsumer consumer) throws DataSetException {
 94         logger.debug("setConsumer(consumer={}) - start", consumer);
 95         _consumer = consumer;
 96     }
 97 
 98     public void produce() throws DataSetException {
 99         logger.debug("produce() - start");
100 
101         try {
102             SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
103             saxParserFactory.setValidating(_validating);
104             XMLReader xmlReader = saxParserFactory.newSAXParser()
105                     .getXMLReader();
106 
107             xmlReader.setContentHandler(this);
108             xmlReader.setEntityResolver(this);
109             xmlReader.setErrorHandler(this);
110             xmlReader.parse(_inputSource);
111         } catch (ParserConfigurationException e) {
112             throw new DataSetException(e);
113         } catch (SAXException e) {
114             DataSetException exceptionToRethrow = CustomXmlProducer
115                     .buildException(e);
116             throw exceptionToRethrow;
117         } catch (IOException e) {
118             throw new DataSetException(e);
119         }
120     }
121 
122     /**
123      * Wraps a {@link SAXException} into a {@link DataSetException}
124      * 
125      * @param cause
126      *            The cause to be wrapped into a {@link DataSetException}
127      * @return A {@link DataSetException} that wraps the given
128      *         {@link SAXException}
129      */
130     protected final static DataSetException buildException(SAXException cause) {
131         int lineNumber = -1;
132         if (cause instanceof SAXParseException) {
133             lineNumber = ((SAXParseException) cause).getLineNumber();
134         }
135         Exception exception = cause.getException() == null ? cause : cause
136                 .getException();
137         String message;
138 
139         if (lineNumber >= 0) {
140             message = "Line " + lineNumber + ": " + exception.getMessage();
141         } else {
142             message = exception.getMessage();
143         }
144 
145         if (exception instanceof DataSetException) {
146             return (DataSetException) exception;
147         } else {
148             return new DataSetException(message, exception);
149         }
150     }
151 
152     // //////////////////////////////////////////////////////////////////////////
153     // EntityResolver interface
154 
155     public InputSource resolveEntity(String publicId, String systemId)
156             throws SAXException {
157         logger.debug("resolveEntity(publicId={}, systemId={}) - start",
158                 publicId, systemId);
159 
160         InputStream in = getClass().getClassLoader().getResourceAsStream(
161                 "org/dbunit/dataset/xml/dataset.dtd");
162         return (new InputSource(in));
163     }
164 
165     // //////////////////////////////////////////////////////////////////////
166     // ContentHandler interface
167 
168     public void startElement(String uri, String localName, String qName,
169             Attributes attributes) throws SAXException {
170         if (logger.isDebugEnabled()) {
171             logger.debug(
172                     "startElement(uri={}, localName={}, qName={}, attributes={}) - start",
173                     new Object[] { uri, localName, qName, attributes });
174         }
175 
176         try {
177             // dataset
178             if (qName.equals(DATASET)) {
179                 _consumer.startDataSet();
180                 return;
181             }
182 
183             // table
184             if (qName.equals(TABLE)) {
185                 _activeTableName = attributes.getValue(NAME);
186                 _activeColumnNames = new LinkedList<String>();
187                 return;
188             }
189 
190             // column
191             if (qName.equals(COLUMN)) {
192                 _activeCharacters = new StringBuffer();
193                 return;
194             }
195 
196             // row
197             if (qName.equals(ROW)) {
198                 // End of metadata at first row
199                 if (_activeColumnNames != null) {
200                     _activeMetaData = createMetaData(_activeTableName,
201                             _activeColumnNames);
202                     _consumer.startTable(_activeMetaData);
203                     
204                     __activeColumnNames = _activeColumnNames;
205                     _activeColumnNames = null;
206                 }
207 
208 //                _activeRowValues = new LinkedList();
209                 _activeRowValues = new HashMap<String, String>();
210                 return;
211             }
212 
213             // value
214             if (qName.equals(VALUE)) {
215                 _activeColumnName = attributes.getValue(COLUMN_ATTR_NAME);
216                 _activeCharacters = new StringBuffer();
217                 return;
218             }
219 
220 //            // null
221 //            if (qName.equals(NULL)) {
222 //                _activeRowValues.add(null);
223 //                return;
224 //            }
225 //
226 //            // none
227 //            if (qName.equals(NONE)) {
228 //                _activeRowValues.add(ITable.NO_VALUE);
229 //                return;
230 //            }
231         } catch (DataSetException e) {
232             throw new SAXException(e);
233         }
234     }
235 
236     public void endElement(String uri, String localName, String qName)
237             throws SAXException {
238         if (logger.isDebugEnabled()) {
239             logger.debug("endElement(uri={}, localName={}, qName={}) - start",
240                     new Object[] { uri, localName, qName });
241         }
242 
243         try {
244             // dataset
245             if (qName.equals(DATASET)) {
246                 _consumer.endDataSet();
247                 return;
248             }
249 
250             // table
251             if (qName.equals(TABLE)) {
252                 __activeColumnNames = null;
253                 // End of metadata
254                 if (_activeColumnNames != null) {
255                     _activeMetaData = createMetaData(_activeTableName,
256                             _activeColumnNames);
257                     _consumer.startTable(_activeMetaData);
258                     _activeColumnNames = null;
259                 }
260 
261                 _consumer.endTable();
262                 _activeTableName = null;
263                 _activeMetaData = null;
264                 return;
265             }
266 
267             // column
268             if (qName.equals(COLUMN)) {
269                 _activeColumnNames.add(_activeCharacters.toString());
270                 _activeCharacters = null;
271                 return;
272             }
273 
274             // row
275             if (qName.equals(ROW)) {
276                 final int length = __activeColumnNames.size();
277                 Object[] values = new Object[length];
278 //                for (int i = 0; i < values.length; i++) {
279 //                    values[i] = (i >= _activeRowValues.size()) ? ITable.NO_VALUE
280 //                            : _activeRowValues.get(i);
281 //                }
282                 for(int i = 0;i < length; i ++){
283                     values[i] = _activeRowValues.get(__activeColumnNames.get(i));
284                 }
285                 _consumer.row(values);
286                 _activeRowValues = null;
287                 return;
288             }
289 
290             // value
291             if (qName.equals(VALUE)) {
292                 _activeRowValues.put(_activeColumnName, _activeCharacters.toString());
293                 _activeColumnName = null;
294                 _activeCharacters = null;
295                 return;
296             }
297 
298 //            // null
299 //            if (qName.equals(NULL)) {
300 //                // Nothing to do, already processed in startElement()
301 //                return;
302 //            }
303 //
304 //            // none
305 //            if (qName.equals(NONE)) {
306 //                // Nothing to do, already processed in startElement()
307 //                return;
308 //            }
309         } catch (DataSetException e) {
310             throw new SAXException(e);
311         }
312     }
313 
314     public void characters(char ch[], int start, int length)
315             throws SAXException {
316         if (_activeCharacters != null) {
317             _activeCharacters.append(ch, start, length);
318         }
319     }
320 
321     public void error(SAXParseException e) throws SAXException {
322         throw e;
323     }
324 
325 }

原来value是读到一个list里的,然后和column比较,少的部分用空的Object填,我改成先把value根据name属性放到一个map中,最后再放到一个数组,map中没有的值就是null。

CustomXmlDataSet:

 1 import java.io.InputStream;
 2 import java.io.Reader;
 3 
 4 import org.dbunit.dataset.CachedDataSet;
 5 import org.dbunit.dataset.DataSetException;
 6 import org.xml.sax.InputSource;
 7 
 8 /**
 9  * @author hlw
10  * 
11  */
12 public class CustomXmlDataSet extends CachedDataSet {
13 
14     /**
15      * Creates an XmlDataSet with the specified xml reader.
16      */
17     public CustomXmlDataSet(Reader reader) throws DataSetException {
18         super(new CustomXmlProducer(new InputSource(reader)));
19     }
20 
21     /**
22      * Creates an XmlDataSet with the specified xml input stream.
23      */
24     public CustomXmlDataSet(InputStream in) throws DataSetException {
25         super(new CustomXmlProducer(new InputSource(in)));
26     }
27 
28 }

这个类就是把默认的super(new XmlProducer(...))改成super(new CustomXmlProducer(...))。

 

然后在使用的时候

InputStream input = resourceLoader.getResource(xmlPath)
                        .getInputStream();
IDataSet dataset = new CustomDataSet(input);

还是和原来的用法一样。

 

另外如果测试数据有相互的外键引用的话,会插不进去,这时可以把数据库的约束检查关掉,对于mysql可以在connection url后面加上sessionVariables=FOREIGN_KEY_CHECKS=0

如jdbc.url=jdbc\:mysql\://localhost\:3306/test?sessionVariables=FOREIGN_KEY_CHECKS=0

 

 posted on 2012-05-04 15:31  ﹎敏ō  阅读(1834)  评论(0编辑  收藏  举报