Hibernate Transaction Rollback Example

This tutorial will guide you through setting up and demonstrating a transaction rollback in Hibernate 6 with Java 21. We'll create a simple application that performs database operations within a transaction and triggers a rollback in case of an exception.

Introduction

Transactions in Hibernate allow multiple operations to be grouped into a single unit of work. If any operation fails, the transaction can be rolled back, ensuring data integrity. This tutorial will demonstrate how to handle transactions and rollbacks in Hibernate.

In this tutorial, we will:

  1. Set up a Maven project with Hibernate and an H2 database dependency.
  2. Configure Hibernate.
  3. Create an entity class (User).
  4. Implement a method to perform database operations with transaction management.
  5. Demonstrate transaction rollback 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 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-rollback-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>

        <!-- 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 User that will be mapped to a table in the database. This class uses annotations to define the entity and its fields.

package com.example.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    // 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 getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

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.

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 Transaction Management

Create a class UserService to handle database operations with transaction management. This class includes a method to create a user and demonstrates how to handle transactions and rollbacks.

package com.example.service;

import com.example.entity.User;
import com.example.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class UserService {

    public void createUser(String name, String email) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = null;

        try {
            transaction = session.beginTransaction();
            User user = new User();
            user.setName(name);
            user.setEmail(email);
            session.save(user);
            // Simulate an error
            if (name == null) {
                throw new RuntimeException("Name cannot be null");
            }
            transaction.commit();  // Commit the transaction
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
                System.out.println("Transaction rolled back due to: " + e.getMessage());
            }
            e.printStackTrace();
        } finally {
            session.close();
        }
    }
}

Explanation:

  • The createUser method opens a Hibernate session and begins a transaction.
  • A new User object is created and saved to the database.
  • If the name is null, an exception is thrown to simulate an error.
  • The transaction is committed using transaction.commit() if no exception occurs, ensuring the changes are saved in the database.
  • If an exception occurs, the transaction is rolled back using transaction.rollback().
  • The session is closed in the finally block to release resources.

Step 5: Demonstrate Transaction Rollback

Create a MainApp class to demonstrate the transaction rollback functionality. This class calls the createUser method of UserService to perform database operations and trigger a rollback in case of an error.

package com.example.main;

import com.example.service.UserService;

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

        // Create a user with valid data
        userService.createUser("Ramesh Fadatare", "[email protected]");
        System.out.println("User created with valid data!");

        // Attempt to create a user with invalid data to trigger rollback
        userService.createUser(null, "[email protected]");
    }
}

Explanation:

  1. Create a UserService Instance:

    UserService userService = new UserService();
    

    An instance of UserService is created to call its methods for performing database operations.

  2. Create a User with Valid Data:

    userService.createUser("Ramesh Fadatare", "[email protected]");
    System.out.println("User created with valid data!");
    

The createUser method is called with valid data. This will add a new user to the database and commit the transaction. A message is printed to indicate that the user has been created with valid data.

3. Attempt to Create a User with Invalid Data:


userService.createUser(null, "[email protected]");

The createUser method is called with a null name, which will trigger an exception and cause the transaction to be rolled back. The exception is caught in the createUser method, and a rollback is performed, ensuring no partial or invalid data is committed to the database.

Sample Output

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

User created with valid data!
Transaction rolled back due to: Name cannot be null

This output indicates that the first user was successfully created, and the second operation was rolled back due to the exception, ensuring data integrity.

Conclusion

In this tutorial, we have successfully demonstrated how to manage transactions and handle rollbacks in Hibernate. We set up a Hibernate project, created an entity class, and implemented transaction management with rollback functionality. This guide provides a solid foundation for managing transactions and ensuring data integrity in your Hibernate-based applications.

Comments