Jakarta Bean Validation
Validation constraints, groups, custom validators, and validating method parameters.
# CLAUDE.md — Jakarta Bean Validation
## Constraint annotations
- `@NotNull` — field must be present.
- `@NotBlank` — string must be non-null and contain at least one non-whitespace character.
- `@NotEmpty` — collection or string must be non-null and have ≥1 element/character.
- `@Size(min, max)` — bounds on collections, strings, arrays.
- `@Min`, `@Max`, `@DecimalMin`, `@DecimalMax` — numeric bounds.
- `@Pattern(regexp)` — regex match. Anchor with `^` and `$` if you mean the full string.
- `@Email`, `@URL` (Hibernate Validator) — common formats.
Apply on fields of DTOs:
```java
record CreateUser(
@NotBlank @Email String email,
@NotBlank @Size(min = 8, max = 100) String password,
@NotNull @Min(13) Integer age
) {}
```
## Triggering validation
- In JAX-RS / Spring MVC, add `@Valid` to the request body parameter.
- For method parameters / return types, add `@Validated` to the class (Spring) or use `@ExecutableType` (Jakarta EE) with a CDI interceptor.
- Validate manually:
```java
Set<ConstraintViolation<T>> violations = validator.validate(obj);
if (!violations.isEmpty()) throw new ConstraintViolationException(violations);
```
## Groups
- For different validation rules at different stages (create vs update), use validation groups:
```java
interface OnCreate {}
interface OnUpdate {}
record User(
@Null(groups = OnCreate.class) @NotNull(groups = OnUpdate.class) String id,
@NotBlank @Email String email
) {}
```
- Apply with `@Validated(OnCreate.class)` or `validator.validate(obj, OnCreate.class)`.
- Don't over-design groups. Most apps need 0–2.
## Cross-field validation
- Custom constraint annotation + a `ConstraintValidator<MyAnnotation, MyClass>`.
- Apply at class level (`@Target(TYPE)`) when the rule depends on multiple fields:
```java
@PasswordsMatch
record SignupRequest(@NotBlank String password, @NotBlank String confirmPassword) {}
```
## Method-level validation
- Annotate method parameters and return:
```java
@NotNull
User findById(@NotBlank String id);
```
- Requires CDI interceptor or Spring's method validation enabled.
- Throws `ConstraintViolationException` on violation.
## Error responses
- In JAX-RS, write a `ConstraintViolationExceptionMapper` to translate to a 400 with field-level errors.
- In Spring, `@RestControllerAdvice` with `@ExceptionHandler(MethodArgumentNotValidException.class)` is the equivalent.
- Return a structured error body — clients should know which field failed.
## Localization
- `@NotNull(message = "{user.name.required}")` references a message key.
- Provide `ValidationMessages.properties` per locale.
- Don't hard-code English messages in annotations for products with i18n.
## Don't
- Don't validate inside services if the same rule applies at the request boundary. Catch invalid input early.
- Don't use Bean Validation for business rules ("user can't have more than 3 active subscriptions"). That's domain logic, not input validation.
- Don't write `@NotNull String` next to `@NotBlank String`. `@NotBlank` already implies non-null and non-empty.
- Don't put validation annotations on getters in addition to fields — they're applied twice.
Other Jakarta EE templates
Modern Quarkus Rules
Quarkus 3+ defaults: dev mode, build-time optimization, configuration, and CDI.
Quarkus REST (RESTEasy Reactive)
JAX-RS endpoints with RESTEasy Reactive — request validation and response models.
Quarkus + Hibernate ORM with Panache
Active-record and repository styles with Panache, Flyway migrations, transactions.
Modern Jakarta EE Rules
Jakarta EE 10+ namespace migration, modular architecture, and platform conventions.
Jakarta CDI (Contexts & Dependency Injection)
CDI scopes, qualifiers, producers, events, and the lifecycle to actually understand.