运行 Spring 引导 JPA 测试时,数据库中没有保留任何值



我正在寻找spring-data-jpa中映射列默认值的解决方案,这使我找到了@ColumnDefault注释的文档;我想尝试一下。所以我更新了一个需要默认值的实体(今天这是通过架构处理的.sql我们在其中定义额外的列行为),如下所示:

@Entity
@Getter
@Setter
@ToString
@DynamicInsert
@NoArgsConstructor
@Relation(collectionRelation = "customers")
public class Customer extends Organisation implements Identifiable<Long>, Serializable {
private static final long serialVersionUID = 8101819808147191270L;
@Column(nullable = false, updatable = false, length = 3)
@ColumnDefault("'INR'")
private String currency;
@Column(scale = 2)
@ColumnDefault("0.10")
private Double tds;
@Column(nullable = false, updatable = false, length = 3)
@ColumnDefault("'INV'")
private String invoicePrefix;

现在,由于我使用的是@ColumnDefault我希望为列配置默认值,当我查看数据库时,它们就是默认值。但是,当我运行测试以插入值时;默认值字段为null。我为测试配置了Postgresql(因为生产将在Postgresql中);虽然Postgresql中表的DDL显示默认值被添加到 表定义

CREATE TABLE data_api_it.customer
(
currency character varying(3) COLLATE pg_catalog."default" NOT NULL DEFAULT 'INR'::character varying,
invoice_prefix character varying(3) COLLATE pg_catalog."default" NOT NULL DEFAULT 'INV'::character varying,
tds double precision DEFAULT 0.10,
id bigint NOT NULL,
CONSTRAINT customer_pkey PRIMARY KEY (id),
CONSTRAINT fk3afgab8nfy6ykn6b70uuh9v59 FOREIGN KEY (id)
REFERENCES data_api.organisation (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)

这是失败的测试

@Slf4j
@RunWith(SpringRunner.class)
@DataJpaTest(showSql = false)
@ContextConfiguration(classes = { DataApiJpaConfiguration.class })
@TestPropertySource(locations = {"classpath:application-it.properties","classpath:application-test.properties"})
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class CustomerRepositoryIntegrationTest {
@Autowired
private CustomerRepository customerRepository;
@Test
public void testPrePersistAddsMandatoryFields() {
Customer bhau = new Customer();
bhau.setName("Bhau & Sons Pvt. Ltd.");
bhau.setDomain(RandomStringUtils.randomAlphanumeric(8));
bhau = customerRepository.saveAndFlush(bhau);
Customer badaBhau = customerRepository.findById(bhau.getId()).get();
assertThat(badaBhau.getTds().doubleValue()).isEqualTo(0.10);
assertThat(badaBhau.getInvoicePrefix()).isEqualTo("INV");
assertThat(badaBhau.getCurrency()).isEqualTo("INR");
}
}

失败是一个NullPointerExceptionassertThat(badaBhau.getTds().doubleValue()).isEqualTo(0.10);当我在调试中运行时,我注意到badaBhau确实没有设置从数据库查询时的默认值。然后,当它持续bhau时,我在行之后暂停了;浏览了已配置的测试数据库,并注意到bhau实体甚至没有首先保存到数据库中。

我还使用 Postgesql Dev DB 运行了该应用程序,尝试点击 API URL 以使用 JSON 数据保存客户

{
"name":"Minty and Sons Pvt. Ltd.",
"pan": "AASONAL123",
"domain": "xy123456"
}

此调用成功创建记录,当然,默认值分别为INRINV0.10分别用于currencyinvoice_prefixtds

因此,虽然我理解并喜欢@ColumnDefault如何为我解决默认值问题;我对测试失败的原因(或者我做错了什么)感到非常困惑

由于 Hibernate 的第一级缓存,bhaubadaBhau将是同一个实例,即不会有以下因素触发的数据库查找: 相反,具有指定 ID 的客户将从一级缓存中检索。可以通过启用 SQL 日志记录来验证这一点。

Customer badaBhau = customerRepository.findById(bhau.getId()); //no db lookup

要获得该值,可以通过清除持久性上下文或刷新持久性实例来强制数据库查找。

public class MyTestClass {
@PersistenceContext
private EntityManager em;
@Test
public void testDefaultFieldsArePopulated() {
Customer bhau = new Customer();
bhau.setName("Bhau & Sons Pvt. Ltd.");
bhau.setDomain(RandomStringUtils.randomAlphanumeric(8));
bhau = customerRepository.saveAndFlush(bhau);
em.clear(); //db lookup will now happen
Customer badaBhau = customerRepository.findById(bhau.getId());
assertThat(badaBhau.getTds().doubleValue()).isEqualTo(0.10);
assertThat(badaBhau.getInvoicePrefix()).isEqualTo("INV");
assertThat(badaBhau.getCurrency()).isEqualTo("INR");
}
}

https://howtodoinjava.com/hibernate/understanding-hibernate-first-level-cache-with-example/

Crizzis 在他的答案中也提出了一个有效的观点,随着测试的通过,看起来 Postgres 正在用列的默认值替换空值。由于您可能无法在不同的数据库引擎中依赖此功能,因此您还可以考虑在您的实体上使用 Hibernates@DynamicInsert注释,这将创建一个仅设置非空字段的插入语句。

https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/DynamicInsert.html

对于插入,此实体是否应使用动态 sql 生成,其中 在准备好的 SQL 语句中只引用非空列?

我的部分答案将是@AlanHay已经提到的。

即使你听从了他的建议,我仍然希望你的测试失败。@ColumnDefault的目的是在执行 DLL 时将指定的默认值与列定义一起使用,不多也不少。默认值表示:"只要未在INSERT语句中指定列的值,请改用以下值"。

问题是,您正在为列指定一个值,因为您的列不会使用insertable = falseINSERT语句中排除。即使您没有为属性设置值,Hibernate也会发送显式NULL。您的方法可能适用于currencyindexPrefix,因为您将它们标记为nullable = false(这是特定于数据库的,您必须查阅文档以了解 Postgres 如何处理这种情况)。我非常怀疑它是否适用于tds列,因为在这种情况下NULL是一个有效的值。

最新更新