HIbernate Inteview Question
๐ Key Differences Between CrudRepository
and JpaRepository
๐ง Summary
Use
CrudRepository
for basic CRUD operations.Use
JpaRepository
when you need pagination, sorting, batch operations, or JPA-specific features.
Since
JpaRepository
extendsCrudRepository
, it includes everythingCrudRepository
offers — and more.
Feature | CrudRepository | JpaRepository |
---|---|---|
Inheritance | Base interface for CRUD operations | Extends CrudRepository |
Basic Methods | save() , findById() , delete() | Inherits all from CrudRepository |
Additional Features | ❌ No extra features | ✅ Adds JPA-specific methods |
Batch Operations | ❌ Not supported | ✅ deleteInBatch() , saveAll() |
Pagination & Sorting | ❌ Not supported | ✅ findAll(Pageable) , findAll(Sort) |
Flush Support | ❌ No | ✅ flush() , saveAndFlush() |
Use Case | Simple CRUD-only repositories | Advanced JPA features and query customization |
๐ Session vs SessionFactory
๐ Session vs SessionFactory — Refined Understanding
SessionFactory is like a blueprint or factory that knows how to create sessions. It’s built once when your application starts and holds all the configuration, mappings, and connection settings. Think of it as the central hub that knows how to talk to the database.
Session is the actual conversation between your app and the database. Every time you want to do something — like save a user, fetch a URL mapping, or delete a click event — you open a new session. It’s lightweight, short-lived, and specific to that task or request.
๐ง Analogy Time
Imagine a call center:
SessionFactory is the entire call center setup — the infrastructure, the phone lines, the training manuals.
Session is each individual phone call — a temporary, focused interaction between a customer and an agent.
You don’t rebuild the call center for every call. You just pick up the phone (create a session) when needed.
๐ Lifecycle Summary
Component | Created When? | Lives How Long? | Used For? |
---|---|---|---|
SessionFactory | Once at app startup | Entire app lifecycle | Creating sessions, caching metadata |
Session | Per request/transaction | Short-lived (per task) | Performing DB operations |
If you're using Spring Boot, it wraps this logic inside EntityManagerFactory
and EntityManager
, but the core idea remains the same.
๐ญ SessionFactory — The Factory
Purpose: Creates
Session
objects.Lifecycle: Long-lived; typically one per application.
Performance: Expensive to create, so it's initialized once at startup.
Responsibilities:
Loads Hibernate configuration and mappings.
Manages connection pooling.
Handles second-level caching.
Provides access to metadata and services.
๐ง You build it once, and it serves as the backbone for all database interactions.
๐ Session — The Worker
Purpose: Represents a single unit of work with the database.
Lifecycle: Short-lived; created per transaction or request.
Thread Safety: ❌ Not thread-safe.
Responsibilities:
Performs CRUD operations (
save()
,get()
,delete()
, etc.).Manages first-level cache (session-level).
Handles transaction boundaries.
Wraps a JDBC connection internally.
๐ง It’s like a temporary workspace where you load, modify, and persist entities.
๐ Summary Table
Feature | SessionFactory | Session |
---|---|---|
Scope | Application-wide | Per transaction/request |
Thread-safe | Yes | No |
Lifecycle | Long-lived | Short-lived |
Purpose | Creates Sessions | Performs DB operations |
Caching | Second-level cache | First-level cache |
Cost to create | High | Low |
If you’re using Spring Boot, it often manages these under the hood via EntityManagerFactory
, but understanding this distinction helps when debugging or optimizing performance.
๐ง First-Level Cache (Session Cache)
Scope: Tied to a single Hibernate
Session
Enabled by default: Yes
Purpose: Prevents repeated database hits for the same entity within one session
Example: If you fetch a
User
entity twice in the same session, Hibernate will serve the second request from memory, not the database
Session session = sessionFactory.openSession();
User user1 = session.get(User.class, 1); // Hits DB
User user2 = session.get(User.class, 1); // Served from cache
๐ง Second-Level Cache (SessionFactory Cache)
Scope: Shared across multiple sessions
Enabled by default: No — you must configure it
Purpose: Stores entities across sessions to reduce DB load for frequently accessed data
Backed by: External cache providers like EHCache, Redis, or Infinispan
// Session 1
Session session1 = sessionFactory.openSession();
User user1 = session1.get(User.class, 1); // Hits DB and stores in 2nd-level cache
// Session 2
Session session2 = sessionFactory.openSession();
User user2 = session2.get(User.class, 1); // Served from 2nd-level cache
๐ Summary Table
Feature | First-Level Cache | Second-Level Cache |
---|---|---|
Scope | Per Session | Across Sessions |
Enabled by Default | ✅ Yes | ❌ No (must configure) |
Configuration Needed | No | Yes (choose provider + setup) |
Use Case | Avoid duplicate DB hits in same session | Improve performance across sessions |
Hibernate uses these caching layers to reduce database access, improve response time, and scale better under load. If you're building a high-performance app, configuring second-level caching can be a game-changer.
Want help setting up EHCache or Redis for second-level caching in your project?
๐ง First-Level Cache (Session Cache)
Scope: Tied to a single Hibernate
Session
Enabled by default: Yes
Purpose: Prevents repeated database hits for the same entity within one session
Example: If you fetch a
User
entity twice in the same session, Hibernate will serve the second request from memory, not the database.
๐ง Second-Level Cache (SessionFactory Cache)
Scope: Shared across multiple sessions
Enabled by default: No — you must configure it
Purpose: Stores entities across sessions to reduce DB load for frequently accessed data
Backed by: External cache providers like EHCache, Redis, or Infinispan.
How does hibernate manage transaction?
Hibernate manages transactions using its own Transaction API, which wraps around JDBC or JTA (Java Transaction API) depending on your setup. The goal is to ensure that a group of database operations either fully succeed or fully fail—preserving data integrity through the ACID principles: Atomicity, Consistency, Isolation, and Durability.
๐ง Two Ways Hibernate Manages Transactions
1. Programmatic Transaction Management
You manually control the transaction lifecycle in your code:
Transaction transaction = null;
try (Session session = sessionFactory.openSession()) {
transaction = session.beginTransaction();
// Perform DB operations
session.save(entity);
transaction.commit(); // All good
} catch (Exception e) {
if (transaction != null) transaction.rollback(); // ❌ Rollback on error
e.printStackTrace();
}
2. Declarative Transaction Management (Spring + Hibernate)
You let Spring handle it using annotations:
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
๐ง Real-World Example
Imagine a job portal where users can filter jobs by location, salary, experience, and company. Instead of writing dozens of static queries, you use Criteria API to build one dynamic query that adapts to whatever filters the user selects.
Where Do We Use Criteria API?
Search Filters in Web Apps For example, filtering users by name, age, or location based on user input.
Admin Dashboards When building dynamic reports or analytics with multiple optional filters.
Multi-Field Search Forms Criteria API shines when you don’t know in advance which fields will be used for filtering.
Complex Business Logic Queries When queries involve multiple joins, conditions, or aggregations.
Reusable Query Components You can modularize query logic and reuse it across services or repositories.
Let’s build a real-world example using the Criteria API based on your project context. Suppose you have a User
entity and you want to search users dynamically based on optional filters like:
First name
Email
Role
Here’s how you can do it using Hibernate’s JPA Criteria API:
๐ What’s Happening Here
We use
CriteriaBuilder
to start building the query.Root<User>
defines the entity/table we’re querying.We dynamically add
Predicate
s based on which filters are provided.The final query is executed with all active conditions.
๐ง Why This Is Useful
You don’t need to write multiple query methods for different combinations of filters.
It’s safe, clean, and easy to extend (e.g., add pagination, sorting, etc.).
This error is a classic Spring Boot startup failure caused by a missing or misconfigured MySQL JDBC driver. Let's break it down and walk through how to fix it:
๐งฉ Root Cause
The key part of the error is:
Cannot load driver class: com.mysql.cj.jdbc.Driver
This means Spring Boot is trying to initialize a MySQL datasource using HikariCP, but it can't find the MySQL driver class in the classpath.
๐ ️ How to Fix It
1. Add MySQL JDBC Driver Dependency
If you're using Maven, make sure your pom.xml
includes:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- Use latest stable version -->
</dependency>
If you're using Gradle:
implementation 'mysql:mysql-connector-java:8.0.33'
2. Check Your application.properties
or application.yml
Make sure your datasource configuration is correct:
spring.datasource.url=jdbc:mysql://localhost:3306/your_database
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Or in YAML:
spring:
datasource:
url: jdbc:mysql://localhost:3306/your_database
username: your_username
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
3. Ensure the Driver Is in the Classpath
If you're running the app outside your IDE (e.g., via java -jar
), make sure the MySQL driver is bundled in your final JAR or available in the classpath.
4. Clean and Rebuild
Sometimes stale builds cause issues. Run:
mvn clean install
Or for Gradle:
./gradlew clean build
Want help checking your full config or build file? Just paste it here and I’ll walk through it with you.
Ah, this is a classic Hibernate mapping misstep. The error you're seeing:
Property 'com.example.url_shortener.Entity.UrlMapping.user' is a '@ManyToMany' and is directly annotated '@JoinColumn'
means Hibernate is confused because you're trying to use @JoinColumn
directly on a @ManyToMany
relationship—which isn't allowed. Let’s break it down and fix it.
๐ง Why This Happens
In JPA/Hibernate, a @ManyToMany
relationship must use a @JoinTable
, not a @JoinColumn
. That’s because many-to-many associations require an intermediate join table to map the relationship.
✅ How to Fix It
Let’s say your entity looks like this:
@ManyToMany
@JoinColumn(name = "user_id") // ❌ This is invalid
private List<User> users;
You need to replace it with:
@ManyToMany
@JoinTable(
name = "urlmapping_user", // name of the join table
joinColumns = @JoinColumn(name = "urlmapping_id"), // FK to current entity
inverseJoinColumns = @JoinColumn(name = "user_id") // FK to target entity
)
private List<User> users;
This tells Hibernate:
Use a join table named
urlmapping_user
Link
urlmapping_id
to your current entity (UrlMapping
)Link
user_id
to theUser
entity
๐ Bonus Tip
If you want to customize the join table name or column names, this is the place to do it. Otherwise, Hibernate will auto-generate names that might not match your DB schema.
If you share your full UrlMapping
entity, I can help you rewrite the annotations precisely. Want to do that?
This error is pointing to a problem in your JPA entity mapping—specifically with the clickEvents
association in your UrlMapping
entity. The key part is:
Association 'com.example.url_shortener.Entity.UrlMapping.clickEvents' targets the type 'com.example.url_shortener.Entity.ClickEvent' which is not an ...
That unfinished message usually ends with something like “...which is not an entity” or “...which is not annotated with @Entity.”
๐ง What This Means
Hibernate is trying to map a relationship to ClickEvent
, but it doesn’t recognize ClickEvent
as a valid JPA entity. This typically happens when:
The
ClickEvent
class is missing the@Entity
annotation.The class is not in a package that’s scanned by Spring Boot.
There’s a typo or mismatch in the class name or import.
✅ How to Fix It
Here’s what to check:
1. Annotate ClickEvent
Properly
Make sure your ClickEvent
class looks like this:
package com.example.url_shortener.Entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class ClickEvent {
@Id
private Long id;
// other fields and mappings
}
⚠️ If you're using Spring Boot 3.x+, make sure you're using
jakarta.persistence.*
imports, notjavax.persistence.*
.
2. Ensure It’s in a Scanned Package
Spring Boot automatically scans entities in the same package or sub-packages of your main application class. If ClickEvent
is outside that scope, either move it or explicitly scan it:
@SpringBootApplication
@EntityScan(basePackages = "com.example.url_shortener.Entity")
public class Application { ... }
3. Check Your Relationship Mapping
In UrlMapping
, make sure the relationship is valid:
@OneToMany(mappedBy = "urlMapping")
private List<ClickEvent> clickEvents;
And in ClickEvent
, you should have:
@ManyToOne
@JoinColumn(name = "url_mapping_id")
private UrlMapping urlMapping;
If you want to paste your entity classes here, I can help you fine-tune the annotations. Want to do that?
Thanks for sharing the start of your User
entity. Based on the context and the error you're facing with the UrlMapping.clickEvents
association, it looks like your project is encountering multiple JPA mapping issues. Let's tackle them one by one.
✅ Fixing the ClickEvent
Entity Issue
From the error:
Association 'com.example.url_shortener.Entity.UrlMapping.clickEvents' targets the type 'ClickEvent' which is not an ...
This typically means ClickEvent
is either:
Missing the
@Entity
annotationNot properly imported or scanned by Spring Boot
Make sure your ClickEvent
class looks like this:
package com.example.url_shortener.Entity;
import jakarta.persistence.*;
import lombok.Data;
@Entity
@Data
@Table(name = "click_events")
public class ClickEvent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Example fields
private String ipAddress;
private String userAgent;
@ManyToOne
@JoinColumn(name = "url_mapping_id")
private UrlMapping urlMapping;
}
๐ Mapping clickEvents
in UrlMapping
In your UrlMapping
entity, the relationship should be:
@OneToMany(mappedBy = "urlMapping", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ClickEvent> clickEvents;
This ensures Hibernate knows how to link the two entities.
๐ง Entity Scan Reminder
If your entities are in a package that’s not automatically scanned by Spring Boot, add this to your main class:
@SpringBootApplication
@EntityScan(basePackages = "com.example.url_shortener.Entity")
public class UrlShortenerApplication { ... }
Would you like help reviewing the full UrlMapping
entity or your pom.xml
to ensure everything’s wired correctly? I can walk through it with you.
Great question, Shivam! The SessionFactory is one of the most important components in Hibernate—it’s the heavyweight backbone of the framework’s database interaction layer.
๐️ What Is SessionFactory?
In Hibernate, the SessionFactory
is a thread-safe, immutable, and heavyweight object that acts as a factory for Session
instances. It’s designed to be created once per application (or per database) and reused throughout the lifecycle of the app.
๐ง Key Responsibilities
Creates
Session
objects: These are lightweight, short-lived objects used to interact with the database.Manages database connections: It handles connection pooling and transaction management.
Caches metadata: It stores mappings between Java classes and database tables, improving performance.
Supports second-level caching: This helps reduce database hits by caching entities across sessions.
๐งช Typical Usage
Configuration configuration = new Configuration().configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
Once you have a Session
, you can perform operations like:
session.save(entity);
session.get(Entity.class, id);
session.close();
๐ง Why It Matters
Efficiency: Creating a
SessionFactory
is expensive, so it should be done once and reused.Scalability: It’s designed to handle concurrent access in multi-threaded environments.
Centralized Configuration: It encapsulates all Hibernate settings and mappings.
If you're using Spring Boot with Hibernate, the SessionFactory
is usually managed automatically via Spring’s configuration. But understanding its role helps you appreciate how Hibernate works under the hood.
Want to see how to configure it manually or integrate it with Spring Boot? I can walk you through that too.
Absolutely, Shivam! Think of SessionFactory and Session in Hibernate like a car manufacturing plant and the cars it produces. Here's how they differ:
๐ญ SessionFactory — The Factory
Purpose: Creates
Session
objects.Lifecycle: Long-lived; typically one per application.
Thread Safety: ✅ Thread-safe and immutable.
Performance: Expensive to create, so it's initialized once at startup.
Responsibilities:
Manages connection pooling.
Handles second-level caching.
Provides access to metadata and services.
๐ง You build it once, and it serves as the backbone for all database interactions.
๐ Session — The Worker
Purpose: Represents a single unit of work with the database.
Lifecycle: Short-lived; created per transaction or request.
Thread Safety: ❌ Not thread-safe.
Responsibilities:
Performs CRUD operations (
save()
,get()
,delete()
, etc.).Manages first-level cache (session-level).
Handles transaction boundaries.
Wraps a JDBC connection internally.
๐ง It’s like a temporary workspace where you load, modify, and persist entities.
๐ Summary Table
Feature | SessionFactory | Session |
---|---|---|
Scope | Application-wide | Per transaction/request |
Thread-safe | Yes | No |
Lifecycle | Long-lived | Short-lived |
Purpose | Creates Sessions | Performs DB operations |
Caching | Second-level cache | First-level cache |
Cost to create | High | Low |
If you’re using Spring Boot, it often manages these under the hood via EntityManagerFactory
, but understanding this distinction helps when debugging or optimizing performance.
Want to dive into how transactions behave across sessions next?
Great question, Shivam! In Hibernate, caching is a powerful performance optimization technique, and it’s divided into first-level and second-level caches. Here's how they differ:
๐ง First-Level Cache (Session Cache)
Scope: Tied to a single Hibernate
Session
Enabled by default: Yes
Purpose: Prevents repeated database hits for the same entity within one session
Example: If you fetch a
User
entity twice in the same session, Hibernate will serve the second request from memory, not the database
Session session = sessionFactory.openSession();
User user1 = session.get(User.class, 1); // Hits DB
User user2 = session.get(User.class, 1); // Served from cache
๐ง Second-Level Cache (SessionFactory Cache)
Scope: Shared across multiple sessions
Enabled by default: No — you must configure it
Purpose: Stores entities across sessions to reduce DB load for frequently accessed data
Backed by: External cache providers like EHCache, Redis, or Infinispan
// Session 1
Session session1 = sessionFactory.openSession();
User user1 = session1.get(User.class, 1); // Hits DB and stores in 2nd-level cache
// Session 2
Session session2 = sessionFactory.openSession();
User user2 = session2.get(User.class, 1); // Served from 2nd-level cache
๐ Summary Table
Feature | First-Level Cache | Second-Level Cache |
---|---|---|
Scope | Per Session | Across Sessions |
Enabled by Default | ✅ Yes | ❌ No (must configure) |
Configuration Needed | No | Yes (choose provider + setup) |
Use Case | Avoid duplicate DB hits in same session | Improve performance across sessions |
Hibernate uses these caching layers to reduce database access, improve response time, and scale better under load. If you're building a high-performance app, configuring second-level caching can be a game-changer.
Want help setting up EHCache or Redis for second-level caching in your project?
Exactly, Shivam — you've got the essence right, and let me sharpen it a bit more for clarity.
๐ Session vs SessionFactory — Refined Understanding
SessionFactory is like a blueprint or factory that knows how to create sessions. It’s built once when your application starts and holds all the configuration, mappings, and connection settings. Think of it as the central hub that knows how to talk to the database.
Session is the actual conversation between your app and the database. Every time you want to do something — like save a user, fetch a URL mapping, or delete a click event — you open a new session. It’s lightweight, short-lived, and specific to that task or request.
๐ง Analogy Time
Imagine a call center:
SessionFactory is the entire call center setup — the infrastructure, the phone lines, the training manuals.
Session is each individual phone call — a temporary, focused interaction between a customer and an agent.
You don’t rebuild the call center for every call. You just pick up the phone (create a session) when needed.
๐ Lifecycle Summary
Component | Created When? | Lives How Long? | Used For? |
---|---|---|---|
SessionFactory | Once at app startup | Entire app lifecycle | Creating sessions, caching metadata |
Session | Per request/transaction | Short-lived (per task) | Performing DB operations |
If you're using Spring Boot, it wraps this logic inside EntityManagerFactory
and EntityManager
, but the core idea remains the same.
Want to see how this plays out in a real Hibernate config or Spring Boot setup? I can show you both.
Hibernate manages transactions using its own Transaction API, which wraps around JDBC or JTA (Java Transaction API) depending on your setup. The goal is to ensure that a group of database operations either fully succeed or fully fail—preserving data integrity through the ACID principles: Atomicity, Consistency, Isolation, and Durability.
๐ง Two Ways Hibernate Manages Transactions
1. Programmatic Transaction Management
You manually control the transaction lifecycle in your code:
Transaction transaction = null;
try (Session session = sessionFactory.openSession()) {
transaction = session.beginTransaction();
// Perform DB operations
session.save(entity);
transaction.commit(); // ✅ All good
} catch (Exception e) {
if (transaction != null) transaction.rollback(); // ❌ Rollback on error
e.printStackTrace();
}
You explicitly begin, commit, and rollback transactions.
Gives you fine-grained control, but can be verbose and error-prone.
2. Declarative Transaction Management (Spring + Hibernate)
You let Spring handle it using annotations:
@Transactional
public void saveUser(User user) {
userRepository.save(user);
}
Spring opens and commits the transaction automatically.
If an exception occurs, it rolls back the transaction.
Cleaner and easier to maintain.
๐ง Key Concepts
Concept | Description |
---|---|
Session | Represents a single unit of work and wraps a JDBC connection. |
Transaction | Represents a single transaction. You begin, commit, or rollback using this. |
@Transactional | Spring annotation to manage transactions declaratively. |
Rollback | Ensures no partial changes are saved if something fails. |
Hibernate integrates seamlessly with Spring Boot, so most modern apps use declarative management. But knowing both styles helps when debugging or optimizing performance.
Want to see how this works with nested transactions or exception handling? I can walk you through that next.
The Criteria API in Hibernate is a powerful, object-oriented way to build dynamic queries without writing raw SQL or HQL. Instead of writing query strings, you construct queries using Java objects and methods—making your code more type-safe, readable, and maintainable.
๐ง What It Does
Allows you to build queries programmatically using Java.
Supports dynamic filtering, sorting, pagination, and joins.
Helps avoid SQL injection by using parameterized queries.
Works well when query conditions are not known at compile time.
๐ ️ Example Using JPA Criteria API (Hibernate 5.2+)
Session session = sessionFactory.openSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
// Add conditions
query.select(root).where(cb.equal(root.get("firstName"), "Shivam"));
List<User> results = session.createQuery(query).getResultList();
This builds a query like:
SELECT * FROM users WHERE first_name = 'Shivam';
๐ Key Components
Component | Role |
---|---|
CriteriaBuilder | Entry point to build queries |
CriteriaQuery | Represents the query structure |
Root<T> | Defines the entity/table to query |
Predicate | Represents conditions (WHERE clauses) |
๐งจ Deprecated Hibernate Criteria API
Hibernate used to have its own Criteria
API (pre-5.2), like:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.eq("firstName", "Shivam"));
List<User> users = criteria.list();
But this is now deprecated, and the JPA Criteria API is the recommended approach.
If you're building complex queries with multiple filters, joins, or dynamic conditions, Criteria API is your best friend. Want help writing a Criteria query for your own entity? Just share the structure and I’ll whip one up.
The Criteria API in Hibernate is a game-changer when it comes to writing flexible, dynamic, and type-safe queries. Here's a breakdown of its benefits and where it's most useful:
๐ฏ Benefits of Using Criteria API
✅ Type-Safe Queries You build queries using Java objects and methods, which means compile-time checking and fewer runtime errors.
๐ก️ Protection Against SQL Injection Since it uses parameterized queries under the hood, it’s much safer than string-based query construction.
๐ Dynamic Query Building You can construct queries based on runtime conditions—perfect for search filters or user-driven queries.
๐งฉ Object-Oriented Syntax No need to write raw SQL or HQL. You use Java constructs like
CriteriaBuilder
,Predicate
, andRoot
.๐ฆ Integration with JPA Works seamlessly with JPA and Hibernate, especially in Spring Boot applications.
๐ Supports Complex Queries You can easily add joins, groupings, projections, and nested conditions.
๐ง Where Do We Use Criteria API?
Search Filters in Web Apps For example, filtering users by name, age, or location based on user input.
Admin Dashboards When building dynamic reports or analytics with multiple optional filters.
Multi-Field Search Forms Criteria API shines when you don’t know in advance which fields will be used for filtering.
Complex Business Logic Queries When queries involve multiple joins, conditions, or aggregations.
Reusable Query Components You can modularize query logic and reuse it across services or repositories.
๐ง Real-World Example
Imagine a job portal where users can filter jobs by location, salary, experience, and company. Instead of writing dozens of static queries, you use Criteria API to build one dynamic query that adapts to whatever filters the user selects.
If you want, I can show you how to write a Criteria query for one of your entities—just tell me which one and what filters you want to apply.
Awesome, Shivam! Let’s build a real-world example using the Criteria API based on your project context. Suppose you have a User
entity and you want to search users dynamically based on optional filters like:
First name
Email
Role
Here’s how you can do it using Hibernate’s JPA Criteria API:
๐ ️ Sample Criteria Query
public List<User> searchUsers(String firstName, String email, String role) {
Session session = sessionFactory.openSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (firstName != null && !firstName.isEmpty()) {
predicates.add(cb.equal(root.get("firstName"), firstName));
}
if (email != null && !email.isEmpty()) {
predicates.add(cb.equal(root.get("email"), email));
}
if (role != null && !role.isEmpty()) {
predicates.add(cb.equal(root.get("role"), role));
}
query.select(root).where(cb.and(predicates.toArray(new Predicate[0])));
return session.createQuery(query).getResultList();
}
๐ What’s Happening Here
We use
CriteriaBuilder
to start building the query.Root<User>
defines the entity/table we’re querying.We dynamically add
Predicate
s based on which filters are provided.The final query is executed with all active conditions.
๐ง Why This Is Useful
You don’t need to write multiple query methods for different combinations of filters.
It’s safe, clean, and easy to extend (e.g., add pagination, sorting, etc.).
Works beautifully in search forms, admin panels, and reporting dashboards.
Want to try building a Criteria query for your UrlMapping
or ClickEvent
entity next? Just tell me what filters or conditions you want to apply.
Hibernate defines three primary object states that represent the lifecycle of an entity in relation to the database and the Hibernate Session
. Understanding these states helps you manage persistence and optimize performance.
๐ 1. Transient State
Definition: The object is created using
new
, but it’s not associated with any HibernateSession
.Database Link: ❌ No representation in the database.
Persistence Context: Not tracked by Hibernate.
Example:
User user = new User(); // Transient
user.setName("Shivam");
Changes to this object won’t be saved unless it’s explicitly persisted.
๐ 2. Persistent State
Definition: The object is associated with an open Hibernate
Session
.Database Link: ✅ Represents a row in the database.
Persistence Context: Tracked by Hibernate. Any changes are automatically synchronized with the DB.
Example:
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user); // Now persistent
session.getTransaction().commit();
Hibernate automatically detects changes and updates the database when the transaction is committed.
๐ 3. Detached State
Definition: The object was once associated with a session, but the session is now closed.
Database Link: ✅ Still represents a row in the database.
Persistence Context: ❌ No longer tracked by Hibernate.
Example:
session.close(); // user becomes detached
user.setName("Updated Shivam"); // Change won't be tracked
You need to reattach it using
session.update()
orsession.merge()
to persist changes.
๐ง Bonus: Removed State (Optional)
Some sources also mention a removed state, where an entity is scheduled for deletion but not yet removed from the database until the transaction is committed.
๐ Summary Table
State | Associated with Session | Stored in DB | Tracked by Hibernate |
---|---|---|---|
Transient | ❌ No | ❌ No | ❌ No |
Persistent | ✅ Yes | ✅ Yes | ✅ Yes |
Detached | ❌ No (was Yes) | ✅ Yes | ❌ No |
Understanding these states helps you avoid common pitfalls like lazy loading exceptions, unintended updates, or memory leaks. Want to see how these states behave in a real Hibernate transaction flow? I can walk you through that next.
Understanding when and how to use transient, persistent, and detached states in Hibernate is key to writing efficient, predictable, and bug-free data access code. Let’s break it down with real-world use cases and decision points:
๐ฆ 1. Transient State
Use When: You’re creating a new object that hasn’t been saved to the database yet.
Typical Scenario:
You instantiate a new entity using
new
.You want to populate its fields before saving.
Example:
User user = new User(); // Transient
user.setName("Shivam");
Decision Point: Use transient when you're preparing data but haven't yet decided to persist it. Once ready, pass it to a session to make it persistent.
๐ 2. Persistent State
Use When: You want Hibernate to track changes and automatically sync them with the database.
Typical Scenario:
You save a new entity using
session.save()
orsession.persist()
.You load an entity using
session.get()
orsession.load()
.
Example:
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(user); // Now persistent
user.setEmail("shivam@example.com"); // Hibernate tracks this change
session.getTransaction().commit();
Decision Point: Use persistent state when you want automatic change tracking and database synchronization. This is ideal during active transactions.
๐ 3. Detached State
Use When: You need to work with an entity outside of a session (e.g., in a web layer or after session is closed).
Typical Scenario:
You fetch an entity, close the session, and pass it to another layer.
You later reattach it to a new session to update or delete.
Example:
session.close(); // user becomes detached
user.setName("Updated Shivam"); // Change not tracked
Session newSession = sessionFactory.openSession();
newSession.update(user); // Reattach and persist changes
Decision Point: Use detached state when you need to pass entities across layers (e.g., from DAO to service to controller) and reattach them later for updates.
๐ง Summary Table
State | When to Use | Hibernate Tracks Changes | Needs Session |
---|---|---|---|
Transient | Before saving to DB | ❌ No | ❌ No |
Persistent | During active DB operations | ✅ Yes | ✅ Yes |
Detached | After session is closed, for reuse | ❌ No (until reattached) | ❌ No |
๐ก How to Decide?
Ask yourself:
Do I need to save this object? → Start with transient.
Am I in a transaction and want Hibernate to track changes? → Make it persistent.
Do I need to pass this object outside the session and update later? → It will become detached.
Hibernate’s lifecycle is like managing relationships: transient is like dating, persistent is like marriage, and detached is like a long-distance relationship—you can reconnect, but you need effort ๐
Want to see how this plays out in a Spring Boot service layer or with REST APIs? I can show you that next.
๐ Hibernate Object States — Refined Summary
๐ก Transient
What it is: A newly created object using
new
, not yet associated with Hibernate.Use case: You’re preparing data but haven’t decided to save it yet.
DB impact: ❌ Not saved to the database.
๐ข Persistent
What it is: An object that’s attached to an active Hibernate
Session
.Use case: You want Hibernate to track changes and commit them automatically.
DB impact: ✅ Changes are synced with the database on
commit()
.Example:
๐ต Detached
What it is: An object that was persistent but the session is now closed.
Use case: You want to reuse or update the object later.
DB impact: ❌ Changes are not tracked unless reattached.
Example:
๐ง Decision Guide
Goal | Use This State |
---|---|
Prepare data but not save | Transient |
Save and auto-track changes | Persistent |
Reuse/update after session | Detached |
๐ง Purpose of the Configuration
Class
The Configuration
class is used to:
Load Hibernate settings from configuration files like
hibernate.cfg.xml
orhibernate.properties
.Register entity classes and mapping files (either annotated classes or XML-based
.hbm.xml
files).Set up database connection properties such as URL, username, password, dialect, driver class, etc.
Build the
SessionFactory
, which is the core object used to create sessions and perform database operations.
๐ฆ What It Bootstraps
Component | Role |
---|---|
hibernate.cfg.xml | Contains DB and Hibernate settings |
Entity mappings | Defines how Java classes map to DB tables |
SessionFactory | Created using Configuration to manage sessions |
software development, bootstrapping (or "bootstrap") refers to the process of starting up a system or application from scratch—often with minimal initial resources. It’s like pulling yourself up by your own bootstraps: using a small, simple foundation to build something much larger and more complex.
๐ง In Contexts Like Hibernate or Spring
When we say Hibernate “bootstraps” itself using the Configuration
class, we mean:
It loads configuration files (like
hibernate.cfg.xml
)It registers entity mappings
It initializes the SessionFactory, which is the core engine for database operations
๐ Key Differences Between get()
and load()
in Hibernate
Feature | get() Method | load() Method |
---|---|---|
Loading Strategy | Eager loading — hits the DB immediately | Lazy loading — returns a proxy object |
Return Type | Actual entity object | Proxy object (placeholder) |
If Entity Not Found | Returns null | Throws ObjectNotFoundException |
Performance | Slightly slower (loads full object) | Faster (defers loading until needed) |
Use Case | Use when you're unsure if the entity exists | Use when you're sure the entity exists |
Hibernate wraps all database operations in transactions using its Transaction
API or Spring’s @Transactional
annotation.
If any operation fails, Hibernate rolls back the entire transaction.
This prevents partial updates and ensures atomicity—either all changes succeed, or none do.
Example: If you're saving a
User
and their associatedClickEvent
, and theClickEvent
fails, theUser
won't be saved either.
๐ง 2. Dirty Checking
Hibernate automatically tracks changes to persistent entities during a session.
It compares the current state of an object with its original state (snapshot).
Only modified fields are updated in the database.
This avoids accidental overwrites and ensures accurate updates.
๐งฑ 3. Constraint Enforcement
Hibernate respects and enforces database-level constraints like:
Primary keys
Foreign keys
If you try to violate these (e.g., insert a null into a non-null column), Hibernate throws a DataIntegrityViolationException
.
๐ 4. Optimistic & Pessimistic Locking
To handle concurrent access:
Optimistic locking uses a
@Version
field to detect conflicting updates.Pessimistic locking locks rows during a transaction to prevent other sessions from modifying them.
This ensures consistency when multiple users access the same data.
๐งช 5. Validation & Cascading
Hibernate integrates with Bean Validation (JSR 380) to validate entity fields before persisting.
Cascade operations ensure related entities are updated or deleted together, maintaining relational integrity.
๐ง Real-World Scenario
Imagine you're updating a user's profile and their associated settings. Hibernate:
Starts a transaction
Tracks changes via dirty checking
Validates fields
Updates both entities atomically
Rolls back if anything fails
Result: Your database stays clean, consistent, and trustworthy.
๐ 1. Optimistic Locking — Preventing Conflicting Updates
Hibernate uses a special field annotated with @Version
to track changes and prevent concurrent modification issues.
✅ How to Use It
๐ง What Happens
When you load a
User
, Hibernate stores its version.When you update it, Hibernate checks if the version in DB matches.
If someone else updated it in the meantime, the versions mismatch → Hibernate throws
OptimisticLockException
.
This ensures no two sessions overwrite each other’s changes silently.
Now imagine this flow:
You load a user with
version = 3
.You update the name:
user.setName("Shivam Updated")
.You commit the transaction.
Hibernate will:
Check that the current version in the DB is still
3
.If it matches, it updates the record and sets
version = 4
.
✅ This ensures that no other transaction has modified the entity in the meantime.
๐ง Why This Matters
It prevents lost updates in concurrent environments.
It allows Hibernate to detect stale data and throw an exception if needed.
It’s automatic — you don’t need to manually increment the version.
๐ 2. Dirty Checking — Tracking Changes Automatically
Hibernate keeps a snapshot of each persistent entity when it’s loaded. If you modify a field, Hibernate detects the change and updates the DB automatically on commit()
.
✅ Example
๐ 1. Optimistic Locking (Default in Hibernate)
How it works: Each entity has a
@Version
field. When an update happens, Hibernate checks if the version in memory matches the version in the database.If mismatch: Hibernate throws an
OptimisticLockException
to prevent overwriting newer data.Best for: Low-conflict environments (e.g., read-heavy apps).
@Version private int version;
This allows multiple users to read the same data,
but only one can successfully update it if no one else has changed it in the meantime.
.
๐ 2. Pessimistic Locking
How it works: Hibernate locks the database row during a transaction using SQL
SELECT FOR UPDATE
.Prevents: Other transactions from reading or writing the locked row.
Best for: High-conflict environments (e.g., financial apps, inventory systems).
๐ 3. Transaction Isolation Levels
Hibernate respects the isolation level defined by your database (e.g.,
READ_COMMITTED
,REPEATABLE_READ
,SERIALIZABLE
).These control how visible changes are across concurrent transactions.
For example,
REPEATABLE_READ
ensures that if you read a row twice in the same transaction,
it won’t change—even if another transaction modifies it in between.
๐ Optimistic Locking — Trust First, Verify Later
No actual DB row lock during read or write.
Hibernate relies on a
@Version
field to detect if someone else modified the data.If the version matches → ✅ update proceeds.
If the version changed → ❌ throws
OptimisticLockException
.
๐ Pessimistic Locking — Lock First, Then Work
Hibernate issues a SQL
SELECT FOR UPDATE
when reading the row.This locks the row in the database.
Other transactions must wait until your transaction completes.
Ensures no one else can read or write to that row during your operation.
❓ Q: Your Hibernate app is slow when fetching data with many relationships. What strategy would you use?
Answer: I’d use lazy loading to avoid fetching unnecessary related entities upfront.
To fix N+1 query issues, I’d apply join fetching, batch fetching, or subselect fetching.
Additionally, I’d use DTO projections for read-only views and enable second-level caching for frequently accessed data.
1. Use Lazy Loading Wisely
Set relationships to
FetchType.LAZY
instead ofEAGER
.This ensures related entities are loaded only when accessed, not upfront.
2. Avoid the N+1 SELECT Problem
Happens when Hibernate fetches one parent entity and then makes N additional queries for each child.
Fix it using:
JOIN FETCH
in JPQL@EntityGraph
or@NamedEntityGraph
Batch fetching (
hibernate.default_batch_fetch_size
).
3. Enable Batch Fetching
Configure in
application.properties
:
4 Use DTO Projections for Read-Only Views
Instead of loading full entities, fetch only required fields into DTOs.
❓ Q: How do you manage Hibernate sessions in a web app to prevent memory leaks?
Answer:
I use the Session-per-request strategy, where a Hibernate session is opened at the start of each HTTP request and closed at the end. This ensures that sessions are short-lived and properly cleaned up. In a Spring-based application, this is typically handled automatically using @Transactional
, which binds the session to the current thread and closes it after the transaction completes. For manual setups, I ensure sessions are closed in a finally
block or use try-with-resources
to guarantee cleanup.
❓ Q: What happens if an error occurs during a Hibernate transaction after some operations succeed?
Answer:
Hibernate wraps all operations in a transactional boundary. If an error occurs mid-transaction, Hibernate automatically rolls back the entire transaction, undoing all changes made so far. This ensures that the database remains in a consistent state and prevents partial updates. Whether you're using Hibernate's native Transaction
API or Spring's @Transactional
, the rollback mechanism guarantees atomicity—either all operations succeed, or none are committed.
Q: How do you track changes in entity data using Hibernate?
Answer:
I use Hibernate Envers, a built-in module that automatically tracks changes to entity data. By annotating entities with @Audited
, Envers creates audit tables and stores historical versions of the data, including inserts, updates, and deletes. This allows me to retrieve previous states of an entity and maintain a complete change history for compliance, debugging, or analytics.
@Table
and @Column
annotations to explicitly map entity classes and fields to the legacy database's table and column names. This allows me to work with non-standard naming conventions without altering the schema.@Cacheable
. This stores the result of frequently accessed queries in a cache, reducing repeated database hits. I also configure a cache provider like EhCache or Redis in the application. For example, I enable caching with @EnableCaching
and use @Cacheable("users")
on methods that fetch user data.๐ ️ Quick Setup Steps
Enable caching in your Spring Boot app:
@SpringBootApplication @EnableCaching public class MyApp { }
2. Annotate methods to cache results:
@Cacheable("users") public User getUserById(Long id) { return userRepository.findById(id).orElse(null); }3.Configure cache provider inapplication.properties
:spring.cache.type=redis spring.redis.host=localhost spring.redis.port=6379 spring.cache.redis.time-to-live=600000 # Optional: TTL in milliseconds4. Evict or Update Cache When NeededKeeps your cache in sync with DB updates.
@CacheEvict(value = "users", key = "#user.id") public void updateUser(User user) { userRepository.save(user); }
๐ง Why It Helps
Reduces database load
Speeds up response time for repeated queries
Improves scalability for read-heavy applications.
๐งฑ What Is a Database Schema?
A schema is the structure of your database — it defines:
Tables and their relationships
Columns, data types, constraints (e.g. primary keys, foreign keys)
Indexes, views, stored procedures, etc.
Think of it as the blueprint of your database.
When business requirements evolve (e.g. adding a new feature),
your schema might need to change — like adding a new column, renaming a table, or modifying constraints.
@OneToMany
, @ManyToOne
, or @ManyToMany
annotations with mappedBy
. @JsonManagedReference
and @JsonBackReference
, or I expose DTOs instead of entities. This ensures clean JSON output and avoids stack overflow errors.๐ What Jackson Does
Jackson is a library that converts Java objects to JSON and vice versa. When you have bidirectional relationships (like User → Order → User
), Jackson tries to serialize everything — including nested objects — recursively.
๐ How Infinite Recursion Happens
Imagine this setup:
@Entity
public class User {
@OneToMany(mappedBy = "user")
private List<Order> orders;
}
@Entity
public class Order {
@ManyToOne
private User user;
}
User
object to JSON:Jackson will:
Serialize
User
See
orders
→ serialize eachOrder
Each
Order
has auser
→ serializeUser
againThat
User
hasorders
→ and so on...
๐ฅ Result: Infinite loop → StackOverflowError
๐ ️ Easy Fix with Jackson Annotations
Use @JsonManagedReference
and @JsonBackReference
:
public class User {
@JsonManagedReference
private List<Order> orders;
}
public class Order {
@JsonBackReference
private User user;
}
✅ This tells Jackson:
Serialize
orders
insideUser
But don’t serialize
user
insideOrder
again
spring.jpa.properties.hibernate.jdbc.batch_size
in application.properties
. spring.jpa.properties.hibernate.order_inserts=true
and hibernate.order_updates=true
to ensure efficient batching.๐ง What Is Flushing in Hibernate?
Flushing means:
Hibernate pushes changes from memory (Java objects) to the database — but doesn’t commit them yet.
๐งพ Real-World Analogy
Imagine you're writing a document in MS Word:
You type changes — they’re in memory.
You hit Save — that’s like flush().
You hit Submit or Send — that’s like commit().
So:
flush() = sync changes to disk/database
commit() = finalize and make changes permanent
When Hibernate calls flush()
, it sends the SQL statements (like INSERT
, UPDATE
, DELETE
) to the database engine, but the changes are stored in the transaction buffer — not yet permanently saved.
⚙️ What Is Automatic Flushing?
Automatic flushing happens when Hibernate decides to flush changes without you explicitly calling flush()
. This usually occurs:
Before a query is executed (to ensure query results reflect the latest changes)
Before committing a transaction
Hibernate uses FlushMode.AUTO by default, which means:
“Flush when needed — like before running a query or committing.”
๐ง What Does “Store in Memory” vs “Store in DB” Mean?
๐️ Store in Memory
Means the data is temporarily held in RAM — fast, but not permanent. Hibernate does this inside the Session or cache before committing to the database.
Think of it like writing on a whiteboard — quick access, but if the power goes out, it’s gone.
๐️ Store in Database
Means the data is persisted — saved permanently in your database (like MySQL, PostgreSQL, etc.).
Like saving a file to your hard drive — it stays even after shutdown.
๐ง Why Hibernate Uses Memory First
It lets you batch changes before hitting the DB
Improves performance by reducing DB round-trips
Supports rollback if something fails before commit
๐ What Is the Transaction Buffer?
It’s a temporary area inside the database where changes are held during an open transaction.
These changes are not visible to other transactions.
If you call
commit()
, the buffer is flushed into the actual database tables.If you call
rollback()
, the buffer is discarded — no changes are saved.
๐ง Summary
Hibernate Action | What Happens in DB |
---|---|
save() | Stored in Hibernate memory (not DB) |
flush() | SQL sent to DB, stored in transaction buffer |
commit() | Buffer is finalized → data saved permanently |
rollback() | Buffer discarded → no changes saved |
๐ What Is Pagination?
Pagination is the technique of breaking large datasets into smaller chunks (pages), so you don’t fetch everything at once. It improves performance, reduces memory usage, and gives better control over data display — especially in APIs and UI tables.
๐ ️ How to Implement Pagination in Spring Data JPA
Spring Data makes pagination super easy using the Pageable
interface and Page<T>
result type.
๐ง Bonus Tip
For REST APIs, Spring Boot supports automatic pagination via @RequestParam
:
@GetMapping("/users")
public Page<User> getUsers(@RequestParam int page, @RequestParam int size) {
return userRepository.findAll(PageRequest.of(page, size));
}
Comments
Post a Comment