The Spring validation @NotNull does not validate

blueSky picture blueSky · Jul 31, 2018 · Viewed 11.2k times · Source

I’m trying to validate a POST request using Spring Validator. This is the Object I’m trying to validate:

@Validated
public class Request implements Serializable {
    private static final long serialVersionUID = 1L;

    private Long id1;
    private Long id2;   
    private String s;   
    private List<Mapping> mappings;

    public Request() {}
    public Request (Long id1, Long id2, String s, List<Mapping> mappings) {
        this.id1 = id1;
        this.id2 = id2;
        this.s = s;
        this.mappings = mappings;
    }

    //getter() and setter() methods.
}

And this is the Mapping class:

@Validated
public class Mapping implements Serializable {
    private static final long serialVersionUID = 1L;
    @JsonProperty(value="id")
    private Long id;

    @JsonProperty(value="string")
    private String string;

    @JsonProperty(value="account_id")
    @NotNull
    private String accountId;

    @JsonProperty(value="last_updated_at")
    @NotNull    
    private OffsetDateTime lastUpdatedAt;

    // Getter and setters()
    // I'll skip the getter() and setter() for id and string fields.
    @Override
    public String getAccountId() {
        return accountId;
    }

    @Override
    public void setAccountId (@NotNull String accountId) {
        this.accountId = accountId;
    }

    @Override
    public OffsetDateTime getLastUpdatedAt() {
        return lastUpdatedAt;
    }

    @Override
    public void setLastUpdatedAt(@NotNull OffsetDateTime lastUpdatedAt) {
        this.lastUpdatedAt = lastUpdatedAt;
    }
}

This is the message being posted:

[Request [id1=null, id2=null, s=6eq2J6, mappings=

[Mapping [id=2779, string=6eq2J6, accountId=null, lastUpdatedAt=null]]]]

This is how I receive the request, but the bindingResult.hasErrors() is always empty. The pushRequest is exactly as it was sent.

@RestController
@RequestMapping(value="/${path}", method={RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT})
@Validated
public class MappingController {

@PostMapping(value="/", consumes={"application/json"})
public void processMappings (@Valid @RequestBody Optional<Request> pushRequest, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        log.info("Error!");
    }
}
}

Why the @NotNull for accountId and lastUpdatedAt does not make the validation to fail when they have Null value?

I've tried to find the solution by searching this error, the solutions was to add @Validated to the Request, Mapping and MappingController classes, which did not solve the problem.

Another solution was to add these Beans to the Main() class, which did not solve the problem.

public class Main {
    public static void main(String[] args) {        
        SpringApplication.run(Main.class, args);        
        //System.out.println("version: " + SpringVersion.getVersion()); // version: 5.1.0.RC1
    }

    @Bean
    public javax.validation.Validator localValidatorFactoryBean() {
       return new LocalValidatorFactoryBean();
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
                MethodValidationPostProcessor mvProcessor = new MethodValidationPostProcessor();
                mvProcessor.setValidator(validator());
                return mvProcessor;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
                    LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
                    validator.setProviderClass(HibernateValidator.class);
                    validator.afterPropertiesSet();
                    return validator;
    }
}

Answer

blueSky picture blueSky · Jul 31, 2018

I think I found the answer thanks to this post:

jsr-303-valid-annotation-nested-object-not-working

So as soon as I added the @Valid to these fields:

@Valid
private List<Mapping> mappings;

And these fields inside Mapping:

@JsonProperty(value="account_id")
    @Valid
    @NotNull
    private String accountId;

    @JsonProperty(value="last_updated_at")
    @Valid
    @NotNull    
    private OffsetDateTime lastUpdatedAt;

The validation started working.

Also I removed the @NotNull in the setter() methods:

Setters() like this:

@Override
    public void setAccountId (String accountId) {
        this.accountId = accountId;
    }    

    @Override
    public void setLastUpdatedAt(OffsetDateTime lastUpdatedAt) {
        this.lastUpdatedAt = lastUpdatedAt;
    }

Also my code worked without any of the Validator beans which I added to Main().