Hibernate Validator Tutorial

This tutorial will guide you through setting up and demonstrating the use of Hibernate Validator in Hibernate 6 with Java 21. We'll create a simple application that performs validation on a Product entity.

Introduction

Hibernate Validator is the reference implementation of Bean Validation (JSR 380). It allows you to define validation constraints on your domain model using annotations. In this tutorial, we will:

  1. Set up a Maven project with Hibernate Validator and an H2 database dependency.
  2. Configure Hibernate.
  3. Create an entity class (Product) with validation constraints.
  4. Implement a method to validate the Product entity.
  5. Demonstrate validation with a sample application.

Step 1: Set Up Your Project

1.1 Create a Maven Project

Open your IDE and create a new Maven project.

1.2 Add Dependencies

Update your pom.xml file to include the necessary dependencies for Hibernate, Hibernate Validator, and H2 (an in-memory database for simplicity).

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>hibernate-validator-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Hibernate ORM -->
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.4.0.Final</version>
        </dependency>

        <!-- Hibernate Validator -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>8.0.0.Final</version>
        </dependency>

        <!-- Validation API -->
        <dependency>
            <groupId>jakarta.validation</groupId>
            <artifactId>jakarta.validation-api</artifactId>
            <version>3.0.2</version>
        </dependency>

        <!-- H2 Database -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.1.214</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

1.3 Configure Hibernate

Create a file named hibernate.cfg.xml in the src/main/resources directory to configure Hibernate. This file contains the database connection settings and Hibernate properties.

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
        <property name="hibernate.connection.driver_class">org.h2.Driver</property>
        <property name="hibernate.connection.url">jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1</property>
        <property name="hibernate.connection.username">sa</property>
        <property name="hibernate.connection.password"></property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>
    </session-factory>
</hibernate-configuration>

Explanation:

  • hibernate.dialect specifies the SQL dialect to be used.
  • hibernate.connection.driver_class specifies the JDBC driver class.
  • hibernate.connection.url specifies the JDBC URL for the database connection.
  • hibernate.connection.username and hibernate.connection.password specify the database credentials.
  • hibernate.hbm2ddl.auto specifies the schema generation strategy.
  • hibernate.show_sql specifies whether to show SQL statements in the logs.

Step 2: Create the Entity Class

Create an entity class Product that will be mapped to a table in the database. This class uses annotations to define the entity, its fields, and validation constraints.

package com.example.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull(message = "Name cannot be null")
    @Size(min = 2, max = 50, message = "Name must be between 2 and 50 characters")
    private String name;

    @NotNull(message = "Description cannot be null")
    @Size(min = 5, max = 200, message = "Description must be between 5 and 200 characters")
    private String description;

    @Min(value = 0, message = "Price must be greater than or equal to 0")
    private double price;

    // Getters and setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

Explanation:

  • The @Entity annotation specifies that the class is an entity and is mapped to a database table.
  • The @Id annotation specifies the primary key of the entity.
  • The @GeneratedValue(strategy = GenerationType.IDENTITY) annotation specifies that the primary key is auto-incremented.
  • The @NotNull annotation ensures that the field cannot be null.
  • The @Size annotation specifies the size constraints for the field.
  • The @Min annotation ensures that the price is greater than or equal to 0.

Step 3: Create the Hibernate Utility Class

Create a utility class HibernateUtil to manage the Hibernate SessionFactory. This class ensures a single instance of SessionFactory is created and provides a method to close it.

package com.example.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        // Close caches and connection pools
        getSessionFactory().close();
    }
}

Explanation:

  • The buildSessionFactory method creates the SessionFactory from the hibernate.cfg.xml configuration file.
  • The getSessionFactory method returns the singleton instance of SessionFactory.
  • The shutdown method closes the SessionFactory to release resources.

Step 4: Implement Validation

Create a class ProductService to handle database operations and validation. This class includes methods to validate and save the Product entity.

