dbunitクラシックのNoSuchColumnException解決の道
11245 ワード
詳細
愚痴をこぼす
dbunitはこんなに多くの人が使っていて、このプロジェクトは意外にも誰もメンテナンスしていないようで、自動2012年9月にreleaseの1つのバージョンをリリースしてから、もう更新していません.寒いですね.
dbunitには、MySQLテーブルの構造を説明できない大きなバグがあり、@DataSetを使用してデータを準備すると、次のような異常が放出されます.
ネット上には多くの苦痛な人が答えを探していますが、依然として苦痛です...
http://zfanxu.iteye.com/blog/1508339
http://bbs.csdn.net/topics/310215234
実はこれはdbunitの1つのBUGで、多くのバージョンがこの問題があるようで、報告は解決したと言って、実は解決していません.私は最新の2.4.9のバージョンを使ってもこの問題を投げ出します.
解決する
問題にぶつかって文句ばかり言っても始まらないし、dbunitの作者が直すことを期待できないので、自分でやるしかない.ネット上のいくつかの文章に従ってdbunitのソースコードを変更し、自分のMaven私服に再コンパイルしてアップロードした.やっと解決した.
コンパイルを再変更しないように、解読したdbunit jarを添付ファイルに入れます.必要ならダウンロードして使用できます.
続行...
最近またDB 2ベースのunitilsフレームワーク全体で、また問題が発生したことを発見して、結果は再びunitilsとdbunitのソースコードをよく追跡して、ついに転覆性の重大な発見がありました:
もともとネット上でずっと言っていたのはDBUNITフレームワークがこの問題を招いた説明が間違っていて、本当の間違いはunitilsフレームワークの間違いです!!
DBUNITは、異なるデータベースに対して異なるインタフェースを実装しているためです.
unitils(具体的にはDbUnitModuleモジュール)は、どのデータベースでもこのクラスを統一的に使用します.
データベースが特殊でなければ、もちろんDefaultMetadataHandlerで問題なく、特殊であればデータベースのMetadata情報が取れなくなり、結果的に異常が発生します.
しかし、現在のDBUnitのDb 2 M etadataHandlerは確かにBUGがあるので、私の解決方法は:
1)unitilsのDbUnitModule実装クラスを複写する.
2)dbunitを複写したDb 2 M etadataHandler実装クラス;
3)unitilsのプロファイルを構成し、これらのカスタム実装クラスを適用する.
以下はMyDb 2 M etadataHandlerのソースコードです.
最後にunitilsを変更します.propertiesの構成:
まとめ
前の解決策を採用するのはmysqlの問題しか解決できず、dbunitのソースコードを直接変更するのはよくない案ですが、今はそれを廃棄して、みんなは要りません.
2つ目のソリューションを採用しましょう.優雅なソリューションです.dbunitのソースコードを変更せず、unitilsの拡張構成だけで実現しました.だから、添付ファイルのdbunit-2.4.8.2.2をダウンロードしないでください.jarです.最新のdbunitバージョンを直接使用しましょう.
この问题、私を何回死なせる心はすべてあって、今やっと解决して、みんなに役に立つことを望みます! dbunit-2.4.8.2.jar (586.8 KB) ダウンロード回数:71
愚痴をこぼす
dbunitはこんなに多くの人が使っていて、このプロジェクトは意外にも誰もメンテナンスしていないようで、自動2012年9月にreleaseの1つのバージョンをリリースしてから、もう更新していません.寒いですね.
dbunitには、MySQLテーブルの構造を説明できない大きなバグがあり、@DataSetを使用してデータを準備すると、次のような異常が放出されます.
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
ネット上には多くの苦痛な人が答えを探していますが、依然として苦痛です...
http://zfanxu.iteye.com/blog/1508339
http://bbs.csdn.net/topics/310215234
実はこれはdbunitの1つのBUGで、多くのバージョンがこの問題があるようで、報告は解決したと言って、実は解決していません.私は最新の2.4.9のバージョンを使ってもこの問題を投げ出します.
解決する
問題にぶつかって文句ばかり言っても始まらないし、dbunitの作者が直すことを期待できないので、自分でやるしかない.ネット上のいくつかの文章に従ってdbunitのソースコードを変更し、自分のMaven私服に再コンパイルしてアップロードした.やっと解決した.
コンパイルを再変更しないように、解読したdbunit jarを添付ファイルに入れます.必要ならダウンロードして使用できます.
続行...
最近またDB 2ベースのunitilsフレームワーク全体で、また問題が発生したことを発見して、結果は再びunitilsとdbunitのソースコードをよく追跡して、ついに転覆性の重大な発見がありました:
もともとネット上でずっと言っていたのはDBUNITフレームワークがこの問題を招いた説明が間違っていて、本当の間違いはunitilsフレームワークの間違いです!!
DBUNITは、異なるデータベースに対して異なるインタフェースを実装しているためです.
org.dbunit.database.IMetadataHandler
unitils(具体的にはDbUnitModuleモジュール)は、どのデータベースでもこのクラスを統一的に使用します.
org.dbunit.database.DefaultMetadataHandler
データベースが特殊でなければ、もちろんDefaultMetadataHandlerで問題なく、特殊であればデータベースのMetadata情報が取れなくなり、結果的に異常が発生します.
しかし、現在のDBUnitのDb 2 M etadataHandlerは確かにBUGがあるので、私の解決方法は:
1)unitilsのDbUnitModule実装クラスを複写する.
2)dbunitを複写したDb 2 M etadataHandler実装クラス;
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([email protected])
* @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;
}
}
以下はMyDb 2 M etadataHandlerのソースコードです.
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([email protected])
* @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のソースコードを直接変更するのはよくない案ですが、今はそれを廃棄して、みんなは要りません.
2つ目のソリューションを採用しましょう.優雅なソリューションです.dbunitのソースコードを変更せず、unitilsの拡張構成だけで実現しました.だから、添付ファイルのdbunit-2.4.8.2.2をダウンロードしないでください.jarです.最新のdbunitバージョンを直接使用しましょう.
org.dbunit
dbunit
2.4.9 >
この问题、私を何回死なせる心はすべてあって、今やっと解决して、みんなに役に立つことを望みます!