是unitils的一个bug。
使用mysql,用@DataSet注入测试数据时,会抛出org.dbunit.dataset.NoSuchColumnException。
起因是新版本的dbunit(目前是2.4.9)细化了各种数据库的MetadataHandler的处理,为每一种数据库提供了一个MetadataHandler,如MySqlMetadataHandler,Db2MetadataHandler等。而unitils并没有升级(快两年没更新了,还会更新吗?),仍然使用dbunit提供的DefaultMetadataHandler。这个DefaultMetadataHandler并不能很好的处理各个数据库之间的不同,所以会导致兼容问题。
有一种解决方案是将dbunit降级成2.4.2版本,虽然能解决这个问题,但是会引入新的问题。如果在数据XML文件中写入同一个表的两条数据,如:
<?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
真的很感谢这个作者抛砖引玉提供了很多线索。
我不是很喜欢直接修改第三方开源库的源码,缺点就不说了,所以想用扩展的方式解决这个问题。
首先通过阅读源码发现,dbunit提供了MySqlConnection,OracleConnection等等类。而dbunit则是定义了一个他们的兄弟类DbUnitDatabaseConnection,所有的dbunit的数据库连接都从这个类生成,这个类自然不包括各个数据库的方言之类的东西。
再看MySqlConnection,其实里面也没什么东西,就是向config里set了两个属性
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
来源:oschina
链接:https://my.oschina.net/u/719192/blog/173644