Create and Validate Product

package com.example.service;

import com.example.entity.Product;
import com.example.util.HibernateUtil;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;

import java.util.Set;

public class ProductService {

    private Validator validator;

    public ProductService() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }

    public void validateAndSaveProduct(Product product) {
        Set<ConstraintViolation<Product>> violations = validator.validate(product);

        if (!violations.isEmpty()) {
            for (ConstraintViolation<Product> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            saveProduct(product);
        }
    }

    private void saveProduct(Product product) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = null;

        try {
            transaction = session.beginTransaction();
            session.save(product);
            transaction.commit();
            System.out.println("Product saved successfully");
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            session.close();
        }
    }
}

Explanation:

  • The ProductService constructor initializes the Validator instance.
  • The validateAndSaveProduct method

validates the Product entity. If there are validation violations, they are printed to the console. If there are no violations, the saveProduct method is called to save the product to the database.

  • The saveProduct method opens a Hibernate session, begins a transaction, saves the product, commits the transaction, and closes the session.

Step 5: Demonstrate Validation

Create a MainApp class to demonstrate the validation functionality. This class calls the validateAndSaveProduct method of ProductService.

package com.example.main;

import com.example.entity.Product;
import com.example.service.ProductService;

public class MainApp {
    public static void main(String[] args) {
        ProductService productService = new ProductService();

        // Create a valid product
        Product validProduct = new Product();
        validProduct.setName("Laptop");
        validProduct.setDescription("A high-performance laptop");
        validProduct.setPrice(1500.00);

        // Validate and save the valid product
        System.out.println("Validating and saving valid product:");
        productService.validateAndSaveProduct(validProduct);

        // Create an invalid product
        Product invalidProduct = new Product();
        invalidProduct.setName("P");
        invalidProduct.setDescription("Short");
        invalidProduct.setPrice(-50.00);

        // Validate and save the invalid product
        System.out.println("Validating and saving invalid product:");
        productService.validateAndSaveProduct(invalidProduct);
    }
}

Explanation:

  1. Create a ProductService Instance:

    ProductService productService = new ProductService();
    

    An instance of ProductService is created to call its methods for performing database operations and validation.

  2. Create a Valid Product:

    Product validProduct = new Product();
    validProduct.setName("Laptop");
    validProduct.setDescription("A high-performance laptop");
    validProduct.setPrice(1500.00);
    

    A valid product is created with appropriate values for its fields.

  3. Validate and Save the Valid Product:

    System.out.println("Validating and saving valid product:");
    productService.validateAndSaveProduct(validProduct);
    

    The validateAndSaveProduct method is called to validate and save the valid product. Since the product is valid, it will be saved to the database.

  4. Create an Invalid Product:

    Product invalidProduct = new Product();
    invalidProduct.setName("P");
    invalidProduct.setDescription("Short");
    invalidProduct.setPrice(-50.00);
    

    An invalid product is created with inappropriate values for its fields.

  5. Validate and Save the Invalid Product:

    System.out.println("Validating and saving invalid product:");
    productService.validateAndSaveProduct(invalidProduct);
    

    The validateAndSaveProduct method is called to validate and save the invalid product. Since the product is invalid, validation violations will be printed to the console, and the product will not be saved.

Sample Output

When you run the MainApp class, you should see the following output:

Validating and saving valid product:
Product saved successfully
Validating and saving invalid product:
Name must be between 2 and 50 characters
Description must be between 5 and 200 characters
Price must be greater than or equal to 0

This output indicates that the valid product was successfully saved, and the invalid product failed validation with the appropriate error messages.

Conclusion

In this tutorial, we have successfully demonstrated how to use Hibernate Validator to validate entities in Hibernate. We set up a Hibernate project, created an entity class with validation constraints, and implemented validation functionality. This guide provides a solid foundation for ensuring data integrity and enforcing business rules in your Hibernate-based applications.

Comments