Hibernate Validator Documentation

repository·main·Indexed 23 days ago

https://github.com/hibernate/hibernate-validator

Hibernate Validator is the reference implementation of the Jakarta Validation specification, providing a framework for bean and method validation using annotations or XML. This documentation covers installation via Maven, CDI integration, the use of the annotation processor for constraint verification, and detailed instructions for executing and configuring JMH performance benchmarks, including CPU profiling with async-profiler and core pinning on Linux.

Tokens
21.4K
Snippets
19
Records
131
Agent score
76%

What's inside Hibernate Validator

  1. What is Hibernate Validator?

    main

    Hibernate Validator is the reference implementation of the Jakarta Validation specification. It provides a metadata model and API for entity and method validation, allowing developers to define validation logic once and apply it across different application layers (presentation, business, and persistence) to avoid duplication.

    Key characteristics:

    • Metadata-driven: Uses annotations as the default metadata source, with support for XML to override or extend metadata.
    • Tier-agnostic: The API is not tied to a specific application tier (like web or persistence) or programming model, making it suitable for server-side applications and rich client applications (e.g., Swing).
  2. What is the Hibernate Validator Annotation Processor?

    main

    The Hibernate Validator Annotation Processor is a build-time tool that prevents common mistakes when using constraint annotations. It plugs into the Java compilation process and raises compilation errors when it detects incorrect usage, such as:

    • Specifying constraint annotations on unsupported data types (e.g., @Past on a String).
    • Annotating setter methods instead of getter methods.
    • Annotating static fields or methods.
    • Annotating primitive fields or methods with @Valid.
    • Using annotation types that are not themselves constraint annotations.
    • Invalid definitions of dynamic default group sequences with @GroupSequenceProvider.
    • Invalid method parameter or return value constraints in inheritance hierarchies.
  3. Use cascaded validation for method parameters and return values

    main

    The @Valid annotation can be used on method parameters and return values to enable cascaded validation.

    • Parameters: When validating method arguments, if a parameter is marked with @Valid, the constraints on the properties of that object are also evaluated.
    • Return Values: If a return value is marked with @Valid, the constraints on the returned object are checked.
    • Containers: Cascaded validation can be applied to container elements (e.g., List<@Valid Car>). Each element in the collection will be validated recursively.

    Note: null values are ignored during cascaded validation for parameters and properties.

  4. Compose constraints with Boolean OR and NOT logic

    main

    While Jakarta Validation defaults to logical AND for composed constraints, Hibernate Validator allows OR and NOT composition using the @ConstraintComposition annotation and the CompositionType enum.

    Composition Types:

    • AND: Standard logical AND.
    • OR: Logical OR (validation passes if any composing constraint is valid).
    • $$ALL_FALSE$$: Logical NOT (useful for enforcing that no composing constraints are met). Using this type implicitly ensures only a single violation is reported if the composition fails.
  5. How message interpolation works

    main

    Message interpolation is the process of creating error messages for violated Jakarta Validation constraints. When a constraint is violated, the validation engine uses a MessageInterpolator to resolve the message descriptor defined in the constraint's message attribute.

    The interpolation algorithm follows these steps:

    1. Resolve Message Parameters: Look up keys enclosed in {} (e.g., {min}) in the application's resource bundle (e.g., ValidationMessages.properties on the classpath). This is recursive.
    2. Resolve Built-in Messages: If not found in the application bundle, look up the parameter in the standard error messages bundle (org.hibernate.validator.ValidationMessages).
    3. Resolve Constraint Attributes: Replace message parameters with the value of the constraint annotation member of the same name (e.g., ${min} refers to the min attribute of a @Size annotation).
    4. Resolve Message Expressions: Evaluate string literals enclosed in ${} as Jakarta Expression Language (EL) expressions.

    To retrieve the final message, call ConstraintViolation#getMessage().

  6. Declare bean constraints at different levels

    main

    In Jakarta Validation, constraints are expressed via Java annotations. You can apply constraints at four distinct levels depending on your requirements:

    1. Field-level constraints: Annotate a class field directly. The validation engine uses a 'field access strategy', accessing the instance variable directly without invoking getter/setter methods. Constraints can be applied to any access type (public, private, etc.), but static fields are not supported.
    2. Property-level constraints: Annotate the getter method of a property (following JavaBeans standards). The engine uses a 'property access strategy', accessing state via the accessor method. This allows constraining read-only properties that lack setters.
    3. Container element constraints: Specify constraints on the type argument of a parameterized type (e.g., List<@NotNull String>). This requires the constraint to have ElementType.TYPE_USE in its @Target definition.
    4. Class-level constraints: Annotate the class itself. These are used when validation depends on the correlation between multiple properties (e.g., ensuring a passengers count does not exceed seatCount).

    Best Practices:

    • Avoid Duplication: Do not annotate both a field and its corresponding getter; this causes the field to be validated twice.
    • Byte Code Enhancement: If using a byte code enhancing library, use property-level constraints, as the library may not be able to determine field access via reflection.
    • Consistency: Stick to either field or property annotations within a single class.
  7. Structure for adding new performance tests

    main

    When adding new performance tests, place them in the directory corresponding to the validation namespace they target:

    • jakarta/: Tests based on the jakarta.validation namespace (Jakarta Validation spec).
    • javax/: Tests based on the javax.validation namespace (Java Bean Validation spec versions 1.0 and 1.1).
    • javax-bv2/: Tests based on the javax.validation namespace (Java Bean Validation spec version 2.0).
    • java/: Contains the main test runner sources.
  8. Use Fail fast on property violation mode

    main

    The Fail fast on property violation mode (marked as @Incubating) allows class-level constraint validation to occur only if all property-level constraints have passed without violations.

    This simplifies class-level validators because you can assume the properties are already valid (e.g., non-null), removing the need for manual sanity checks within the class-level validator.

    Note: Only simple property constraints are checked before class-level ones; cascading constraints are delayed until later.

    This mode can be enabled:

    • Programmatically via the configuration API.
    • Via XML property configuration.
  9. Understand Path and Node mutability changes in {hvVersionShort}.x

    main

    In Hibernate Validator {hvVersionShort}.x, the internal representation of Path and Node has become mutable during the validation process.

    Key implications for developers:

    • Do not rely on immutability: If you have implemented custom traversable resolvers, do not assume that Path or Node objects will remain unchanged during validation.
    • ConstraintViolation remains safe: The Path and Node representations contained within a ConstraintViolation object remain immutable.
  10. Method constraints in inheritance hierarchies

    main

    When using method constraints with inheritance, you must follow the Liskov Substitution Principle to ensure behavioral subtyping:

    1. Parameter Constraints (Preconditions): Subtypes may not strengthen preconditions. Therefore, Jakarta Validation disallows adding parameter constraints to methods that override or implement a method from a supertype or interface. If a method overrides a method from multiple parallel supertypes, no parameter constraints may be specified.
    2. Return Value Constraints (Postconditions): Subtypes may strengthen postconditions. You can add additional return value constraints to overriding methods without violating the principle.

    If these rules are violated, a ConstraintDeclarationException is raised. These rules apply to methods but not to constructors (as constructors are never overridden).

  11. Requesting validation groups

    main

    Validation groups allow you to restrict which constraints are applied during a validation cycle. This is useful for scenarios like UI wizards where only a subset of constraints should be active at a specific step.

    By default, if no group is specified for a constraint annotation, it belongs to the jakarta.validation.groups.Default group. To validate constraints belonging to specific groups, pass those groups as var-arg parameters to the Validator#validate() or ExecutableValidator methods.

    Important Notes:

    • When requesting multiple groups, the evaluation order is non-deterministic.
    • Using interfaces for groups provides type-safety and allows for group inheritance.
  12. Pin benchmarks to specific CPU cores

    main

    On Linux, the script can pin the benchmark JVM to specific cores using taskset to avoid inconsistent results (e.g., when running on efficiency cores in hybrid CPUs).

    • Auto-detect: By default, the script attempts to detect P-cores on Intel hybrid CPUs.
    • Manual Override: Use the --cores <spec> flag with a range or list (e.g., --cores 0-7 or --cores 0,2,4,6).

    Note: Core pinning is not supported on macOS.

    # Custom thread count and core pinning
    ./performance/scripts/run-benchmarks.sh --threads 4 --cores 0-3