java 141 lines · 4 tabs

Bind and Validate a Date-Range Search with @ModelAttribute and a Custom PropertyEditor

Shared by codesnips Sep 2026
4 tabs
package com.example.search;

import java.time.LocalDate;

@ValidDateRange
public class DateRangeSearch {

    private LocalDate from;
    private LocalDate to;
    private String keyword;

    public LocalDate getFrom() {
        return from;
    }

    public void setFrom(LocalDate from) {
        this.from = from;
    }

    public LocalDate getTo() {
        return to;
    }

    public void setTo(LocalDate to) {
        this.to = to;
    }

    public String getKeyword() {
        return keyword;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword == null ? null : keyword.trim();
    }
}
4 files · java Explain with highlit

This snippet shows how Spring MVC binds query parameters onto a command object with @ModelAttribute, converts raw date strings through a PropertyEditor, and enforces a cross-field rule that the start must not be after the end. It is the classic pattern for a search form where the URL carries from and to parameters and the controller wants a typed, validated object instead of loose strings.

The DateRangeSearch command class is a plain POJO holding from, to, and an optional keyword. It carries a bean-validation constraint at the type level, @ValidDateRange, which expresses a relationship between two fields — something field-level annotations like @NotNull cannot capture. The two LocalDate fields are populated by the binder rather than parsed by hand, which keeps the controller free of string wrangling.

LocalDateEditor extends PropertyEditorSupport and is the bridge between the raw request string and a LocalDate. Its setAsText trims input, treats blank as null, and rethrows a DateTimeParseException as an IllegalArgumentException so Spring records a proper field-level binding error instead of blowing up. Implementing getAsText makes the editor symmetric so the value renders back correctly when the form redisplays.

The editor is not global by default; @InitBinder in SearchController registers it per-controller via binder.registerCustomEditor(LocalDate.class, new LocalDateEditor(...)). Using a strict DateTimeFormatter here means the parse format is a controller decision, not a scattered constant. The search handler takes @Valid @ModelAttribute plus a BindingResult; the BindingResult must immediately follow the validated object or Spring throws before the method runs.

Binding and type-conversion errors surface as field errors, while the class-level range check surfaces as a global error. When result.hasErrors() is true the handler returns to the form view instead of querying, so invalid input never reaches the service layer. ValidDateRange and its DateRangeValidator implement the ConstraintValidator contract; the validator returns true when either bound is null so the null case is left to other constraints, avoiding double-reporting. This separation — editor for conversion, validator for business rules, controller for flow — is why the request handler stays this small.


Related snips

graphql
type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
    createdAt: String!

GraphQL API with Spring Boot

java graphql spring-boot
by David Kumar 3 tabs
ruby
class SignupForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :account_name, :string
  attribute :email, :string

Shallow Controller, Deep Params: Form Object Pattern

rails activemodel form-object
by codesnips 3 tabs
javascript
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["form"]
  static values = { delay: { type: Number, default: 250 } }

Debounced live search with Stimulus + Turbo Streams

rails hotwire stimulus
by codesnips 4 tabs
java
package com.example.starter.config;

import com.example.starter.properties.CustomProperties;
import com.example.starter.service.CustomService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

Custom Spring Boot starters

java spring-boot starter
by David Kumar 4 tabs
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Form Validation Example</title>
  <style>

HTML forms with validation and accessibility

html forms validation
by Alex Chang 1 tab
java
package com.example.demo.config;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;

Messaging with Apache Kafka

java kafka messaging
by David Kumar 3 tabs

Share this code

Here's the card — post it anywhere.

Bind and Validate a Date-Range Search with @ModelAttribute and a Custom PropertyEditor — share card
Link copied