Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate Validator not triggered as Spring Repository.save() is invoked in unit tests

Tags:

This is my entity:

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

And this is my repository:

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

When I write the following unit test for the repository, the save is successful even though the "label" field is null!

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

Whether I use @NotNull or @Column(nullable = false) the database is created with the not null flag on the column:

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

I thought the validators would work automatically. What am I doing wrong here?

like image 477
Paul Avatar asked Jun 27 '18 14:06

Paul


1 Answers

I thought the validators would work automatically. What am I doing wrong here?

You save the entity but you don't flush the state of the current entity manager.
So the validation of the entity is not performed yet.

You can refer to the Hibernate validator FAQ :

Why is my JPA entity not validated when I call persist()?

Why is my JPA entity not validated when I call persist()? The short answer is call EntityManager#flush() if you want the validation to be triggered.

Hibernate ORM and a few other ORMs try to batch as many operations as possible when accessing the database. It is likely that the actual entity "persist" operation only happens when you call flush() or when the transaction commits.

Also, knowing which entity is about to be persisted depends on your cascading strategy and the state of your object graph. Flush is when Hibernate ORM identifies all the entities that have been changed and require database actions (see also HHH-8028).

So use JpaRepository.saveAndFlush() instead of JpaRepository.save() to allow the entity to be validated.
Or alternatively inject an EntityManager or an TestEntityManager in the test class, invoke JpaRepository.save() and then invoke EntityManager/TestEntityManager.flush().

For information :

JpaRepository.save() invokes em.persist(entity)/em.merge(entity)
JpaRepository.saveAndFlush() invokes JpaRepository.save() and then em.flush()


To be able to invoke saveAndFlush(), you have to make your Repository interface extend JpaRepository such as :

public interface AuditEventRepository extends  JpaRepository<AuditEventEntity, UUID> {

As JpaRepository extends PagingAndSortingRepository, this change stays consistent with your existing declaration.


I would add that this assertion is not required :

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

What you want to assert is that ValidationException is thrown and maybe that it contains the actual error message.

like image 85
davidxxx Avatar answered Sep 28 '22 06:09

davidxxx