DBUnit和Postgres UUID主键

4
我正在尝试使用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 列,则记录将被插入。


2
也许我来晚了,但是除非你明确地使用供应商特定的数据类型工厂(例如uuid),否则DbUnit将无法识别任何供应商特定的数据类型。在您的情况下,您应该使用org.dbunit.ext.postgresql.PostgresqlDataTypeFactory的实例。 - pozs
3个回答

4

我也遇到了同样的问题,最终找到了解决方案。根据文档所述,您需要覆盖默认的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)的工具。值得注意的是,需要从ApplicationContext获得一个java.sql.DataSource Bean。

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
    }
}

0

这个错误信息

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列时,所有其他列都可以被插入而没有问题。进一步的测试似乎表明这个问题与列的类型和默认值有关。不幸的是,我只是缺乏时间去发现根本原因。 - user372743

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接