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:

  • @NotEmpty and @Size — CharSequence, Collection, Map and arrays;

  • @DecimalMin, @DecimalMax and @Digits — Number and CharSequence;

  • @Min and @Max — Number only (no CharSequence validator yet);

  • @Past, @PastOrPresent, @Future and @FutureOrPresent — Instant, LocalDate and java.util.Date only (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 (ValueExtractor SPI);

  • 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.