Bean Validation constraint tester
Which constraints let null through, and where @NotEmpty stops and@NotBlank begins. The grid below is generated by running each constraint against each value, so it is the behaviour rather than a description of it.
The comparison
✓ passes ✗ fails — would not compile on that type
Test your own value
The rule behind most of the surprises
Every constraint except @NotNull, @NotEmpty and@NotBlank treats null as valid. A field annotated only@Size(min = 8) accepts a missing value. @Email accepts a missing value. @AssertTrue on a Boolean accepts a missing value.
This is deliberate: it lets one annotation express the format and another express whether the field is required, so an optional field can still be checked when present. It means a required field usually needs two:
@NotNull
@Size(min = 8, max = 64)
private String password;Choosing between the three
| Constraint | Applies to | Use when |
|---|---|---|
@NotNull | Anything | The field must be present. Numbers, booleans, objects, dates. |
@NotEmpty | String, Collection, Map, array | A list must have at least one element. |
@NotBlank | String only | Almost every required text field a user types into. |
For a text field submitted from a form, @NotBlank is nearly always the one you want. @NotEmpty accepts a string of spaces, which passes validation and then reaches the database as meaningless data.
Two more that catch people out
@Emailaccepts the empty string. An untouched email input posts"", which passes. Add@NotBlankwhen the address is required.@Patternanchors at both ends — the regex must match the whole value, not a part of it. It does test the empty string, unlike@Email.
Making it actually run
Annotations do nothing on their own. In a controller the parameter needs@Valid, and for nested objects each field needs it too:
@PostMapping("/orders")
ResponseEntity<Void> create(@Valid @RequestBody OrderRequest request) { ... }
record OrderRequest(
@NotBlank String customer,
@Valid @NotNull Address address // @Valid recurses into Address
) {}On a Spring bean method, validation needs @Validated on the class. Without one of these, the constraints are inert and everything passes.
Which package
Spring Boot 3 moved to Jakarta EE, so the import isjakarta.validation.constraints.*. Spring Boot 2 usedjavax.validation.constraints.*. Mixing them means the annotations compile and are then ignored, which looks exactly like validation not being switched on.