Jakarta Validation API defines a metadata model and API for JavaBean and method validation
—
Group-based validation for conditional constraint application and validation sequencing with group conversion support during cascading validation.
Marker interface representing the default validation group.
/**
* Default validation group
* Constraints belong to this group unless explicitly assigned to other groups
*/
interface Default {}Annotation for defining sequential validation of groups.
/**
* Defines a sequence of groups that should be validated sequentially
* If validation fails for one group, subsequent groups are not validated
*/
@Target({TYPE})
@Retention(RUNTIME)
@interface GroupSequence {
/**
* The sequence of groups to validate
* @return array of group classes
*/
Class<?>[] value();
}Annotation for converting validation groups during cascading validation.
/**
* Converts validation groups during cascaded validation
* Applied to fields, method parameters, and return values marked with @Valid
*/
@Target({FIELD, METHOD, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(ConvertGroup.List.class)
@interface ConvertGroup {
/**
* The source group to convert from
* @return source group class
*/
Class<?> from();
/**
* The target group to convert to
* @return target group class
*/
Class<?> to();
/**
* Container for multiple ConvertGroup annotations
*/
@Target({FIELD, METHOD, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@interface List {
ConvertGroup[] value();
}
}Usage Examples:
import jakarta.validation.*;
import jakarta.validation.constraints.*;
import jakarta.validation.groups.*;
// 1. Define validation groups
interface BasicInfo {}
interface AdvancedInfo {}
interface AdminInfo {}
// 2. Apply constraints to specific groups
public class User {
@NotNull(groups = {BasicInfo.class, AdvancedInfo.class})
@Size(min = 2, max = 50, groups = BasicInfo.class)
private String name;
@NotNull(groups = BasicInfo.class)
@Email(groups = BasicInfo.class)
private String email;
@Min(value = 18, groups = AdvancedInfo.class)
private Integer age;
@NotNull(groups = AdminInfo.class)
private String adminRole;
// constructors, getters, setters...
}
// 3. Group sequence for ordered validation
@GroupSequence({BasicInfo.class, AdvancedInfo.class, AdminInfo.class})
interface UserValidationSequence {}
// 4. Validate with specific groups
public class ValidationGroupExample {
private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public void validateUserGroups() {
User user = new User();
user.setName(""); // Invalid for BasicInfo
user.setAge(15); // Invalid for AdvancedInfo
// Validate only basic information
Set<ConstraintViolation<User>> basicViolations =
validator.validate(user, BasicInfo.class);
System.out.println("Basic validation violations: " + basicViolations.size());
// Validate advanced information
Set<ConstraintViolation<User>> advancedViolations =
validator.validate(user, AdvancedInfo.class);
System.out.println("Advanced validation violations: " + advancedViolations.size());
// Validate with sequence (stops at first failing group)
Set<ConstraintViolation<User>> sequenceViolations =
validator.validate(user, UserValidationSequence.class);
System.out.println("Sequence validation violations: " + sequenceViolations.size());
// Validate multiple groups at once
Set<ConstraintViolation<User>> multiGroupViolations =
validator.validate(user, BasicInfo.class, AdvancedInfo.class);
System.out.println("Multi-group validation violations: " + multiGroupViolations.size());
}
}
// 5. Group conversion in cascading validation
public class Order {
@NotNull
@Valid
@ConvertGroup(from = Default.class, to = BasicInfo.class)
private User customer; // When validating Order, customer uses BasicInfo group
@Valid
@ConvertGroup(from = BasicInfo.class, to = AdvancedInfo.class)
@ConvertGroup(from = AdvancedInfo.class, to = AdminInfo.class)
private User assignedAgent; // Multiple group conversions
@NotNull(groups = BasicInfo.class)
private String orderNumber;
// constructors, getters, setters...
}
// 6. Dynamic group validation
public class UserRegistrationService {
private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public ValidationResult validateUser(User user, String validationLevel) {
Class<?>[] groups;
switch (validationLevel) {
case "basic":
groups = new Class<?>[]{BasicInfo.class};
break;
case "advanced":
groups = new Class<?>[]{BasicInfo.class, AdvancedInfo.class};
break;
case "admin":
groups = new Class<?>[]{UserValidationSequence.class};
break;
default:
groups = new Class<?>[]{Default.class};
}
Set<ConstraintViolation<User>> violations = validator.validate(user, groups);
return new ValidationResult(violations.isEmpty(), violations);
}
}
// 7. Conditional validation based on object state
public class ConditionalUser {
@NotNull
private String username;
@NotNull(groups = PremiumUser.class)
@Size(min = 10, groups = PremiumUser.class)
private String premiumFeature;
private boolean isPremium;
public Set<ConstraintViolation<ConditionalUser>> validate() {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
if (isPremium) {
return validator.validate(this, Default.class, PremiumUser.class);
} else {
return validator.validate(this, Default.class);
}
}
interface PremiumUser {}
}Install with Tessl CLI
npx tessl i tessl/maven-jakarta-validation--jakarta-validation-api