The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To validate a child object when its parent is validated, put @Valid on the parent’s reference to that child. For example, use @NotNull with @Valid when the child must exist and its own constraints must also be checked. Without @Valid on the association, constraints inside the child are not cascaded from the parent.
How cascading validation works
@Valid is a marker for cascaded validation, not a constraint such as @NotBlank or @NotNull. It tells a Jakarta Bean Validation provider to follow an annotated association and validate the associated object. The root object must itself be passed to validation—for example, with validator.validate(order)—before cascading can begin.
Here, validating an OrderRequest checks the customer’s name because the association is marked @Valid:
Recommended Free Tools
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
public class OrderRequest {
@NotNull
@Valid
private CustomerRequest customer;
}
public class CustomerRequest {
@NotBlank
private String name;
}
If the parent field were only private CustomerRequest customer;, validating the parent would not by itself traverse into customer.name. The child’s annotations define rules for the child; @Valid connects those rules to validation of the parent graph.
@Valid versus @NotNull
| Annotation | What it checks | Example |
|---|---|---|
@NotNull |
The reference itself is present. | customer == null fails. |
@Valid |
Constraints on the referenced object are evaluated. | A blank customer.name fails. |
@NotBlank |
A string is non-null and contains a non-whitespace character. | name = " " fails. |
@NotEmpty |
A supported string, collection, map, or array is non-null and non-empty. | An empty item list fails. |
@Size |
A supported value’s size falls within the configured bounds. | A list with fewer than two entries fails @Size(min = 2). |
Cascading skips a null reference. Thus @Valid alone does not make a child mandatory. For a required child whose contents must also be checked, use:
@NotNull
@Valid
private AddressRequest address;
@NotNull rejects a missing address; @Valid checks the address’s constraints when it is present.
Field and getter placement
You can place cascading metadata on a field:
@Valid
private CustomerRequest customer;
Or use a JavaBean getter:
private CustomerRequest customer;
@Valid
public CustomerRequest getCustomer() {
return customer;
}
Bean Validation supports field and property access. In a bean, keep constraints and cascading annotations consistently on fields or consistently on getters unless you have a deliberate access strategy. Mixing the two can make it unclear which access strategy the provider uses and may lead to metadata being applied differently than expected.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Validation through multiple levels
Cascading is recursive, but each association that should be traversed needs @Valid. For example:
public class OrderRequest {
@NotNull
@Valid
private ShippingRequest shipping;
}
public class ShippingRequest {
@NotNull
@Valid
private AddressRequest address;
}
public class AddressRequest {
@NotBlank
private String city;
}
Validating the order can reach shipping.address.city. If shipping has @Valid but address does not, traversal stops at the shipping object. Use @NotNull at any level where a missing intermediate object should also fail.
Rank #2
Lists, sets, arrays, maps, and nested containers
For a collection of child DTOs, modern type-use syntax makes it clear that each element should be cascaded:
public class OrderRequest {
@NotEmpty
private List<@Valid LineItemRequest> items;
}
public class LineItemRequest {
@NotBlank
private String productCode;
@Min(1)
private int quantity;
}
The established container-level form is also used in existing code:
@Valid
private List<LineItemRequest> items;
Choose one form rather than putting @Valid on both the container and its element type. The Jakarta Validation 4.0 milestone draft says behavior is undefined when both locations are annotated for the same cascade. The 4.0 document is a draft, so check support in the API and provider versions used by your project.
@NotEmpty in the first example checks that the list is present and non-empty; the element-level @Valid checks each line item’s constraints. Cascading by itself does not reject a null or empty collection. You can instead use @NotNull and @Size(min = 1) when you want to express presence and size as separate rules.
Type-use cascading can also be used with other supported container shapes:
private Set<@Valid AddressRequest> addresses;
private AddressRequest @Valid [] addressArray;
private Map<String, @Valid AddressRequest> addressesByType;
private Map<@Valid CustomerId, @Valid CustomerRequest> customers;
private List<@Valid List<@Valid AddressRequest>> addressGroups;
For maps, values and keys are distinct type arguments: annotate the value to validate map values and annotate the key separately when key objects must be cascaded. Standard container support and type-use behavior depend on the Bean Validation API and provider version. A custom generic container generally needs a value extractor so the provider knows how to access its contained values.
Run validation in plain Java
In Java SE, include a Bean Validation provider. For example, Hibernate Validator is an implementation of Jakarta Validation. Its current 9.1 line requires Java 17 or later and targets Jakarta Validation 3.1. Hibernate Validator 9.1.3.Final was listed as the latest stable 9.1 release on August 18, 2026; versions change, so check the release page before pinning a version.
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>9.1.3.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish.expressly</groupId>
<artifactId>expressly</artifactId>
<version>6.0.0</version>
</dependency>
A Java SE application normally needs an Expression Language implementation for standard message interpolation. See Hibernate Validator’s getting-started guidance for setup details.
import jakarta.validation.Valid;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
public class Demo {
static class Parent {
@NotNull
@Valid
private final Child child;
Parent(Child child) { this.child = child; }
}
static class Child {
@NotBlank
private final String name;
Child(String name) { this.name = name; }
}
public static void main(String[] args) {
try (ValidatorFactory factory =
Validation.buildDefaultValidatorFactory()) {
Validator validator = factory.getValidator();
Parent parent = new Parent(new Child(""));
validator.validate(parent).forEach(violation ->
System.out.println(violation.getPropertyPath()
+ ": " + violation.getMessage())
);
}
}
}
The property path identifies the nested location, such as child.name. The exact message can vary with provider, locale, and message configuration.
Spring MVC and Spring Boot
At an MVC request boundary, a controller can ask Spring to validate a request body:
Rank #4
@PostMapping("/orders")
public ResponseEntity<Void> create(
@Valid @RequestBody OrderRequest request) {
return ResponseEntity.ok().build();
}
The DTO still needs @Valid on child associations such as OrderRequest.customer. The controller annotation initiates request validation; it does not automatically cascade through every nested field. Spring’s exact behavior, including method-validation behavior and error handling, depends on the Spring Framework version and method signature. See the Spring MVC validation reference.
In Spring Boot, the usual dependency is spring-boot-starter-validation. Let Spring Boot’s dependency management select a compatible provider unless you have a specific reason to override it; if overriding, verify compatibility with your Boot release. See Spring Boot’s build-system documentation.
For plain Java method calls, writing @Valid on a parameter does not automatically intercept and validate every invocation. Executable validation must be invoked directly or integrated through the framework’s method-validation support. For example:
public void submit(@Valid OrderRequest order) { /* ... */ }
@Valid
public OrderResponse create(@Valid OrderRequest request) { /* ... */ }
@Valid can be used on executable parameters and return values, but the application must have method or executable validation configured and invoked.
javax.validation or jakarta.validation?
Older Java applications often use imports such as javax.validation.Valid. Newer Jakarta-based applications use jakarta.validation.Valid. These are different API namespaces, not interchangeable spellings:
Best Value
// Older javax-based stack
import javax.validation.Valid;
// Jakarta-based stack
import jakarta.validation.Valid;
Use the namespace expected by your framework and validation provider throughout the application. Mixing javax annotations with a provider expecting jakarta can result in annotations not being recognized or in dependency incompatibilities. Hibernate Validator 9.x targets Jakarta Validation 3.1 and requires Java 17 or later; older provider lines may be needed for legacy Java or javax-based applications. Check the migration guide and release information for version-specific compatibility.
Groups, cycles, and persistence models
@Valid controls traversal; it does not choose a validation group. When cascading should switch from one group to another, use group conversion:
@Valid
@ConvertGroup(from = Default.class, to = ExtendedChecks.class)
private AddressRequest address;
A default group sequence defined on a class does not simply propagate unchanged into associated objects. Group behavior is specified by Jakarta Validation; see the 3.0 specification for details.
Providers prevent infinite cascading along the same navigation path in cyclic object graphs, but bidirectional relationships can still produce complicated paths or surprising results. A shared object reachable through different branches may be encountered along each path. For API input, dedicated request DTOs are often easier to validate than a bidirectional persistence graph. ORM reachability, lazy associations, and proxies can also affect what is traversed; persistence integration may involve a TraversableResolver. See the 3.1 specification.
Quick Recap
Troubleshooting nested validation
- Confirm the root is validated. In plain Java, call
validator.validate(root); in a web application, confirm the relevant controller or framework validation entry point is active. - Check every association. Add
@Validat each parent-to-child link you expect the provider to traverse. - Check for nulls. A null child is skipped during cascading. Add
@NotNullif it is required. - Check collections separately. Use
@NotEmptyor suitable presence/size constraints if the container itself must exist or contain elements, then cascade its elements. - Check annotation placement. Use either container-level or element-level
@Validfor a collection, not both for the same cascade. Confirm your API and provider support the type-use form you chose. - Check imports and dependencies. Keep
javax.validationandjakarta.validationstacks consistent and ensure a compatible provider is present. - Check the active group and framework integration. A constraint may not run if the group is not being validated, or if method validation has not been enabled.
- Read the property path. A path such as
items[0].quantitypoints to the failing property on the first list element; use it to locate where the cascade succeeded or stopped.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

