最近使用dbunit-2.4.8 + Unitils 3.3做DAO层数据库测试的时候
出现如下错误:
org.unitils.core.UnitilsException: Error inserting test data from DbUnit dataset for method public void org.zfanxu.test.UserDaoTestWithUnitils.testGetUserById() at org.unitils.dbunit.DbUnitModule.insertDataSet(DbUnitModule.java:156) at org.unitils.dbunit.DbUnitModule$DbUnitListener.beforeTestSetUp(DbUnitModule.java:557) at org.unitils.core.Unitils$UnitilsTestListener.beforeTestSetUp(Unitils.java:273) at org.unitils.UnitilsJUnit4TestClassRunner$TestListenerInvokingMethodRoadie.runBeforesThenTestThenAfters(UnitilsJUnit4TestClassRunner.java:151) at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84) at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49) at org.unitils.UnitilsJUnit4TestClassRunner.invokeTestMethod(UnitilsJUnit4TestClassRunner.java:95) at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:61) at org.unitils.UnitilsJUnit4TestClassRunner.access$000(UnitilsJUnit4TestClassRunner.java:44) at org.unitils.UnitilsJUnit4TestClassRunner$1.run(UnitilsJUnit4TestClassRunner.java:62) at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34) at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44) at org.unitils.UnitilsJUnit4TestClassRunner.run(UnitilsJUnit4TestClassRunner.java:68) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 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) ... 18 more Caused by: org.dbunit.dataset.NoSuchColumnException: user.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) ... 20 more
于是在网上google下得知是dbunit的一个bug:http://stackoverflow.com/questions/2210429/dbunit-confusion-over-case-sensitivity-on-table-column-names
google上也基本没有可行的解决方案!
后来我通过修改dbunit的源代码得以解决,错误的原因是unitils默认使用的是“DefaultMetadataHandler.java”这个类去加载数据库信息,从而得不到数据库schema的值
DefaultMetadataHandler.java
boolean areEqual = areEqualIgnoreNull(catalog, catalogName, caseSensitive) && areEqualIgnoreNull(schema, schemaName, caseSensitive) && areEqualIgnoreNull(table, tableName, caseSensitive) && areEqualIgnoreNull(column, columnName, caseSensitive);
这个时候的schemaName是空的,但是传进来的schema是有值的,从而报错!
所以解决方法如下:
方法1. 修改DefaultMetadataHandler.java文件将上述代码换成如下代码
boolean areEqual = areEqualIgnoreNull(table, tableName, caseSensitive) && areEqualIgnoreNull(column, columnName, caseSensitive);
对,我把catalog和schema的比较都删掉了!
方法2: 因为我使用的是mysql数据库,我直接把dbunit中的类"MySqlMetadataHandler"覆盖掉原来的DefaultMetadataHandler.java文件,同样能解决问题
最后,也来个抛砖引玉,如果有人有更好的解决方法或是可以不通过修改源代码解决的,可以回复我! :-D