【问题标题】:DBUnit & Postgres UUID Primary KeyDBUnit & Postgres UUID 主键
【发布时间】:2015-03-04 23:08:04
【问题描述】:

我正在尝试使用 DBUnit 运行集成测试,但是我发现自己无法插入主键列,这显然不适用于稍后在文件中引用主键的外键。

例如,我有以下 DDL:

CREATE TABLE attributes(
    attribute_id UUID NOT NULL DEFAULT uuid_generate_v4(),
    attribute VARCHAR(64) NOT NULL,
    description TEXT NOT NULL,
    PRIMARY KEY(attribute_id)
);

DBUnit 设置 XML 如下所示:

<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
    <attributes attribute_id="233bc966-4fcd-4b46-88e6-3e07090f322d" attribute="Empathy" description="Empathy Description" />
</dataset>

当我尝试运行测试时,我得到了失败:

    org.dbunit.dataset.NoSuchColumnException: attributes.ATTRIBUTE_ID -  (Non-uppercase input column: attribute_id) in ColumnNameToIndexes cache map. Note that the
ap's column names are NOT case sensitive.

这是正在运行的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@DbUnitConfiguration(dataSetLoader = TestConfiguration.FlatXmlDataLoaderProxy.class)
@ContextConfiguration(classes = {ApplicationConfiguration.class, TestConfiguration.class})
@TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class
})
public class ApplicationAssessmentJobTest {
    @Autowired
    private ApplicationAssessmentJob applicationAssessmentJob;

    @Test
    @DatabaseSetup("/dbunit/ApplicationAssessmentJobTestSetup.xml")
    @DatabaseTearDown("dbunit/ApplicationAssessmentJobTestTearDown.xml")
    public void testJob() {
        ApplicationAssessmentJobModel model = new ApplicationAssessmentJobModel();
        model.setApplicationId(UUID.fromString("41fa1d51-c1ee-482b-80a7-a6eefda64436"));

        applicationAssessmentJob.receiveMessage(model);
    }
}

显示的此错误似乎与根本问题没有直接关系。如果我从 XML 中删除 attribute_id 列,则会插入记录。

【问题讨论】:

  • 也许我来不及了,但是 DbUnit 不会识别任何供应商特定的数据类型(例如 uuid),除非您明确地 use the vendor-specific data-type factory。在您的情况下,您应该使用 org.dbunit.ext.postgresql.PostgresqlDataTypeFactory 的实例。

标签: postgresql dbunit spring-test-dbunit


【解决方案1】:

我遇到了同样的问题,我终于找到了解决方案。 如文档中所述,您需要覆盖默认的 dbunit DatabaseConfig 以设置特定于 PostgreSQL 的 IDataTypeFactory。

这是我的测试配置:

@Autowired
private DataSource dataSource;

@Bean
public DatabaseConfigBean databaseConfigBean() {
    DatabaseConfigBean databaseConfigBean = new DatabaseConfigBean();
    databaseConfigBean.setDatatypeFactory(new PostgresqlDataTypeFactory());
    return databaseConfigBean;
}

@Bean(name = "dbUnitDatabaseConnection")
public DatabaseDataSourceConnectionFactoryBean dbUnitDatabaseConnection() throws SQLException, DatabaseUnitException, IOException {
    DatabaseDataSourceConnectionFactoryBean databaseDataSourceConnectionFactoryBean = new DatabaseDataSourceConnectionFactoryBean();
    databaseDataSourceConnectionFactoryBean.setDatabaseConfig(databaseConfigBean());
    databaseDataSourceConnectionFactoryBean.setDataSource(dataSource);
    databaseDataSourceConnectionFactoryBean.setSchema("public");
    return databaseDataSourceConnectionFactoryBean;
}

然后,您需要指定您将在测试中使用自定义 IDatabaseConnection。这些连接将由 bean 'dbUnitDatabaseConnection' 创建。例如,这是我声明我的 spring 测试配置的方式:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringRepositoryConfigurationTest.class})
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DbUnitTestExecutionListener.class })
@DbUnitConfiguration(databaseConnection = {"dbUnitDatabaseConnection"})

