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.
- Reading Hibernate configuration (
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()
, andget()
. - 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
-
What’s the difference between Hibernate and JPA?
JPA is a specification; Hibernate is an implementation with extra features. -
How does Hibernate caching improve performance?
It reduces database hits with session-level and second-level caches. -
What are the drawbacks of eager fetching?
Loads unnecessary data, increasing memory use. -
How do I solve the N+1 select problem?
UseJOIN FETCH
, batch fetching, or entity graphs. -
Can I use Hibernate without Spring?
Yes, Hibernate can run standalone. -
What’s the best inheritance mapping strategy?
JOINED
for normalized schemas,SINGLE_TABLE
for performance. -
How does Hibernate handle composite keys?
With@EmbeddedId
or@IdClass
. -
How is Hibernate 6 different from Hibernate 5?
Hibernate 6 usesjakarta.persistence
and improved SQL APIs. -
Is Hibernate suitable for microservices?
Yes, but use DTOs and avoid heavy entity graphs. -
When should I not use Hibernate?
Avoid in analytics or batch jobs requiring raw SQL performance.