Everything on this page is standard Jakarta Validation 3.1 — Erasmus adds no proprietary annotation. What is listed here is what the current development line actually supports.
Built-in constraints
21 of the specification’s 22 built-in constraint types are implemented: @NotNull, @NotBlank, @NotEmpty, @Size, @Min, @Max, @DecimalMin, @DecimalMax, @Positive, @PositiveOrZero, @Negative, @NegativeOrZero, @Digits, @Pattern, @Email, @AssertTrue, @AssertFalse, @Past, @PastOrPresent, @Future and @FutureOrPresent (38 validator implementations). The known gap is @Null, which has no validator yet.
Target-type coverage is not complete for every constraint — transcribed from the validator sources:
-
@NotEmptyand@Size—CharSequence,Collection,Mapand arrays; -
@DecimalMin,@DecimalMaxand@Digits—NumberandCharSequence; -
@Minand@Max—Numberonly (noCharSequencevalidator yet); -
@Past,@PastOrPresent,@Futureand@FutureOrPresent—Instant,LocalDateandjava.util.Dateonly (the other temporal types listed by the spec are not covered yet); -
the remaining constraints each target their single spec-defined type.
public class Order {
@NotBlank
private String reference;
@Positive
private int quantity;
@Email
private String contact;
@Future
private java.time.LocalDate deliveryDate;
}
Validating
The three entry points of jakarta.validation.Validator work today:
validator.validate(order); // whole bean
validator.validateProperty(order, "reference"); // one property
validator.validateValue(Order.class, "quantity", -1); // a candidate value
getConstraintsForClass and forExecutables() both throw UnsupportedOperationException today: the constraint metadata API lands with milestone M6, executable (method/constructor) validation with milestone M5.
Custom constraints
Standard authoring — annotation plus ConstraintValidator:
@Target(FIELD)
@Retention(RUNTIME)
@Constraint(validatedBy = PalindromeValidator.class)
public @interface Palindrome {
String message() default "must read the same both ways";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PalindromeValidator implements ConstraintValidator<Palindrome, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext ctx) {
if (value == null) return true; // @NotNull's job, not ours
var clean = value.toLowerCase().replaceAll("\\W", "");
return new StringBuilder(clean).reverse().toString().equals(clean);
}
}
A constraint can declare several validators for different target types (e.g. one for Collection, one for Map); Erasmus picks the most specific one from the validated type, boxing primitives as the spec requires, and throws UnexpectedTypeException when none fits.
Composed constraints
Constraints compose by stacking them on a new annotation; @ReportAsSingleViolation collapses the report:
@NotBlank
@Size(min = 3, max = 16)
@Pattern(regexp = "[a-z0-9_]+")
@Constraint(validatedBy = {})
@ReportAsSingleViolation
@Target(FIELD) @Retention(RUNTIME)
public @interface Username {
String message() default "not a valid username";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Message interpolation
Messages resolve from ValidationMessages.properties (bundled in English and French) and interpolate constraint attributes and a subset of Expression Language:
@Size(min = 2, max = 20, message = "between {min} and {max} please")
private String name;
The EL subset covers ternaries, || / &&, equality, relational, additive, multiplicative and unary operators — deliberately not method calls or nested navigation, which keeps the interpolator dependency-free and injection-safe. Anything beyond the subset renders literally.
Not there yet
The following spec areas are milestones in progress — attempting them today will not behave per spec:
-
Cascading and groups (
@Valid, group sequences) — M3; -
Container element extraction (
List<@Email String>) — M4 (ValueExtractorSPI); -
Executable validation (method parameters / return values) — M5;
-
XML mapping (
validation.xml, constraint mappings) — planned as M10, demand-driven, not implemented.
See the migration page for what this means when coming from Hibernate Validator.