DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How Stalactite ORM Implements Its Fluent DSL
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Understanding Java Signals
  • How Spring and Hibernate Simplify Web and Database Management

Trending

  • Why High-Performance AI/ML Is Essential in Modern Cybersecurity
  • A Deep Dive Into Firmware Over the Air for IoT Devices
  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • Kubeflow: Driving Scalable and Intelligent Machine Learning Systems
  1. DZone
  2. Coding
  3. Java
  4. Java Bean Validation: Applying Constraints Programmatically

Java Bean Validation: Applying Constraints Programmatically

Learn how to configure a Java bean validator in order to apply constraints programmatically within a Spring application.

By 
Maksim Kren user avatar
Maksim Kren
·
Sep. 24, 24 · Code Snippet
Likes (3)
Comment
Save
Tweet
Share
7.1K Views

Join the DZone community and get the full member experience.

Join For Free

Imagine you are working on a project that includes several entities implemented as part of the system. According to the required business logic, you need to enforce specific validation rules. However, these classes are provided as a third-party dependency, which means they cannot be modified and as a result cannot be annotated directly.

To tackle this scenario, we will explore a feature of the Hibernate Validator known as 12.4. Programmatic constraint definition and declaration from the documentation, and demonstrate how it can be integrated with the Spring Framework. 

The source code referenced in this article is available on GitHub via this link.

Use Case Description

Let's introduce a User entity to better understand our use case. This entity will act as a third-party dependency throughout our example:

Java
 
package com.example.bean.validation.entity;

public class User {

  private Long id;
  private String name;
  
  // Setters ommited
}


As observed, it is a Java POJO. However, remember that we cannot modify its code due to our specific use case constraints.

To validate the User's id, we have created a custom annotation named UserId:

Java
 
package com.example.bean.validation.constraint;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import jakarta.validation.ReportAsSingleViolation;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Range;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@NotNull
@Range(min = 1)
@ReportAsSingleViolation
@Constraint(validatedBy = {})
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
public @interface UserId {

  String message() default "${validatedValue} must be a positive long";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};
}


Furthermore, according to our business logic, the User's name must not be null. So, we are going to apply the existing NotNull constraint to this field.

Validator Setup

Now, let's examine our validator configuration, which performs the necessary operations to meet our use case requirements:

Java
 
package com.example.bean.validation.conf;

import com.example.bean.validation.constraint.UserIdDef;
import com.example.bean.validation.entity.User;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.cfg.context.TypeConstraintMappingContext;
import org.hibernate.validator.cfg.defs.NotNullDef;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory;

@Configuration
class ValidationConf {

  @Bean
  Validator validator(AutowireCapableBeanFactory autowireCapableBeanFactory) {
    HibernateValidatorConfiguration conf = Validation.byProvider(HibernateValidator.class).configure();
    ConstraintMapping constraintMapping = conf.createConstraintMapping();

    TypeConstraintMappingContext<User> context = constraintMapping.type(User.class);
    context.field("id").constraint(new UserIdDef());
    context.field("name").constraint(new NotNullDef());

    return conf.allowOverridingMethodAlterParameterConstraint(true)
        .addMapping(constraintMapping)
        .constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
        .buildValidatorFactory()
        .getValidator();
  }
}


Here, through the TypeConstraintMappingContext, we assign the necessary annotations to the User's fields by providing their respective constraint definition classes. For our custom UserId, its constraint definition UserIdDef must be implemented by us, as shown:

Java
 
package com.example.bean.validation.constraint;

import org.hibernate.validator.cfg.ConstraintDef;

public class UserIdDef extends ConstraintDef<UserIdDef, UserId> {

  public UserIdDef() {
    super(UserId.class);
  }
}


For the built-in NotNull constraint, the corresponding definition class NotNullDef is readily available.

Applying Validation Logic

In addition to our previous configurations, we have implemented a component using the Spring-driven method validation approach:

Java
 
package com.example.bean.validation.component;

import com.example.bean.validation.entity.User;
import jakarta.validation.Valid;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

@Validated
@Component
public class SomeComponent {

  public void handleUser(@Valid User user) {
    System.out.println("Got validated user " + user);
  }
}


Testing Constraints

To validate the SomeComponent, let's review test scenarios implemented to clearly understand the validation logic applied:

Java
 
package com.example.bean.validation.component;

import com.example.bean.validation.entity.User;
import jakarta.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

@SpringBootTest
class SomeComponentTest {

  @Autowired
  private SomeComponent someComponent;

  @Test
  void provideInvalidUser() {
    User user = new User();
    user.setId(-100L);
    user.setName(null);

    assertThatThrownBy(() -> someComponent.handleUser(user))
        .isInstanceOf(ConstraintViolationException.class)
        .hasMessageContaining("handleUser.arg0.id: -100 must be a positive long")
        .hasMessageContaining("handleUser.arg0.name: must not be null");
  }

  @Test
  void provideValidUser() {
    User user = new User();
    user.setId(1L);
    user.setName("Bob");

    assertDoesNotThrow(() -> someComponent.handleUser(user));
  }
}


Final Words

In this article, we have explored the use case of programmatically annotating fields of an entity. While this approach addresses our specific needs, other more complex scenarios are also possible. For additional information and advanced techniques, refer to the 12.4. Programmatic constraint definition and declaration in the Hibernate Validator documentation (linked earlier in the article).

Spring Framework entity Java (programming language) Data Types

Opinions expressed by DZone contributors are their own.

Related

  • How Stalactite ORM Implements Its Fluent DSL
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Understanding Java Signals
  • How Spring and Hibernate Simplify Web and Database Management

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!