Hibernate Session and SessionFactory Explained with Examples

Illustration for Hibernate Session and SessionFactory Explained with Examples
By Last updated:

When working with Hibernate, two of the most important concepts are Session and SessionFactory. They form the backbone of Hibernate’s architecture and are responsible for interacting with the database, managing entities, and handling transactions.

  • SessionFactory is a heavyweight object used to create and manage Hibernate Sessions.
  • Session is a lightweight object representing a single unit of work with the database.

In this tutorial, we’ll explore how they work, how to configure them, and how to use them effectively with real examples. We’ll also cover best practices, common pitfalls, and integration scenarios.


What is SessionFactory?

  • A SessionFactory is a thread-safe, heavyweight object created once during application startup.
  • It is responsible for:
    • Reading Hibernate configuration (hibernate.cfg.xml).
    • Providing Session instances for database operations.
    • Managing second-level cache.

Think of it like a restaurant kitchen: built once, serving many customers.

Example: Creating SessionFactory

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

public class HibernateUtil {
    private static final SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new Configuration()
                    .configure("hibernate.cfg.xml")
                    .addAnnotatedClass(Student.class)
                    .buildSessionFactory();
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

What is Session?

  • A Session is a lightweight, non-thread-safe object used for a single unit of work (e.g., one CRUD operation or transaction).
  • Responsibilities:
    • Represents a connection between Java application and database.
    • Provides methods like save(), update(), delete(), and get().
    • Manages the first-level cache.

Think of it as your dining table in the restaurant: unique for each customer.

Example: Opening a Session

Session session = HibernateUtil.getSessionFactory().openSession();

Hibernate Configuration (hibernate.cfg.xml)

<hibernate-configuration>
  <session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/testdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">root</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
    <property name="hibernate.hbm2ddl.auto">update</property>
    <property name="hibernate.show_sql">true</property>
    <mapping class="com.example.Student"/>
  </session-factory>
</hibernate-configuration>

Entity Example

import jakarta.persistence.*;

@Entity
@Table(name = "students")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "student_name", nullable = false)
    private String name;

    public Student() {}
    public Student(String name) { this.name = name; }

    // getters and setters
}

CRUD Operations with Session

Create

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Student student = new Student("Alice");
session.persist(student);

tx.commit();
session.close();

Read

Session session = HibernateUtil.getSessionFactory().openSession();
Student student = session.get(Student.class, 1L);
System.out.println(student.getName());
session.close();

Update

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Student student = session.get(Student.class, 1L);
student.setName("Updated Alice");
session.update(student);

tx.commit();
session.close();

Delete

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Student student = session.get(Student.class, 1L);
session.remove(student);

tx.commit();
session.close();

Querying with Hibernate

HQL

List<Student> students = session.createQuery("FROM Student WHERE name = :name", Student.class)
        .setParameter("name", "Alice")
        .list();

Criteria API

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> root = cq.from(Student.class);
cq.select(root).where(cb.equal(root.get("name"), "Alice"));
List<Student> results = session.createQuery(cq).getResultList();

Native SQL

List<Object[]> results = session.createNativeQuery("SELECT * FROM students").list();

Transactions

  • Every Session should be wrapped in a Transaction.
  • Ensures ACID properties.
  • Rollbacks on errors.

Example:

Transaction tx = session.beginTransaction();
session.persist(new Student("Bob"));
tx.commit();

Caching and Performance

  • First-Level Cache → Session-specific, enabled by default.
  • Second-Level Cache → Application-wide, managed by SessionFactory (EHCache, Infinispan).
  • Lazy vs Eager Fetching:
    • Lazy = fetch when needed (like ordering food later).
    • Eager = fetch immediately (like ordering all food upfront).

Real-World Integration with Spring Boot

Spring Boot simplifies SessionFactory management via Spring Data JPA.
Example configuration:

spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

Repository example:

public interface StudentRepository extends JpaRepository<Student, Long> {}

Common Pitfalls

  • Forgetting to close Session → leads to memory leaks.
  • Using SessionFactory repeatedly → should be created only once.
  • Eager fetching misuse → loads unnecessary data.
  • Improper cascading → accidental deletions.

Best Practices

  • Always close sessions after use.
  • Create only one SessionFactory per application.
  • Use lazy fetching by default.
  • Use DTOs instead of exposing entities.
  • Monitor performance with Hibernate statistics.

📌 Hibernate Version Notes

Hibernate 5.x

  • Relied on javax.persistence.
  • Classic SessionFactory APIs.
  • Legacy Criteria API.

Hibernate 6.x

  • Migrated to jakarta.persistence.
  • Improved bootstrapping and query APIs.
  • Better SQL integration.

Conclusion and Key Takeaways

  • SessionFactory: heavyweight, created once, provides Sessions, manages second-level cache.
  • Session: lightweight, short-lived, handles CRUD, transactions, and first-level cache.
  • Always use transactions, caching strategies, and best practices for production.
  • Hibernate 6 modernizes Session APIs and improves SQL handling.

FAQ: Expert Hibernate Questions

  1. What’s the difference between Hibernate and JPA?
    JPA is a specification; Hibernate is an implementation with extra features.

  2. How does Hibernate caching improve performance?
    It reduces database hits with session-level and second-level caches.

  3. What are the drawbacks of eager fetching?
    Loads unnecessary data, increasing memory use.

  4. How do I solve the N+1 select problem?
    Use JOIN FETCH, batch fetching, or entity graphs.

  5. Can I use Hibernate without Spring?
    Yes, Hibernate can run standalone.

  6. What’s the best inheritance mapping strategy?
    JOINED for normalized schemas, SINGLE_TABLE for performance.

  7. How does Hibernate handle composite keys?
    With @EmbeddedId or @IdClass.

  8. How is Hibernate 6 different from Hibernate 5?
    Hibernate 6 uses jakarta.persistence and improved SQL APIs.

  9. Is Hibernate suitable for microservices?
    Yes, but use DTOs and avoid heavy entity graphs.

  10. When should I not use Hibernate?
    Avoid in analytics or batch jobs requiring raw SQL performance.