DCO
DCO

Reputation: 1292

SpringBoot Bean Validation annotation

I am currently writing some @SpringBootTest for bean validation in services.

@Data
@Document
public final class Supplier {

    @Id
    @NotEmpty
    private String supplierId;
    ...
    @NotEmpty
    private String hash;
    ....
}

Test

Annotated with:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.MOCK)

and

 @Test
    void testValidation() {
        Supplier invalidSupplier = SupplierTestDataUtil.createSupplier("1234");
        invalidSupplier.setSupplierId(null);

        //works
        assertThrows(ConstraintViolationException.class, () -> supplierService.publish(invalidSupplier));

        //works
        assertThrows(ConstraintViolationException.class, () -> supplierService.persist(invalidSupplier));
        
        //works not
        assertThrows(ConstraintViolationException.class, () -> supplierService.saveAndPublish(invalidSupplier));

        //works
        assertThrows(ConstraintViolationException.class, () -> supplierService.delete(invalidSupplier));
    }

Service:

@Transactional
public Supplier saveAndPublish(@NotNull Supplier supplier) {
    supplier.setHash(messageDigester.digest(supplier));
    Supplier persisted = persist(supplier);
    publish(supplier);
    return persisted;
}

@Transactional
public Supplier persist(@Valid @NotNull Supplier supplier) {
    return repository.save(supplier);
}

Supplier at saveAndFlush must not be valid at this point because required hash will be generated and set inside that method. Nevertheless my expectation was that ConstraintViolationException will be also thrown because I also call persist and publish method and pass that invalid document.

My point is that you can bypass BeanValidation within the same class.

Upvotes: 1

Views: 249

Answers (1)

qutax
qutax

Reputation: 898

The ConstraintViolationException will be thrown by a Spring Validator which won't be used if you call persist "manually". If persist is called from a Spring context, the @Valid annotation will tell Spring to validate the object based on the class's validation constraints.

You could:

  1. Validate hash always "manually" (without the @NotEmpty annotation)
  2. Call supplier.setHash(messageDigester.digest(supplier)); before calling saveAndPublish and add the @Valid annotation to the Supplier argument of saveAndPublish
  3. Add a Validator instance as a field to your service and call it manually on the Supplier to be validated (in case of saveAndPublish after setting the hash, of course)
  4. Implement two different types of Supplier, e.g, AddSupplier (without hash being validated; used by saveAndPublish) and EditSupplier (with hash being validated; used by persist)

(this is probably an incomplete list)

Upvotes: 1

Related Questions