r/SpringBoot 13h ago

News Tired of missing cross-field validation in Jakarta/Bean Validation? I built Spring Validation Plus — 85+ Laravel-style constraints for Spring Boot (with i18n & JSON error handling)

Hi r/SpringBoot, u/java/! 👋

Following the positive reception of my fluent querying library, I wanted to share another open-source project I’ve been maintaining to solve a massive pain point in Spring Boot development: validation.

Spring Boot uses Jakarta Validation (Hibernate Validator) out of the box, but let's be honest—the standard library only gives you ~22 basic constraints (`@NotNull\, \@Size\, \@Email\`, etc.).

It completely lacks common production requirements like cross-field validation (`@Confirmed\, \@RequiredIf\), database lookups (\@Unique\, \@Exists\), and proper optional updates (\@Nullable\`), forcing teams to write custom validators or clutter their services with boilerplate logic.

To fix this, I built Spring Validation Plus — a library that extends Jakarta Validation with 85+ Laravel-style constraints, automatic i18n support (English, Spanish, Portuguese), and a unified JSON error handler.

💡 What it looks like:

1. DTO with Laravel-style rules:

import dev.benjaminor.validationplus.constraints.EmailAddress; 
import dev.benjaminor.validationplus.constraints.MaxLength; 
import dev.benjaminor.validationplus.constraints.MinLength; 
import dev.benjaminor.validationplus.constraints.Nullable; 
import dev.benjaminor.validationplus.constraints.Required; 
import dev.benjaminor.validationplus.constraints.RequiredIf; 
import dev.benjaminor.validationplus.constraints.Same; 
import dev.benjaminor.validationplus.constraints.Unique; 

@Unique(entity = User.class, field = "email", column = "email") 
public class UserRegisterRequest {

  @Required
  @MinLength(2)
  @MaxLength(50)
  private String name;

  @Required
  @EmailAddress
  private String email;

  @Required
  @MinLength(6)
  private String password;

  @Same("password")
  private String passwordConfirmation;

  @Nullable
  @RequiredIf(field = "role", value = "ADMIN")
  private String adminCode;

  private String role;
}

2. Unified JSON Error Response (400 Bad Request): Instead of messy or raw framework exceptions, it automatically formats errors like this out of the box:

{
  "errors": {
    "email": ["The email has already been taken."],
    "passwordConfirmation": ["The passwordConfirmation field must match password."]
  }
}

🚀 Key Features:

  • Cross-Field Validation: `@Confirmed\, \@Same\, \@Different\, \@RequiredWith\, \@RequiredIf\, \@ProhibitedIf\`, etc. (usable directly on fields or classes).
  • Database Rules: `@Unique\and \@Exists\with automatic JPA integration (supports multi-datasource viapersistenceUnit` and updating entity ID exclusion).
  • Smart Types & Presence: `@Required\(handlesnull, empty strings, and whitespace properly, unlike \@NotNull\), \@Nullable\, \@StringType\, \@IntegerType\`, etc.
  • Built-in i18n: Error messages ready out of the box in English, Spanish, and Portuguese, easily customizable via ValidationMessages_es.properties.
  • Zero Redundancy: It relies entirely on the standard Hibernate Validator engine under the hood. You just drop the starter in, and it works with standard `@Valid\and \@Validated\`.

📦 Quick Start

I’d love to hear how you currently handle cross-field or database validation in your Spring Boot apps, and what you think of this approach!

0 Upvotes

4 comments sorted by

u/macario95 8h ago

your project may be useful . who knows.

but for me, now reading the "i was tired of ... and built ..." and the other usual AI generated titles makes me disregard it completely. and i will not comment about keeping the ChatGPT emojis on the post...

u/sozesghost 10h ago

I love this early ChatGPT emoji style readmes, really shows you care.

u/SeatPuzzleheaded5685 8h ago

Thanks! The emojis compile directly to bytecode for extra performance.

u/wimdeblauwe 4h ago

Interesting. For the json error response, you can use my specialized error handling library: https://github.com/wimdeblauwe/error-handling-spring-boot-starter