抱怨
dbunit這麼多人用,這個項目竟然好像沒有人在維護了,自動2012年9月release一個版本後,再沒有更新了,寒心啊。
dbunit有一個大大的BUG,即會解釋不了MySQL表的結構,在使用@DataSet準備數據時,會拋出相似以下的異常:
java
Caused by: org.unitils.core.UnitilsException: Error while executing DataSetLoadStrategy sql
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) ide
... 35 more fetch
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. ui
at org.dbunit.dataset.AbstractTableMetaData.getColumnIndex(AbstractTableMetaData.java:117) spa
at org.dbunit.operation.AbstractOperation.getOperationMetaData(AbstractOperation.java:89) .net
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
網上有不少痛苦的人在苦苦尋答案,但都依舊痛苦着...
http://zfanxu.iteye.com/blog/1508339
http://bbs.csdn.net/topics/310215234
其實這是dbunit的一個BUG,好像不少版本都有這個問題,報告說解決了,其實並無解決。我使用最新的2.4.9的版本照樣會拋出這個問題。
解決
碰到問題光抱怨是沒有用的,又不能期望dbunit的做者改,只能本身着騰了。按照網上的幾篇文章改了dbunit的源碼,從新編譯上傳到本身的Maven私服上。終於解決了。
爲了不你們再從新更改編譯,我把已經解譯好的dbunit jar放在附件中,你們須要的話能夠下載使用。
繼續...
最近又在整基於DB2的unitils框架,發現又出現問題了,結果再次好好跟蹤了unitils及dbunit的源碼,終於有了顛覆性的重大發現:
原來網上一直說的是DBUNIT框架致使這個問題的說明是錯誤的,真正的錯誤是unitils框架的錯誤!!
由於DBUNIT已經爲不一樣數據庫提供了不一樣的接口實現:
org.dbunit.database.IMetadataHandler
而unitils(具體地說是DbUnitModule模塊)無論你什麼數據庫,它統一使用這個類:
org.dbunit.database.DefaultMetadataHandler
若是數據庫不特殊,固然用DefaultMetadataHandler這個沒有問題,若是特殊,則就取不到數據庫的Metadata信息了,結果異常就發生了。
可是,目前的DBUnit的Db2MetadataHandler確實是有BUG的,因此個人解決方法是:
1)複寫了unitils的DbUnitModule實現類;
2)複寫了dbunit的Db2MetadataHandler實現類;
3)配置unitils的配置文件,應用這些自定義的實現類。
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");
//注意這兒:根據不一樣的數據庫(unitils的database.dialect配置參數)爲dbunit
//指定使用不一樣的IMetadataHandler實現(其它數據庫均可以用默認的,還有一個Netezza也是特別的,這裏忽略了)
if("db2".equalsIgnoreCase(configuration.getProperty("database.dialect"))){
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
new Db2DataTypeFactory());
//因爲dbunit自身提供的Db2MetadataHandler有BUG,因此這裏使用本身寫的
//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
...
總結
採用前面的解決方案只能解決mysql的問題,且直接改dbunit的源碼,是很差的方案,如今我把它廢棄了,你們就不要了。
採用第二種方案吧,是優雅的解決方案,沒有更改dbunit的源碼,僅經過unitils的擴展配置實現了,因此你不要下載附件的dbunit-2.4.8.2.jar了,直接使用最新的dbunit版本吧:
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.4.9</version>>
</exclusions>
</dependency>
這個問題啊,讓我死幾次的心都有了,如今終於解決了,但願對你們有幫助!
dbunit-2.4.8.2.jar (586.8 KB)
下載次數: 40