是unitils的一个bug。 java
使用mysql,用@DataSet注入测试数据时,会抛出org.dbunit.dataset.NoSuchColumnException。 mysql
原由是新版本的dbunit(目前是2.4.9)细化了各类数据库的MetadataHandler的处理,为每一种数据库提供了一个MetadataHandler,如MySqlMetadataHandler,Db2MetadataHandler等。而unitils并无升级(快两年没更新了,还会更新吗?),仍然使用dbunit提供的DefaultMetadataHandler。这个DefaultMetadataHandler并不能很好的处理各个数据库之间的不一样,因此会致使兼容问题。 git
有一种解决方案是将dbunit降级成2.4.2版本,虽然能解决这个问题,可是会引入新的问题。若是在数据XML文件中写入同一个表的两条数据,如: sql
<?xml version='1.0' encoding='UTF-8'?> <dataset> <t_role id="1" name="test1" version="0" /> <t_role id="2" name="test2" version="0" /> </dataset>
则会抛出org.dbunit.database.AmbiguousTableNameException 数据库
问题的详细描述见此贴http://zfanxu.iteye.com/blog/1508339 ide
真的很感谢这个做者抛砖引玉提供了不少线索。 测试
我不是很喜欢直接修改第三方开源库的源码,缺点就不说了,因此想用扩展的方式解决这个问题。 spa
首先经过阅读源码发现,dbunit提供了MySqlConnection,OracleConnection等等类。而dbunit则是定义了一个他们的兄弟类DbUnitDatabaseConnection,全部的dbunit的数据库链接都从这个类生成,这个类天然不包括各个数据库的方言之类的东西。 .net
再看MySqlConnection,其实里面也没什么东西,就是向config里set了两个属性 code
getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory()); getConfig().setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MySqlMetadataHandler());因此关键点就在这里,只要能在从unitils获得connection以前把这两个属性set进去,就能够了。
再看unitils源码,获得数据库链接的方法是DbUnitModule的getDbUnitDatabaseConnection方法。那么只要新建一个类,覆盖这个方法,在super以后将相应的属性set进去就能够了。
代码以下:
public final class MySqlDbUnitModule extends DbUnitModule { @Override public DbUnitDatabaseConnection getDbUnitDatabaseConnection(final String schemaName) { DbUnitDatabaseConnection result = dbUnitDatabaseConnections.get(schemaName); if (null != result) { return result; } result = super.getDbUnitDatabaseConnection(schemaName); result.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory()); result.getConfig().setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MySqlMetadataHandler()); return result; } }
最后一步,使用新建的MySqlDbUnitModule替换默认的DbUnitModule。这个就比较简单了,在unitils.properties中加入:
unitils.module.dbunit.className=com.miraclesea.test.database.module.MySqlDbUnitModule
ok,大功告成,不用修改源码的方式,并且还能够重构的更好一点。如使用反射支持不一样的数据库,而不单单是Mysql,或者使用枚举,把全部的数据库类型和它相关的DataTypeFactory以及MetadataHandler映射好。
因为反射不能在编译期检查,因此我选择了第二种方式。我将MySqlDbunitModule重构成了ExtDbunitModule和SupportedDatabaseType两个类。而且在unitils.properties文件中从新定义了一个key,DbUnitModule.database.type,用于set数据库类型是MySql仍是Oracle等,目前支持全部的dbunit支持的数据库类型。有兴趣请到git oschina直接下载源码。
http://git.oschina.net/terrymanu/miracle-framework/tree/master/miraclesea/test-base