You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证嵌套对象空值?订单DTO指定字段校验方案咨询

Great question! Dealing with large DTOs and repetitive null/empty checks is a common pain point, but you're in luck—there are mature frameworks and patterns that let you handle this cleanly without writing hundreds of if statements. Here's how to solve your problem step by step:

The most straightforward approach in Java/Spring ecosystems is using Spring Validation (backed by Hibernate Validator). It supports nested object validation, lets you target specific fields, and eliminates redundant check code entirely.

Step 1: Add Dependencies

If you're using Spring Boot, spring-boot-starter-web already includes validation dependencies. For non-Spring Boot projects, add Hibernate Validator to your pom.xml (or build.gradle):

<!-- Maven -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>8.0.1.Final</version>
</dependency>

Step 2: Annotate Your DTOs

Use standard validation annotations to mark required fields, and use @Valid to trigger nested validation for your List<Mapper>:

Mapper Class (Target Specific Fields)

Only add annotations to the fields you need to validate (firstName, lastName, Address):

import jakarta.validation.constraints.NotBlank;

public class Mapper {
    @NotBlank(message = "firstName cannot be null or empty")
    private String firstName;

    @NotBlank(message = "lastName cannot be null or empty")
    private String lastName;

    @NotBlank(message = "Address cannot be null or empty")
    private String Address;

    // Other fields (no annotations = no validation)
    private String otherField;

    // Getters and Setters
}

Order Class (Top-Level + Nested Validation)

Mark top-level fields with @NotBlank (checks for non-null + non-empty strings) and add @Valid to the properties list to validate each nested Mapper object:

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import java.util.List;

public class Order {
    @NotBlank(message = "orderId cannot be null or empty")
    private String orderId;

    @NotBlank(message = "sourceOrder cannot be null or empty")
    private String sourceOrder;

    @NotBlank(message = "destinationSystem cannot be null or empty")
    private String destinationSystem;

    @NotBlank(message = "orderingTimeStamp cannot be null or empty")
    private String orderingTimeStamp;

    // @Valid tells the validator to check each Mapper in the list
    @Valid
    private List<Mapper> properties;

    // Getters and Setters
}

Step 3: Validate in Your Service/Controller

Option 1: Controller-Level Validation (Spring Web)

If you're receiving requests directly in a Spring controller, use @Valid on the request body and BindingResult to capture errors:

import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.stream.Collectors;

@RestController
public class OrderController {

    @PostMapping("/orders")
    public ResponseEntity<String> processOrder(@Valid @RequestBody Order order, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            // Collect error messages and return a bad request response
            String errorMessage = bindingResult.getAllErrors()
                    .stream()
                    .map(error -> error.getDefaultMessage())
                    .collect(Collectors.joining(", "));
            return ResponseEntity.badRequest().body("Order rejected: " + errorMessage);
        }

        // Validation passed—proceed with order processing
        orderService.handleOrder(order);
        return ResponseEntity.ok("Order processed successfully");
    }
}

Option 2: Service-Level Validation

For validation in a service layer (or non-web contexts), inject the Validator and manually trigger validation:

import jakarta.validation.Validator;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class OrderService {
    private final Validator validator;

    // Inject Validator via constructor
    public OrderService(Validator validator) {
        this.validator = validator;
    }

    public void handleOrder(Order order) {
        Set<jakarta.validation.ConstraintViolation<Order>> violations = validator.validate(order);
        if (!violations.isEmpty()) {
            String errorMessage = violations.stream()
                    .map(violation -> violation.getMessage())
                    .collect(Collectors.joining(", "));
            throw new IllegalArgumentException("Invalid order: " + errorMessage);
        }

        // Business logic here
    }
}

Custom Validation for Edge Cases

If you need more complex rules (e.g., custom format checks for Address), you can create a custom validation annotation and validator:

// Custom annotation
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = ValidAddressValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidAddress {
    String message() default "Invalid address format";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// Validator implementation
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class ValidAddressValidator implements ConstraintValidator<ValidAddress, String> {
    @Override
    public boolean isValid(String address, ConstraintValidatorContext context) {
        // Add your custom address validation logic here
        return address != null && address.length() >= 5 && address.contains(",");
    }
}

Then replace @NotBlank on the Address field with @ValidAddress.

Relevant Design Patterns

  • Separation of Concerns: Validation logic is decoupled from business logic, making your code cleaner and easier to maintain.
  • Strategy Pattern: Each validation rule (like @NotBlank, @ValidAddress) acts as a strategy that the validator executes—you can add/remove rules without changing core processing code.
  • Decorator Pattern: You can wrap your order processing service with a validation decorator that checks the order before passing it to the actual processing logic.

内容的提问来源于stack exchange,提问作者mayank bisht

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:27:57