DBUnit 和 Postgres UUID 主键
DBUnit & Postgres UUID Primary Key
我正在尝试使用 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/Spring-Test-DBUnit 斗争了一天的大部分时间之后,我决定废弃该库并推出自己的库,因为 DBUnit 似乎只是痛苦而已。
我能够在不到 30 分钟的时间内写出大约 40 个 SLoC 的 setup/teardown。它使用普通的 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
}
}
这个错误信息
org.dbunit.dataset.NoSuchColumnException: attributes.ATTRIBUTE_ID ...
表明 dbunit 正在查找名为 ATTRIBUTE_ID
的列。您的 CREATE TABLE 语句创建了一个名为 attribute_id
.
的列
如果 dbunit 使用定界标识符,PostgreSQL 将把 "ATTRIBUTE_ID"
和 "attribute_id"
视为两个不同的标识符。 (在你的情况下,两个不同的列。)如果 dbunit 不 使用定界标识符,PostgreSQL 将折叠为小写,并且会考虑 ATTRIBUTE_ID
和 attribute_id
是相同的标识符--attribute_id
.
SQL 标准要求将普通标识符折叠为大写。 PostgreSQL 这里的行为不是标准的,但不太可能改变。
看起来 dbunit 可能会自行折叠成大写字母(遵循 SQL 标准)。如果是这种情况,您可以将 dbunit 配置为使用区分大小写的标识符。 (我不知道该怎么做。)
我遇到了同样的问题,我终于找到了解决办法。
如文档中所述,您需要覆盖默认的 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"})
我正在尝试使用 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/Spring-Test-DBUnit 斗争了一天的大部分时间之后,我决定废弃该库并推出自己的库,因为 DBUnit 似乎只是痛苦而已。
我能够在不到 30 分钟的时间内写出大约 40 个 SLoC 的 setup/teardown。它使用普通的 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
}
}
这个错误信息
org.dbunit.dataset.NoSuchColumnException: attributes.ATTRIBUTE_ID ...
表明 dbunit 正在查找名为 ATTRIBUTE_ID
的列。您的 CREATE TABLE 语句创建了一个名为 attribute_id
.
如果 dbunit 使用定界标识符,PostgreSQL 将把 "ATTRIBUTE_ID"
和 "attribute_id"
视为两个不同的标识符。 (在你的情况下,两个不同的列。)如果 dbunit 不 使用定界标识符,PostgreSQL 将折叠为小写,并且会考虑 ATTRIBUTE_ID
和 attribute_id
是相同的标识符--attribute_id
.
SQL 标准要求将普通标识符折叠为大写。 PostgreSQL 这里的行为不是标准的,但不太可能改变。
看起来 dbunit 可能会自行折叠成大写字母(遵循 SQL 标准)。如果是这种情况,您可以将 dbunit 配置为使用区分大小写的标识符。 (我不知道该怎么做。)
我遇到了同样的问题,我终于找到了解决办法。 如文档中所述,您需要覆盖默认的 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"})