【问题标题】:Hibernate Validator not triggered as Spring Repository.save() is invoked in unit tests在单元测试中调用 Spring Repository.save() 时未触发 Hibernate Validator
【发布时间】:2018-12-06 12:08:31
【问题描述】:

这是我的实体:

@Builder
@Data
@Entity
@Table(name = "audit_log")
public class AuditEventEntity {
    @Id
    @GeneratedValue
    private UUID id;

    private long createdEpoch;

    @NotNull
    @Size(min = 1, max = 128)
    private String label;

    @NotNull
    @Size(min = 1)
    private String description;
}

这是我的存储库:

@Repository
public interface AuditEventRepository extends PagingAndSortingRepository<AuditEventEntity, UUID> {
}

当我为存储库编写以下单元测试时,即使“标签”字段为空,保存也是成功的!

@DataJpaTest
@RunWith(SpringRunner.class)
public class AuditRepositoryTest {
    @Test
    public void shouldHaveLabel() {
        AuditEventEntity entity = AuditEventEntity.builder()
                .createdEpoch(Instant.now().toEpochMilli())
                .description(RandomStringUtils.random(1000))
                .build();
        assertThat(entity.getLabel()).isNullOrEmpty();
        AuditEventEntity saved = repository.save(entity);
        // Entity saved and didn't get validated!
        assertThat(saved.getLabel()).isNotNull();
        // The label field is still null, and the entity did persist.
    }

    @Autowired
    private AuditEventRepository repository;
}

无论我使用@NotNull 还是@Column(nullable = false),数据库都是使用列上的not null 标志创建的:

Hibernate: create table audit_log (id binary not null, created_epoch bigint not null, description varchar(255) not null, label varchar(128) not null, primary key (id))

我认为验证器会自动工作。我在这里做错了什么?

【问题讨论】:

  • @PospolitaNikita 看起来 Spring 将接受该字段上的任一注释。我都试过了,可以看到 Hibernate 在每种情况下都使用label varchar(128) not null 创建数据库。

标签: java spring validation spring-boot spring-data


【解决方案1】:

我认为验证器会自动工作。我在做什么 错了吗?

您保存了实体,但没有刷新当前实体管理器的状态。
因此尚未执行实体的验证。

您可以参考Hibernate validator FAQ

为什么在我调用 persist() 时我的 JPA 实体没有得到验证?

为什么当我调用 persist() 时我的 JPA 实体没有得到验证?短的 如果您希望验证是,答案是调用EntityManager#flush() 触发。

Hibernate ORM 和其他一些 ORM 尝试批处理尽可能多的操作 访问数据库时可能。实际情况很可能 实体“持久”操作仅在您调用 flush() 或 事务提交。

另外,知道哪个实体将被持久化取决于你的 级联策略和对象图的状态。冲洗是什么时候 Hibernate ORM 识别所有已更改的实体和 需要数据库操作(另见 HHH-8028)。

因此,请使用 JpaRepository.saveAndFlush() 而不是 JpaRepository.save() 来验证实体。
或者在测试类中注入EntityManagerTestEntityManager,调用JpaRepository.save(),然后调用EntityManager/TestEntityManager.flush()

有关信息:

JpaRepository.save() 调用em.persist(entity)/em.merge(entity)
JpaRepository.saveAndFlush() 调用JpaRepository.save() 然后em.flush()


为了能够调用saveAndFlush(),您必须使您的存储库接口扩展JpaRepository,例如:

public interface AuditEventRepository extends  JpaRepository<AuditEventEntity, UUID> {

由于JpaRepository 扩展PagingAndSortingRepository,此更改与您现有的声明保持一致。


我要补充一点,这个断言不是必需的:

assertThat(saved.getLabel()).isNotNull();

您要断言的是 ValidationException 被抛出,并且它可能包含实际的错误消息。

【讨论】:

  • 我尝试使用repository.saveAndFlush(),但我的存储库似乎不存在该方法。我应该从PagingAndSortingRepository 以外的东西继承吗?断言也只是为了证明问题。一旦我明白为什么 save() 没有失败,我就会改变测试。
  • 第一点,我更新了。只需更改接口声明,它应该没问题。对于第二点,在这种情况下很好。
  • 就是这样!从JpaRepository 扩展允许我使用saveAndFlush(),并且验证按预期工作。
猜你喜欢
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 2018-10-10
  • 1970-01-01
  • 2021-07-06
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
相关资源
最近更新 更多