LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2119 min read

Hibernate Transactions

Learn how Hibernate transactions work, why every write should happen inside one, and how beginTransaction, commit, and rollback keep your data consistent.

Introduction

Every write you have done so far in this course — save, update, delete — has quietly happened inside a transaction, because the earlier examples wrapped each one in beginTransaction() and commit(). This lesson makes that machinery explicit: what a transaction actually is, why it matters, and what happens when something goes wrong partway through.

A transaction is a unit of work that either completes entirely or has no effect at all. If you are transferring money between two accounts and the debit succeeds but the credit fails, you do not want the debit to stick — a transaction lets you roll the whole operation back as one atomic step.

What You Will Learn
  • Why writes must happen inside a transaction
  • How to use beginTransaction, commit, and rollback
  • How to safely handle failures mid-transaction
  • A clean try-with-resources transaction pattern
  • How Hibernate transactions relate to ACID properties

Why Transactions Matter

Without a transaction boundary, Hibernate has no clear notion of where one logical operation ends and another begins. If your application crashes, or an exception is thrown, halfway through a multi-step update, an untransacted write can leave your database in an inconsistent state — for example, an order marked as paid with no corresponding payment record.

Transactions solve this by grouping related changes so the database only ever sees the "before" state or the fully-applied "after" state — never something in between.

The Transaction API

Hibernate exposes transactions through the org.hibernate.Transaction interface, obtained from a Session. The basic shape is always the same: begin, do work, commit — with rollback reserved for the failure path.

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employee emp = new Employee();
emp.setName("Priya Nair");
emp.setSalary(65000.0);
session.persist(emp);
tx.commit();
System.out.println("Employee saved successfully.");
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
e.printStackTrace();
} finally {
session.close();
}
Hibernate SQL Log

Click Run to see what this code prints.

Handling Rollback

Rollback undoes every change made since beginTransaction() was called, restoring the database to how it looked beforehand. This is essential when an operation involves multiple related writes and any one of them can fail.

Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Account from = session.get(Account.class, 1L);
Account to = session.get(Account.class, 2L);
from.setBalance(from.getBalance() - 500);
to.setBalance(to.getBalance() + 500);
if (from.getBalance() < 0) {
throw new IllegalStateException("Insufficient funds");
}
session.update(from);
session.update(to);
tx.commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
System.out.println("Transaction rolled back: " + e.getMessage());
} finally {
session.close();
}
Console Output (insufficient funds case)

Click Run to see what this code prints.

Both Accounts, or Neither

Because both balance updates happen inside the same transaction, a rollback undoes both — you will never end up with money deducted from one account without it being credited to the other.

Try-With-Resources Pattern

Session implements AutoCloseable, so a try-with-resources block keeps the session-closing logic out of a finally block and makes the transaction lifecycle easier to read.

try (Session session = factory.openSession()) {
Transaction tx = session.beginTransaction();
try {
Product product = new Product();
product.setName("Wireless Mouse");
product.setPrice(799.0);
session.persist(product);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
}

ACID in Practice

Transactions are how Hibernate (via the underlying database) delivers the ACID guarantees you may already know from database theory.

PropertyWhat It Means Here
Atomicitycommit() applies every change in the transaction; rollback() undoes all of them — never a partial result.
ConsistencyThe database moves from one valid state to another; constraints are never left violated mid-transaction.
IsolationConcurrent transactions do not see each other's uncommitted changes (governed by the database's isolation level).
DurabilityOnce commit() succeeds, the change survives even if the application crashes immediately after.

Common Mistakes

Avoid These Mistakes
  • Performing writes (persist, update, delete) without an explicit transaction and being surprised when nothing is saved.
  • Forgetting to call rollback() in the catch block, leaving the transaction hanging.
  • Calling commit() and then continuing to use the same transaction object as if it were still open.
  • Wrapping unrelated operations into one giant transaction, increasing lock contention and rollback blast radius.
  • Swallowing the exception after rollback() without logging or re-throwing, hiding real failures.

Best Practices

  • Keep transaction boundaries as tight as possible — only the operations that truly need to succeed or fail together.
  • Always pair beginTransaction() with either commit() or rollback(), never leave one hanging.
  • Close the Session in a finally block or use try-with-resources so it always releases its connection.
  • Log the exception before or during rollback so failures are not silently discarded.
  • In Spring applications, prefer @Transactional over manual beginTransaction/commit calls — covered in the Hibernate with Spring lesson.

Frequently Asked Questions

Yes, sequentially — begin, commit (or rollback), then begin again. You just cannot have two active transactions on the same Session at once.

Your changes are never written to the database. Depending on configuration, the transaction may simply remain open, or be rolled back when the session closes.

Not strictly for simple reads, but it is still good practice to wrap reads in a transaction, especially when lazy-loaded associations will be accessed afterward.

An exception is what typically triggers you to call rollback(). Throwing an exception alone does not undo database changes — rollback() is the explicit call that does that.

Key Takeaways

  • Every write to the database through Hibernate should happen inside a transaction.
  • beginTransaction() starts a unit of work; commit() applies it; rollback() undoes it.
  • Rollback keeps multi-step operations atomic — all changes apply, or none do.
  • Transactions are how Hibernate delivers ACID guarantees on top of the underlying database.
  • Always close the Session, typically with try-with-resources or a finally block.

Summary

Transactions are the safety net around every Hibernate write. By wrapping related changes in beginTransaction() and commit(), and rolling back on failure, you guarantee the database never ends up in a half-finished, inconsistent state — even when something goes wrong.

Next Lesson →

Hibernate Validator (Bean Validation)