【讨论】:

    【解决方案2】:

    在与 DBUnit/Spring-Test-DBUnit 斗争了大半天之后,我决定放弃那个库并推出自己的库,因为 DBUnit 似乎只是痛苦而已。

    我能够在不到 30 分钟的时间内用大约 40 个 SLoC 编写设置/拆卸。它使用纯 SQL,这在哲学上更符合我选择的 jOOQ 而不是 Hibernate。当然不太理想,但 15 分钟的搜索并没有找到任何东西来处理在设置和拆卸时运行 SQL 的简单用例。值得注意的是对java.sql.DataSource Bean 的要求,可从ApplicationContext 获得。

    DbInitTestExecutionListener.java

    import org.springframework.core.io.ClassPathResource;
    import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
    import org.springframework.test.context.TestContext;
    import org.springframework.test.context.TestExecutionListener;
    
    import javax.sql.DataSource;
    import java.util.Arrays;
    
    public class DbInitTestExecutionListener implements TestExecutionListener {
        @Override
        public void beforeTestClass(TestContext testContext) throws Exception {}
    
        @Override
        public void prepareTestInstance(TestContext testContext) throws Exception {}
    
        @Override
        public void beforeTestMethod(TestContext testContext) throws Exception {
            DatabaseSetup setup = testContext.getTestMethod().getAnnotation(DatabaseSetup.class);
    
            if (setup != null) {
                if (setup.clearInsert()) {
                    afterTestMethod(testContext);
                }
    
                ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
    
                Arrays.asList(setup.value()).stream()
                        .map(ClassPathResource::new)
                        .forEach(populator::addScript);
    
                populator.execute(testContext.getApplicationContext().getBean(DataSource.class));
            }
        }
    
        @Override
        public void afterTestMethod(TestContext testContext) throws Exception {
            DatabaseTearDown tearDown = testContext.getTestMethod().getAnnotation(DatabaseTearDown.class);
    
            if (tearDown != null) {
                ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
    
                Arrays.asList(tearDown.value()).stream()
                        .map(ClassPathResource::new)
                        .forEach(populator::addScript);
    
                populator.execute(testContext.getApplicationContext().getBean(DataSource.class));
            }
        }
    
        @Override
        public void afterTestClass(TestContext testContext) throws Exception {}
    }
    

    DatabaseTearDown.java

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    public @interface DatabaseTearDown {
        String[] value();
    }
    

    DatabaseSetup.java

    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    public @interface DatabaseSetup {
        boolean clearInsert() default true;
    
        String[] value();
    }
    

    使用最少的测试配置:

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration(classes = {ApplicationConfiguration.class})
    @TestExecutionListeners({
            DependencyInjectionTestExecutionListener.class,
            DirtiesContextTestExecutionListener.class,
            TransactionalTestExecutionListener.class,
            DbInitTestExecutionListener.class
    })
    public class TestClass {
        @Test
        @DatabaseSetup("/dbinit/TestSetup.sql")
        @DatabaseTearDown("/dbinit/TestTearDown.sql")
        public void testJob() {
          // TODO: Add test code here
        }
    }
    

    【讨论】:

      【解决方案3】:

      这个错误信息

      org.dbunit.dataset.NoSuchColumnException: attributes.ATTRIBUTE_ID ...
      

      表明 dbunit 正在寻找名为 ATTRIBUTE_ID 的列。您的 CREATE TABLE 语句会创建一个名为 attribute_id 的列。

      如果 dbunit 使用分隔标识符,PostgreSQL 会将 "ATTRIBUTE_ID""attribute_id" 视为两个不同的标识符。 (在您的情况下,两个不同的列。)如果 dbunit 使用分隔标识符,PostgreSQL 将折叠为小写,它会认为 ATTRIBUTE_IDattribute_id 是相同的标识符 - -attribute_id.

      SQL 标准要求将普通标识符折叠为大写。 PostgreSQL 在这里的行为不是标准的,但不太可能改变。

      看起来 dbunit 可能会自行折叠为大写(遵循 SQL 标准)。如果是这种情况,您可能可以将 dbunit 配置为使用区分大小写的标识符。 (我不知道该怎么做。)

      【讨论】:

      • 这也是我的想法,但是将FlatXmlDataSetLoader 配置为区分大小写并不能解决此问题。值得注意的是,当一个人删除attribute_id 列时,所有其他列都将毫无问题地插入。进一步的测试似乎表明这个问题与列的类型和默认值有关。不幸的是,我根本没有时间去发现根本原因。
      猜你喜欢
      • 2018-10-29
      • 2021-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 1970-01-01
      • 1970-01-01
      • 2013-01-29
      相关资源
      最近更新 更多