【已解决】mysql+unitils用@DataSet,抛NoSuchColumnException

≯℡__Kan透↙ 提交于 2019-11-29 01:24:48

是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


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!