【已解決】mysql+unitils用@DataSet,拋NoSuchColumnException

是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

相關文章
相關標籤/搜索