Hibernate Validator is the reference implementation of Jakarta Validation 3.1 providing annotation-based validation for JavaBeans and method parameters
Hibernate-specific extensions to Jakarta Validation property path nodes providing access to actual property and container element values during validation for enhanced error reporting and debugging.
Property node extension providing access to the actual property value.
package org.hibernate.validator.path;
import jakarta.validation.Path;
/**
* Node representing a property with Hibernate Validator specific functionality.
* Extends standard Jakarta Validation PropertyNode with value access and additional
* Hibernate-specific container metadata.
*/
interface PropertyNode extends Path.PropertyNode {
/**
* Get the value of the bean property represented by this node.
* Returns the actual value that was validated.
*
* @return property value or null if not available
*/
Object getValue();
/**
* Get the container class (e.g., List, Map, Optional) of this property node.
* Hibernate-specific extension.
*
* @return container class or null if not a container element
* @since 6.0
*/
@Incubating
Class<?> getContainerClass();
/**
* Get the type argument index in the container's generic type declaration.
* Hibernate-specific extension.
*
* @return type argument index or null if not a container element
* @since 6.0
*/
@Incubating
Integer getTypeArgumentIndex();
// Inherited from Path.PropertyNode:
// String getName()
// Integer getIndex()
// Object getKey()
// ElementKind getKind()
// boolean isInIterable()
}Usage Example:
import org.hibernate.validator.path.PropertyNode;
import jakarta.validation.*;
import java.util.Set;
class Product {
@Min(10)
private int quantity;
@NotNull
private String name;
// getters/setters...
}
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Product product = new Product();
product.setQuantity(5); // Invalid
product.setName(null); // Invalid
Set<ConstraintViolation<Product>> violations = validator.validate(product);
for (ConstraintViolation<Product> violation : violations) {
Path propertyPath = violation.getPropertyPath();
for (Path.Node node : propertyPath) {
if (node.getKind() == ElementKind.PROPERTY) {
// Cast to Hibernate PropertyNode
PropertyNode propertyNode = node.as(PropertyNode.class);
System.out.println("Property: " + propertyNode.getName());
System.out.println("Invalid value: " + propertyNode.getValue());
System.out.println("Message: " + violation.getMessage());
System.out.println("---");
}
}
}
// Output:
// Property: quantity
// Invalid value: 5
// Message: must be greater than or equal to 10
// ---
// Property: name
// Invalid value: null
// Message: must not be null
// ---Container element node extension providing access to the actual container element value.
package org.hibernate.validator.path;
import jakarta.validation.Path;
/**
* Node representing a container element with Hibernate Validator specific functionality.
* Extends standard Jakarta Validation ContainerElementNode with value access and additional
* Hibernate-specific container metadata.
*/
interface ContainerElementNode extends Path.ContainerElementNode {
/**
* Get the value of the container element represented by this node.
* Returns the actual element value from the container.
*
* @return container element value or null if not available
*/
Object getValue();
/**
* Get the container class (e.g., List, Map, Optional) of this container element node.
* Hibernate-specific extension.
*
* @return container class or null if not available
* @since 6.0
*/
@Incubating
Class<?> getContainerClass();
/**
* Get the type argument index in the container's generic type declaration.
* Hibernate-specific extension.
*
* @return type argument index or null if not available
* @since 6.0
*/
@Incubating
Integer getTypeArgumentIndex();
// Inherited from Path.ContainerElementNode:
// String getName()
// Integer getIndex()
// Object getKey()
// ElementKind getKind()
// boolean isInIterable()
}Usage Example:
import org.hibernate.validator.path.ContainerElementNode;
import jakarta.validation.*;
import jakarta.validation.constraints.*;
import java.util.*;
class ShoppingCart {
@NotNull
@Size(min = 1)
private List<@NotNull @Min(1) Integer> itemQuantities;
public ShoppingCart(List<Integer> itemQuantities) {
this.itemQuantities = itemQuantities;
}
// getters/setters...
}
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
// Create cart with invalid data
ShoppingCart cart = new ShoppingCart(Arrays.asList(1, 0, 5, null, -2));
Set<ConstraintViolation<ShoppingCart>> violations = validator.validate(cart);
for (ConstraintViolation<ShoppingCart> violation : violations) {
Path propertyPath = violation.getPropertyPath();
for (Path.Node node : propertyPath) {
if (node.getKind() == ElementKind.CONTAINER_ELEMENT) {
// Cast to Hibernate ContainerElementNode
ContainerElementNode containerNode = node.as(ContainerElementNode.class);
System.out.println("Container: " + containerNode.getContainerClass().getSimpleName());
System.out.println("Index: " + containerNode.getIndex());
System.out.println("Invalid element value: " + containerNode.getValue());
System.out.println("Message: " + violation.getMessage());
System.out.println("Full path: " + propertyPath);
System.out.println("---");
}
}
}
// Output includes:
// Container: List
// Index: 1
// Invalid element value: 0
// Message: must be greater than or equal to 1
// Full path: itemQuantities[1].<list element>
// ---
// Container: List
// Index: 3
// Invalid element value: null
// Message: must not be null
// Full path: itemQuantities[3].<list element>
// ---import org.hibernate.validator.path.PropertyNode;
import org.hibernate.validator.path.ContainerElementNode;
import jakarta.validation.*;
import jakarta.validation.constraints.*;
import java.util.*;
// Complex nested structure
class Order {
@Valid
private Customer customer;
@NotNull
@Size(min = 1)
private List<@Valid OrderItem> items;
private Map<@NotBlank String, @Valid @NotNull Address> deliveryAddresses;
// getters/setters...
}
class Customer {
@NotBlank
private String name;
@Email
private String email;
// getters/setters...
}
class OrderItem {
@NotNull
private String productId;
@Min(1)
private int quantity;
@DecimalMin("0.01")
private BigDecimal price;
// getters/setters...
}
class Address {
@NotBlank
private String street;
@NotBlank
private String city;
// getters/setters...
}
// Validate and navigate paths
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Order order = new Order();
// ... set up invalid data ...
Set<ConstraintViolation<Order>> violations = validator.validate(order);
for (ConstraintViolation<Order> violation : violations) {
System.out.println("\n=== Constraint Violation ===");
System.out.println("Message: " + violation.getMessage());
System.out.println("Root bean: " + violation.getRootBeanClass().getSimpleName());
System.out.println("Property path: " + violation.getPropertyPath());
System.out.println("Invalid value: " + violation.getInvalidValue());
System.out.println("\nPath details:");
for (Path.Node node : violation.getPropertyPath()) {
System.out.println(" Node kind: " + node.getKind());
System.out.println(" Node name: " + node.getName());
switch (node.getKind()) {
case PROPERTY:
PropertyNode propNode = node.as(PropertyNode.class);
System.out.println(" Property value: " + propNode.getValue());
break;
case CONTAINER_ELEMENT:
ContainerElementNode containerNode = node.as(ContainerElementNode.class);
System.out.println(" Container class: " +
containerNode.getContainerClass().getSimpleName());
System.out.println(" Element index: " + containerNode.getIndex());
System.out.println(" Element key: " + containerNode.getKey());
System.out.println(" Element value: " + containerNode.getValue());
System.out.println(" Type argument index: " +
containerNode.getTypeArgumentIndex());
break;
case BEAN:
System.out.println(" Bean node");
break;
case PARAMETER:
System.out.println(" Parameter index: " + node.as(Path.ParameterNode.class).getParameterIndex());
break;
case RETURN_VALUE:
System.out.println(" Return value node");
break;
}
if (node.isInIterable()) {
System.out.println(" In iterable: true");
System.out.println(" Index: " + node.getIndex());
System.out.println(" Key: " + node.getKey());
}
System.out.println();
}
}
// Example output for items[1].quantity violation:
// === Constraint Violation ===
// Message: must be greater than or equal to 1
// Root bean: Order
// Property path: items[1].quantity
// Invalid value: 0
//
// Path details:
// Node kind: PROPERTY
// Node name: items
// Property value: [OrderItem@123, OrderItem@456]
//
// Node kind: CONTAINER_ELEMENT
// Node name: <list element>
// Container class: List
// Element index: 1
// Element key: null
// Element value: OrderItem@456
// Type argument index: 0
//
// Node kind: PROPERTY
// Node name: quantity
// Property value: 0import org.hibernate.validator.path.PropertyNode;
import jakarta.validation.*;
import jakarta.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.Set;
class UserService {
public User createUser(
@NotBlank String username,
@Email String email,
@Min(18) int age
) {
return new User(username, email, age);
}
@Valid
@NotNull
public User getUser(@NotNull String id) {
return new User("test", "test@example.com", 25);
}
}
// Validate method parameters
ExecutableValidator execValidator = validator.forExecutables();
UserService service = new UserService();
try {
Method method = UserService.class.getMethod("createUser",
String.class, String.class, int.class);
Object[] parameters = {"", "invalid-email", 15}; // Invalid parameters
Set<ConstraintViolation<UserService>> violations =
execValidator.validateParameters(service, method, parameters);
for (ConstraintViolation<UserService> violation : violations) {
System.out.println("Violation on: " + violation.getPropertyPath());
for (Path.Node node : violation.getPropertyPath()) {
if (node.getKind() == ElementKind.METHOD) {
System.out.println("Method: " + node.getName());
} else if (node.getKind() == ElementKind.PARAMETER) {
Path.ParameterNode paramNode = node.as(Path.ParameterNode.class);
System.out.println("Parameter index: " + paramNode.getParameterIndex());
System.out.println("Parameter name: " + paramNode.getName());
System.out.println("Invalid value: " + violation.getInvalidValue());
}
}
System.out.println("Message: " + violation.getMessage());
System.out.println("---");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// Output:
// Violation on: createUser.arg0
// Method: createUser
// Parameter index: 0
// Parameter name: arg0
// Invalid value:
// Message: must not be blank
// ---
// Violation on: createUser.arg1
// Method: createUser
// Parameter index: 1
// Parameter name: arg1
// Invalid value: invalid-email
// Message: must be a well-formed email address
// ---import org.hibernate.validator.path.PropertyNode;
import org.hibernate.validator.path.ContainerElementNode;
import jakarta.validation.*;
import java.util.*;
/**
* Custom error reporter that extracts detailed information from property paths.
*/
class DetailedErrorReporter {
public List<ErrorDetail> generateErrorReport(Set<ConstraintViolation<?>> violations) {
List<ErrorDetail> errors = new ArrayList<>();
for (ConstraintViolation<?> violation : violations) {
ErrorDetail detail = new ErrorDetail();
detail.setMessage(violation.getMessage());
detail.setInvalidValue(violation.getInvalidValue());
detail.setPropertyPath(violation.getPropertyPath().toString());
// Extract detailed path information
List<PathSegment> pathSegments = new ArrayList<>();
for (Path.Node node : violation.getPropertyPath()) {
PathSegment segment = new PathSegment();
segment.setKind(node.getKind().name());
segment.setName(node.getName());
// Get actual value for properties and container elements
if (node.getKind() == ElementKind.PROPERTY) {
PropertyNode propNode = node.as(PropertyNode.class);
segment.setValue(propNode.getValue());
} else if (node.getKind() == ElementKind.CONTAINER_ELEMENT) {
ContainerElementNode containerNode = node.as(ContainerElementNode.class);
segment.setValue(containerNode.getValue());
segment.setContainerType(containerNode.getContainerClass().getSimpleName());
segment.setIndex(containerNode.getIndex());
segment.setKey(containerNode.getKey());
}
pathSegments.add(segment);
}
detail.setPathSegments(pathSegments);
errors.add(detail);
}
return errors;
}
}
class ErrorDetail {
private String message;
private Object invalidValue;
private String propertyPath;
private List<PathSegment> pathSegments;
// getters/setters...
}
class PathSegment {
private String kind;
private String name;
private Object value;
private String containerType;
private Integer index;
private Object key;
// getters/setters...
}
// Usage
DetailedErrorReporter reporter = new DetailedErrorReporter();
Set<ConstraintViolation<Order>> violations = validator.validate(order);
List<ErrorDetail> errors = reporter.generateErrorReport(violations);
// Convert to JSON, log, display in UI, etc.Install with Tessl CLI
npx tessl i tessl/maven-org-hibernate-validator--hibernate-validator