抱怨來(lái)了
dbunit這么多人用兆衅,這個(gè)項(xiàng)目居然好像沒(méi)有人在維護(hù)了镀层,自動(dòng)2012年9月release一個(gè)版本后,再?zèng)]有更新了船逮,寒心啊顾腊。
dbunit有一個(gè)大大的BUG,即會(huì)解釋不了MySQL表的結(jié)構(gòu)傻唾,在使用@DataSet準(zhǔn)備數(shù)據(jù)時(shí)投慈,會(huì)拋出類(lèi)似如下的異常:
Caused by: org.unitils.core.UnitilsException: Error while executing DataSetLoadStrategy
at org.unitils.dbunit.datasetloadstrategy.impl.BaseDataSetLoadStrategy.execute(BaseDataSetLoadStrategy.java:46)
at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:230)
at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:153)
... 35 more
Caused by: org.dbunit.dataset.NoSuchColumnException: t_upload_file.ID - (Non-uppercase input column: id) in ColumnNameToIndexes cache map. Note that the map's column names are NOT case sensitive.
at org.dbunit.dataset.AbstractTableMetaData.getColumnIndex(AbstractTableMetaData.java:117)
at org.dbunit.operation.AbstractOperation.getOperationMetaData(AbstractOperation.java:89)
at org.dbunit.operation.AbstractBatchOperation.execute(AbstractBatchOperation.java:140)
at org.dbunit.operation.CompositeOperation.execute(CompositeOperation.java:79)
at org.unitils.dbunit.datasetloadstrategy.impl.CleanInsertLoadStrategy.doExecute(CleanInsertLoadStrategy.java:45)
at org.unitils.dbunit.datasetloadstrategy.impl.BaseDataSetLoadStrategy.execute(BaseDataSetLoadStrategy.java:44)
... 37 more
網(wǎng)上有很多痛苦的人在苦苦尋答案,但都依舊痛苦著...
http://zfanxu.iteye.com/blog/1508339
http://zfanxu.iteye.com/blog/1508339
其實(shí)這是dbunit的一個(gè)BUG冠骄,好像很多版本都有這個(gè)問(wèn)題伪煤,報(bào)告說(shuō)解決了,其實(shí)并沒(méi)有解決凛辣。我使用最新的2.4.9的版本照樣會(huì)拋出這個(gè)問(wèn)題抱既。
解決辦法
碰到問(wèn)題光抱怨是沒(méi)有用的,又不能指望dbunit的作者改扁誓,只能自己著騰了防泵。按照網(wǎng)上的幾篇文章改了dbunit的源碼,重新編譯上傳到自己的Maven私服上蝗敢。終于解決了捷泞。
為了避免大家再重新更改編譯,我把已經(jīng)解譯好的dbunit jar放在附件中寿谴,大家需要的話可以下載使用锁右。
繼續(xù)...
最近又在整基于DB2的unitils框架,發(fā)現(xiàn)又出現(xiàn)問(wèn)題了,結(jié)果再次好好跟蹤了unitils及dbunit的源碼咏瑟,終于有了顛覆性的重大發(fā)現(xiàn):
原來(lái)網(wǎng)上一直說(shuō)的是DBUNIT框架導(dǎo)致這個(gè)問(wèn)題的說(shuō)明是錯(cuò)誤的拂到,真正的錯(cuò)誤是unitils框架的錯(cuò)誤!码泞!
因?yàn)镈BUNIT已經(jīng)為不同數(shù)據(jù)庫(kù)提供了不同的接口實(shí)現(xiàn):
org.dbunit.database.IMetadataHandler
而unitils(具體地說(shuō)是DbUnitModule模塊)不管你什么數(shù)據(jù)庫(kù)兄旬,它統(tǒng)一使用這個(gè)類(lèi):
org.dbunit.database.DefaultMetadataHandler
如果數(shù)據(jù)庫(kù)不特殊,當(dāng)然用DefaultMetadataHandler這個(gè)沒(méi)有問(wèn)題余寥,如果特殊领铐,則就取不到數(shù)據(jù)庫(kù)的Metadata信息了,結(jié)果異常就發(fā)生了宋舷。
但是罐孝,目前的DBUnit的Db2MetadataHandler確實(shí)是有BUG的,所以我的解決方法是:
1)復(fù)寫(xiě)了unitils的DbUnitModule實(shí)現(xiàn)類(lèi)肥缔;
2)復(fù)寫(xiě)了dbunit的Db2MetadataHandler實(shí)現(xiàn)類(lèi)莲兢;
3)配置unitils的配置文件,應(yīng)用這些自定義的實(shí)現(xiàn)類(lèi)续膳。
package com.ridge.test.unitils.ext;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DefaultMetadataHandler;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.datatype.IDataTypeFactory;
import org.dbunit.dataset.filter.ITableFilterSimple;
import org.dbunit.ext.db2.Db2DataTypeFactory;
import org.dbunit.ext.db2.Db2MetadataHandler;
import org.dbunit.ext.mysql.MySqlDataTypeFactory;
import org.dbunit.ext.mysql.MySqlMetadataHandler;
import org.unitils.core.UnitilsException;
import org.unitils.core.dbsupport.DbSupport;
import org.unitils.core.dbsupport.DefaultSQLHandler;
import org.unitils.core.dbsupport.SQLHandler;
import org.unitils.dbunit.DbUnitModule;
import org.unitils.dbunit.util.DbUnitDatabaseConnection;
import javax.sql.DataSource;
import static org.dbunit.database.DatabaseConfig.FEATURE_BATCHED_STATEMENTS;
import static org.dbunit.database.DatabaseConfig.PROPERTY_DATATYPE_FACTORY;
import static org.dbunit.database.DatabaseConfig.PROPERTY_ESCAPE_PATTERN;
import static org.unitils.core.dbsupport.DbSupportFactory.getDbSupport;
import static org.unitils.core.util.ConfigUtils.getInstanceOf;
/**
* @author : chenxh(quickselect@163.com)
* @date: 13-10-9
*/
public class MyDbunitModule extends DbUnitModule {
protected DbUnitDatabaseConnection createDbUnitConnection(String schemaName) {
// A DbSupport instance is fetched in order to get the schema name in correct case
DataSource dataSource = getDatabaseModule().getDataSourceAndActivateTransactionIfNeeded();
SQLHandler sqlHandler = new DefaultSQLHandler(dataSource);
DbSupport dbSupport = getDbSupport(configuration, sqlHandler, schemaName);
// Create connection
DbUnitDatabaseConnection connection = new DbUnitDatabaseConnection(dataSource, dbSupport.getSchemaName());
DatabaseConfig config = connection.getConfig();
// Make sure that dbunit's correct IDataTypeFactory, that handles dbms specific data type issues, is used
IDataTypeFactory dataTypeFactory = getInstanceOf(IDataTypeFactory.class, configuration, dbSupport.getDatabaseDialect());
config.setProperty(PROPERTY_DATATYPE_FACTORY, dataTypeFactory);
// Make sure that table and column names are escaped using the dbms-specific identifier quote string
if (dbSupport.getIdentifierQuoteString() != null)
config.setProperty(PROPERTY_ESCAPE_PATTERN, dbSupport.getIdentifierQuoteString() + '?' + dbSupport.getIdentifierQuoteString());
// Make sure that batched statements are used to insert the data into the database
config.setProperty(FEATURE_BATCHED_STATEMENTS, "true");
// Make sure that Oracle's recycled tables (BIN$) are ignored (value is used to ensure dbunit-2.2 compliancy)
config.setProperty("http://www.dbunit.org/features/skipOracleRecycleBinTables", "true");
//注意這兒:根據(jù)不同的數(shù)據(jù)庫(kù)(unitils的database.dialect配置參數(shù))為dbunit
//指定使用不同的IMetadataHandler實(shí)現(xiàn)(其它數(shù)據(jù)庫(kù)都可以用默認(rèn)的改艇,還有一個(gè)Netezza也是特別的,這里忽略了)
if("db2".equalsIgnoreCase(configuration.getProperty("database.dialect"))){
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
new Db2DataTypeFactory());
//由于dbunit自身提供的Db2MetadataHandler有BUG坟岔,所以這里使用自己寫(xiě)的
//MyDb2MetadataHandler,源碼在后面了谒兄。
config.setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER,
new MyDb2MetadataHandler());
}else if("mysql".equalsIgnoreCase(configuration.getProperty("database.dialect"))){
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
new MySqlDataTypeFactory());
config.setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER,
new MySqlMetadataHandler());
}
return connection;
}
}
下面是MyDb2MetadataHandler的源碼:
package com.ridge.test.unitils.ext;
import org.dbunit.ext.db2.Db2MetadataHandler;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.dbunit.util.SQLHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author : chenxh(quickselect@163.com)
* @date: 13-10-9
*/
public class MyDb2MetadataHandler extends Db2MetadataHandler {
private static final Logger logger = LoggerFactory.getLogger(MyDb2MetadataHandler.class);
public ResultSet getTables(DatabaseMetaData metaData, String schemaName, String[] tableType)
throws SQLException
{
if(logger.isTraceEnabled())
logger.trace("tableExists(metaData={}, schemaName={}, tableType={}) - start",
new Object[] {metaData, schemaName, tableType} );
return metaData.getTables(null, schemaName, "%", tableType);
}
public boolean tableExists(DatabaseMetaData metaData, String schema, String tableName)
throws SQLException
{
ResultSet tableRs = metaData.getTables(null, schema, tableName, null);
try
{
return tableRs.next();
}
finally
{
SQLHelper.close(tableRs);
}
}
public ResultSet getColumns(DatabaseMetaData databaseMetaData, String schemaName, String tableName)
throws SQLException {
// Note that MySQL uses the catalogName instead of the schemaName, so
// pass in the given schema name as catalog name (first argument).
ResultSet resultSet = databaseMetaData.getColumns(
null, schemaName, tableName, "%");
return resultSet;
}
public boolean matches(ResultSet columnsResultSet, String catalog,
String schema, String table, String column,
boolean caseSensitive) throws SQLException
{
String catalogName = columnsResultSet.getString(1);
String schemaName = columnsResultSet.getString(2);
String tableName = columnsResultSet.getString(3);
String columnName = columnsResultSet.getString(4);
// MYSQL provides only a catalog but no schema
if(schema != null && schemaName == null && catalog==null && catalogName != null){
logger.debug("Switching catalog/schema because the are mutually null");
schemaName = catalogName;
catalogName = null;
}
boolean areEqual =
areEqualIgnoreNull(table, tableName, caseSensitive) &&
areEqualIgnoreNull(column, columnName, caseSensitive);
return areEqual;
}
private boolean areEqualIgnoreNull(String value1, String value2,
boolean caseSensitive) {
return SQLHelper.areEqualIgnoreNull(value1, value2, caseSensitive);
}
}
最后一步,更改unitils.properties的配置:
...
unitils.module.dbunit.className=com.ridge.test.unitils.ext.MyDbunitModule
...
總結(jié)一下
采用前面的解決方案只能解決mysql的問(wèn)題社付,且直接改dbunit的源碼承疲,是不好的方案,現(xiàn)在我把它廢棄了鸥咖,大家就不要了燕鸽。
采用第二種方案吧,是優(yōu)雅的解決方案啼辣,沒(méi)有更改dbunit的源碼啊研,僅通過(guò)unitils的擴(kuò)展配置實(shí)現(xiàn)了,所以你不要下載附件的dbunit-2.4.8.2.jar了鸥拧,直接使用最新的dbunit版本吧:
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.4.9</version>>
</exclusions>
</dependency>
這個(gè)問(wèn)題啊党远,讓我死幾回的心都有了,現(xiàn)在終于解決了富弦,希望對(duì)大家有幫助沟娱!