Hibernate and JPA Interview Preparation
A. Hibernate & JPA Basics ORM (Object Relational Mapping) is a technique that maps Java objects to database tables. Instead of writing SQL queries manually, ORM frameworks convertβ¦
A. Hibernate & JPA Basics
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
ORM (Object Relational Mapping) is a technique that maps Java objects to database tables.
Instead of writing SQL queries manually, ORM frameworks convert Java objects into database records and vice versa.
For example,
Instead of writing:
INSERT INTO employee(id,name) VALUES(1,'John');
We simply write:
Employee emp = new Employee;
emp.setName("John");
entityManager.persist(emp);
Hibernate automatically generates the SQL.
Advantages
- Less SQL code
- Better maintainability
- Database independent
- Automatic object mapping
Popular ORM Frameworks
- Hibernate
- EclipseLink
- OpenJPA
Interview Follow-up
- Is ORM faster than JDBC?
- No.
- JDBC is generally faster because SQL is written manually.
- ORM provides higher productivity and easier maintenance, which is why most enterprise applications use it.
JPA (Java Persistence API) is a Java specification for managing relational data.
It defines:
- Annotations
- Interfaces
- Rules for ORM
JPA itself is not an implementation.
It only defines how persistence should work.
Popular implementations are:
- Hibernate
- EclipseLink
- OpenJPA
Example:
@Entity
public class Employee {
}
The @Entity annotation comes from JPA.
Interview Follow-up
- Can JPA work without Hibernate?
- Yes.
- JPA can work with any JPA implementation such as Hibernate, EclipseLink, or OpenJPA.
| JPA | Hibernate |
|---|---|
| Specification | ORM Framework |
| Defines interfaces | Implements JPA |
| Standard API | Concrete implementation |
| Vendor independent | Hibernate specific |
| Cannot work alone | Can work without JPA |
Simple Interview Statement
> JPA tells us what to do, Hibernate tells us how to do it.
Interview Follow-up
- Which one do you use in Spring Boot?
- We usually use Spring Data JPA, whose default implementation is Hibernate.
Hibernate simplifies database interaction.
Instead of writing repetitive SQL, developers work with Java objects.
Reasons
- Reduces boilerplate code
- Automatic SQL generation
- Database independence
- Caching support
- Lazy loading
- Transaction management
- Relationship mapping
- Pagination support
Real-world Example
Without Hibernate:
- Write SQL
- Execute PreparedStatement
- Read ResultSet
- Convert to Object
With Hibernate:
Employee emp = repository.findById(1).get;
Everything is handled automatically.
Advantages
- Less JDBC code
- Faster development
- Automatic mapping
- Supports caching
- Lazy loading
- HQL and Criteria API
- Database portability
Disadvantages
- Learning curve
- Slight performance overhead
- Hidden SQL generation
- Complex debugging
- N+1 query issues if not configured properly
Interview Question
When should we avoid Hibernate?
For:
- High-performance batch processing
- Very complex SQL
- Stored procedure-heavy applications
JDBC or MyBatis may be a better choice.
Main Components
Application
|
SessionFactory
|
Session
|
Transaction
|
Database
Components
Configuration
Reads hibernate.cfg.xml or Spring configuration.
SessionFactory
- Heavyweight object
- Created once
- Thread-safe
- Creates Sessions
Session
- Represents one database conversation
- Used for CRUD operations
- Not thread-safe
Transaction
Ensures ACID properties.
Database
Stores the actual data.
Interview Tip: One application should have only one SessionFactory, but many Sessions.
Steps
Load Configuration
β
Create SessionFactory
β
Open Session
β
Begin Transaction
β
Perform CRUD
β
Commit/Rollback
β
Close Session
β
Close SessionFactory
Example:
Session session = sessionFactory.openSession;
Transaction tx = session.beginTransaction;
session.persist(employee);
tx.commit;
session.close;
An Entity is a lightweight Java class that represents a database table.
Each object represents one row.
Example
@Entity
@Table(name="employee")
public class Employee {
@Id
private Long id;
private String name;
}
Here:
Employee Table
| id | name |
|---|---|
| 1 | John |
becomes
Employee emp
Entity Rules
- Must have @Entity
- Must have Primary Key
- No-arg constructor
- Should be Serializable (recommended)
- Should not be final
Hibernate entities have 4 lifecycle states.
1. Transient
Object created using new.
Not associated with Hibernate.
Employee emp = new Employee;
No database row exists.
2. Persistent
Managed by Hibernate.
session.persist(emp);
Changes are automatically tracked and synchronized with the database.
3. Detached
Session is closed.
session.close;
Object still exists but Hibernate no longer tracks changes.
4. Removed
Marked for deletion.
session.remove(emp);
Deleted from the database after commit.
Easy Diagram
new
β
Transient
β
persist
β
Persistent
β
close
β
Detached
β
remove
β
Removed
Persistence Context is a first-level cache where Hibernate keeps all managed entities.
Whenever an entity is loaded, Hibernate stores it in the persistence context.
If the same entity is requested again, Hibernate returns it from memory instead of querying the database.
Example
Employee e1 = session.find(Employee.class,1);
Employee e2 = session.find(Employee.class,1);
Only one SQL query executes.
e1 == e2 is true because both references point to the same managed entity.
Benefits
- Reduces database calls
- Automatic dirty checking
- Identity guarantee
- Improves performance
Interview Follow-up
- Is Persistence Context the same as First-Level Cache?
- Yes.
- The first-level cache is implemented by the Persistence Context.
Different databases use different SQL syntax.
Hibernate Dialect tells Hibernate which SQL syntax to generate.
Examples:
- MySQL Dialect
- PostgreSQL Dialect
- Oracle Dialect
- SQL Server Dialect
Example configuration:
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
or
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
Hibernate then generates database-specific SQL.
Interview Follow-up
- What happens if the wrong dialect is configured?
- Hibernate may generate unsupported SQL, causing runtime errors or incorrect behavior.
| Method | JPA/Hibernate | Returns | Use Case |
|---|---|---|---|
| persist | JPA | void | Insert a new entity |
| merge | JPA | Managed entity | Insert or update detached entity |
| save | Hibernate | Generated ID | Save a new entity (Hibernate-specific) |
| update | Hibernate | void | Reattach a detached entity |
persist
entityManager.persist(emp);
- JPA standard
- Makes entity persistent
- Does not return ID
merge
Employee managed = entityManager.merge(emp);
- Used for detached entities
- Returns a managed instance
- Copies state into the persistence context
save
session.save(emp);
- Hibernate-specific
- Returns generated identifier
- Typically used with Hibernate Session API
update
session.update(emp);
- Reattaches a detached entity
- Throws an exception if another instance with the same identifier is already managed
Interview Tip: In modern Spring Boot + Spring Data JPA applications, you’ll mostly use persist (internally via save on repositories for new entities) and merge (for detached updates). Direct use of Hibernate’s save and update is less common unless working with the Hibernate Session API.
B. Entity Mapping
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
| @Entity | @Table |
|---|---|
| Marks a class as a JPA entity | Maps the entity to a specific table |
| Mandatory | Optional |
| Represents a database table | Customizes table details |
| Used by JPA | Defines table name, schema, unique constraints, indexes |
Example:
@Entity
@Table(name = "employees")
public class Employee {
}
If @Table is omitted, Hibernate uses the class name (or naming strategy) as the table name.
Interview Follow-up
- Can an entity exist without @Table?
- Yes. @Entity is sufficient. @Table is only needed when you want to customize the table mapping.
Example:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
AUTO
- Default strategy
- Hibernate chooses the most appropriate strategy based on the database
IDENTITY
- Uses the database’s auto-increment feature
- Common with MySQL
- ID is generated during insert
SEQUENCE
- Uses a database sequence
- Common with Oracle and PostgreSQL
- Better performance for bulk inserts due to pre-allocation
TABLE
- Stores the next ID in a separate table
- Works with all databases
- Slowest and rarely used today
Interview Tip: – MySQL β IDENTITY
- Oracle/PostgreSQL β SEQUENCE
- TABLE is generally avoided in modern applications.
@Column customizes how a field maps to a database column.
Example:
@Column(name = "emp_name", nullable = false, length = 100, unique = true)
private String name;
Common attributes:
- name
- nullable
- length
- unique
- insertable
- updatable
- precision
- scale
If omitted, Hibernate uses default mapping conventions.
@Transient marks a field that should not be persisted to the database.
Example:
@Transient
private int age;
Here, age exists only in the Java object and no column is created for it.
Use cases:
- Calculated values
- Temporary fields
- UI-only data
Interview Follow-up
- Difference between transient keyword and @Transient?
- transient (Java keyword) excludes a field from Java serialization.
- @Transient (JPA annotation) excludes a field from database persistence.
@Embeddable
Marks a reusable value object.
@Embeddable
public class Address {
private String city;
private String state;
}
@Embedded
Embeds that value object into an entity.
@Entity
public class Employee {
@Embedded
private Address address;
}
The Address fields become columns in the Employee table.
Interview Tip: – @Embeddable β defines the reusable object.
- @Embedded β uses the reusable object inside an entity.
A superclass annotated with @MappedSuperclass provides common fields to child entities but does not have its own table.
Example:
@MappedSuperclass
public class BaseEntity {
@Id
private Long id;
private LocalDateTime createdAt;
}
@Entity
public class Employee extends BaseEntity {
}
The Employee table contains id and createdAt columns.
Interview Follow-up
- Difference between @MappedSuperclass and inheritance mapping?
@MappedSuperclass shares fields only and is not an entity itself, whereas inheritance mapping treats the parent as an entity and supports polymorphic queries.
Hibernate supports three strategies.
1. SINGLE_TABLE
- One table for the entire hierarchy
- Uses a discriminator column
Pros: Fast queries, no joins
Cons: Many nullable columns
2. JOINED
- Separate table for each class
- Joined using primary keys
Pros: Normalized schema
Cons: Requires joins, slower reads
3. TABLE_PER_CLASS
- Separate table for every concrete class
Pros: Independent tables
Cons: Expensive polymorphic queries, duplicated columns
Interview Tip: – SINGLE_TABLE β Best performance
- JOINED β Best normalization
- TABLE_PER_CLASS β Least commonly used
Composite keys are used when more than one column forms the primary key.
@EmbeddedId
- Encapsulates all key fields in one embeddable class
- Cleaner and easier to maintain
@IdClass
- Key fields remain in the entity
- Requires a separate key class with matching fields
Interview Tip: For new applications, @EmbeddedId is generally preferred because it provides better encapsulation and cleaner code.
Maps a Java enum to a database column.
Example:
public enum Status {
ACTIVE,
INACTIVE
}
@Enumerated(EnumType.STRING)
private Status status;
Types
- EnumType.STRING β Stores “ACTIVE” (recommended)
- EnumType.ORDINAL β Stores 0, 1 (not recommended because changing enum order can corrupt existing data)
Interview Tip: Always prefer EnumType.STRING in production for readability and safer schema evolution.
@Lob maps large objects such as long text or binary data.
Examples:
@Lob
private String description;
or
@Lob
private byte[] image;
Typical use cases:
- Documents
- Images
- PDFs
- Long text (CLOB)
- Binary files (BLOB)
Unique Constraint
Ensures duplicate values are not allowed.
Example:
@Table(
uniqueConstraints = {
@UniqueConstraint(columnNames = "email")
}
)
Trying to insert the same email twice results in a database constraint violation.
Index
Improves query performance.
Example:
@Table(
indexes = {
@Index(name = "idx_email", columnList = "email")
}
)
Indexes speed up reads but slightly slow inserts, updates, and deletes because the index must also be maintained.
Interview Tip: – Use Unique Constraints to enforce data integrity.
- Use Indexes to improve search performance on frequently queried columns.
C. Entity Relationships
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
A One-to-One relationship means one record in Table A is associated with exactly one record in Table B.
Real-world Example
- Person β Passport
- Employee β Locker
- User β UserProfile
Example:
@Entity
public class User {
@Id
private Long id;
@OneToOne
@JoinColumn(name = "profile_id")
private UserProfile profile;
}
@Entity
public class UserProfile {
@Id
private Long id;
}
Generated tables:
User
-----
id
profile_id
UserProfile
-----------
id
Interview Tip: Use One-to-One only when the relationship is truly one-to-one. Otherwise, prefer One-to-Many or Many-to-One.
Uni-directional
Only one entity knows about the other.
User -----> Profile
@OneToOne
private Profile profile;
Profile has no reference to User.
Advantages
- Simple
- Less memory usage
- Easy to maintain
Bi-directional
Both entities know about each other.
User <-----> Profile
@Entity
public class User {
@OneToOne(mappedBy = "user")
private Profile profile;
}
@Entity
public class Profile {
@OneToOne
@JoinColumn(name="user_id")
private User user;
}
Interview Tip: – Use Uni-directional unless navigation is needed from both sides.
- Every bidirectional relationship should have one owning side.
mappedBy tells Hibernate that this side is not responsible for managing the relationship.
It points to the field that owns the relationship.
Example:
@Entity
public class Department {
@OneToMany(mappedBy = "department")
private List<Employee> employees;
}
@Entity
public class Employee {
@ManyToOne
@JoinColumn(name="department_id")
private Department department;
}
Here,
Employee
|
department_id
Department
(mappedBy="department")
Employee owns the relationship because it contains the foreign key.
Interview Tip: mappedBy is used only on the inverse (non-owning) side.
One Department β Many Employees
Department
1
β
Many Employees
@Entity
public class Department {
@OneToMany(mappedBy="department")
private List<Employee> employees;
}
@Entity
public class Employee {
@ManyToOne
@JoinColumn(name="department_id")
private Department department;
}
Database:
Employee
----------
id
name
department_id
Why is the foreign key stored in Employee?
Because many employees belong to one department.
Interview Tip: Always place the foreign key on the “many” side.
Owning Side
- Contains the foreign key.
- Controls database updates.
- Uses @JoinColumn.
Example:
@ManyToOne
@JoinColumn(name="department_id")
private Department department;
Inverse Side
- Uses mappedBy.
- Does not update the relationship in the database.
Example:
@OneToMany(mappedBy="department")
private List<Employee> employees;
Interview Tip: Only the owning side updates the foreign key.
Changing only the inverse side does not update the database.
Most business relationships are naturally many-to-one.
Examples:
- Many Employees β One Department
- Many Orders β One Customer
- Many Books β One Author
Benefits:
- Simpler mapping
- Better performance
- Easier queries
- Fewer joins
- No unnecessary collections
Interview Question
Why shouldn’t we always use OneToMany?
Because collections:
- consume more memory
- may trigger lazy loading
- can cause N+1 problems
- are harder to maintain
β Prefer LAZY loading for collections.
β Use Set when duplicates are not allowed.
β Keep relationships as simple as possible.
β Avoid unnecessary bidirectional mappings.
β Update both sides of a bidirectional relationship in your code.
Example:
employee.setDepartment(department);
department.getEmployees.add(employee);
Many records in one table relate to many records in another table.
Example:
Students β Courses
Student
M
β
Join Table
β
M
Course
Example:
@ManyToMany
@JoinTable(
name="student_course",
joinColumns=@JoinColumn(name="student_id"),
inverseJoinColumns=@JoinColumn(name="course_id")
)
private List<Course> courses;
Generated table:
student_course
student_id
course_id
Hibernate automatically creates a join table.
Example:
Student
Course
β
student_course
student_id
course_id
No extra entity is required.
Interview Tip: A join table contains only foreign keys unless you explicitly map it as an entity.
Although easy to define, direct Many-to-Many relationships are rarely used in enterprise applications.
Problems:
- Difficult to extend
- Hard to audit
- Poor flexibility
- Complex updates
- Limited support for additional business fields
Example:
Student β Course
Later you need:
- Enrollment Date
- Grade
- Status
- Fee Paid
A simple join table is no longer sufficient.
Interview Tip: Most real-world applications avoid direct @ManyToMany.
Instead of:
Student
β
Course
Use:
Student
β
Enrollment
β
Course
Example:
@Entity
public class Enrollment {
@ManyToOne
private Student student;
@ManyToOne
private Course course;
private LocalDate enrolledDate;
private String grade;
}
Advantages:
- Extra fields supported
- Easier auditing
- Better scalability
- More flexible business logic
Interview Tip: This is the recommended production approach.
| @JoinColumn | mappedBy |
|---|---|
| Defines the foreign key column | Refers to the owning field |
| Used on owning side | Used on inverse side |
| Updates the relationship | Does not update the relationship |
| Creates/manages FK | References existing mapping |
Easy Rule
Foreign Key β @JoinColumn
Opposite Side β mappedBy
| Join Column | Join Table |
|---|---|
| Foreign key stored in one table | Separate table stores relationships |
| Used for One-to-One, One-to-Many, Many-to-One | Commonly used for Many-to-Many |
| Better performance | Requires an extra join |
| Simpler schema | More flexible |
Interview Tip: – @JoinColumn is preferred when a direct foreign key is sufficient.
- @JoinTable is mainly used for Many-to-Many or when an intermediate table is required.
Problem
Department
β
Employees
β
Department
β
Employees...
When returning entities from a REST API, JSON serialization can keep traversing both sides of the relationship indefinitely, leading to infinite recursion or a StackOverflowError.
Solutions
1. @JsonManagedReference + @JsonBackReference
@JsonManagedReference
private List<Employee> employees;
@JsonBackReference
private Department department;
2. @JsonIgnore
Ignore one side of the relationship during serialization.
3. DTOs (Recommended)
Map entities to DTOs before returning API responses.
Interview Tip: In production, DTOs are preferred over exposing JPA entities directly because they avoid recursion, prevent lazy-loading issues, and give better control over the API response.
D. Fetching Strategies
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Eager Loading
Loads related entities immediately.
@OneToOne(fetch = FetchType.EAGER)
Example:
Load Employee
β
Automatically load Department
Advantages
- Related data is immediately available.
- No LazyInitializationException while the persistence context is active.
Disadvantages
- Extra SQL queries
- More memory usage
- Can reduce performance if related data isn’t needed
Lazy Loading
Loads related entities only when accessed.
@OneToMany(fetch = FetchType.LAZY)
Example:
employee.getDepartment;
The SQL executes only when getDepartment is first called.
Advantages
- Better performance
- Lower memory usage
- Loads only required data
Disadvantages
- Can throw LazyInitializationException
- May cause N+1 query problems if used carelessly
Interview Tip: Use LAZY by default, then fetch only what is required.
| Relationship | Default Fetch Type |
|---|---|
| @OneToOne | EAGER |
| @ManyToOne | EAGER |
| @OneToMany | LAZY |
| @ManyToMany | LAZY |
Interview Tip: Many projects explicitly change @ManyToOne and @OneToOne to LAZY for better performance.
This exception occurs when a lazy-loaded entity is accessed after the Hibernate Session (Persistence Context) has been closed.
Example:
Employee employee = repository.findById(1L).get;
session.close;
employee.getDepartment.getName;
Result:
LazyInitializationException
Why does it happen?
The proxy needs an active session to fetch the data.
Once the session is closed, Hibernate cannot load it.
Recommended Solutions
β Fetch required associations inside the transaction using Fetch Join.
β Use EntityGraph.
β Map entities to DTOs within the transaction.
β Initialize required data before closing the session.
Avoid
β Changing everything to FetchType.EAGER.
β Depending on Open Session In View (OSIV) as the primary solution.
Interview Tip: The best solution is to fetch only the required data within the service layer while the transaction is active.
The N+1 problem occurs when Hibernate executes:
- 1 query to fetch parent entities
- N additional queries to fetch each child entity
Example:
1 query β Departments
10 queries β Employees
Total = 11 queries
This significantly impacts performance.
Common Cause
Iterating over lazy associations without optimizing the fetch strategy.
Interview Tip: The N+1 problem is one of the most common Hibernate performance issues discussed in interviews.
A Fetch Join retrieves parent and related entities in a single SQL query.
Example:
@Query("""
SELECT d
FROM Department d
JOIN FETCH d.employees
""")
List<Department> findAllDepartments;
Generated SQL joins both tables and avoids extra queries.
Advantages
- Eliminates N+1 queries
- Better performance
- Reduces database round trips
Interview Tip: JOIN FETCH is one of the most commonly used optimizations in Spring Data JPA.
@EntityGraph specifies which associations should be fetched eagerly for a particular query, without changing the entity mapping.
Example:
@EntityGraph(attributePaths = "employees")
List<Department> findAll;
Advantages:
- Cleaner than custom fetch joins for simple cases
- Query-specific fetching
- Prevents unnecessary eager loading globally
Interview Tip: Use EntityGraph when different use cases require different fetch plans.
Batch fetching reduces the number of SQL queries by loading multiple lazy associations together.
Example:
Instead of:
10 separate queries
Hibernate can execute:
2 batch queries
using a configured batch size.
Example:
@BatchSize(size = 20)
private List<Employee> employees;
Advantages
- Fewer SQL queries
- Better performance
- Reduces the impact of the N+1 problem
OSIV keeps the Hibernate Session open from the start of the web request until the response is rendered.
Flow:
Request
β
Open Session
β
Controller
β
View/JSON Serialization
β
Close Session
This allows lazy-loaded associations to be initialized even after the service method completes.
Advantages
- Prevents LazyInitializationException
- Simple for small applications
Disadvantages
- Keeps database resources open longer
- Can execute unexpected SQL during view rendering or JSON serialization
- Makes performance issues harder to detect
Interview Tip: In production Spring Boot applications, many teams disable OSIV and explicitly fetch the required data in the service layer using Fetch Join, EntityGraph, or DTO projections. This provides better performance, predictable queries, and cleaner architecture.
E. Cascade & Orphan Removal
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Cascade allows an operation performed on a parent entity to be automatically propagated to its child entities.
Without Cascade:
You have to save/update/delete both parent and child manually.
With Cascade:
Hibernate automatically performs the same operation on the child entities.
Example:
@OneToMany(
mappedBy = "department",
cascade = CascadeType.PERSIST
)
private List<Employee> employees;
Department department = new Department;
department.getEmployees.add(new Employee);
entityManager.persist(department);
Both Department and Employee are saved.
Real-world Example
Department
β
Employees
Saving the department automatically saves all employees.
Interview Tip: Cascade only propagates operations. It does not create relationships automaticallyβyou still need to set both sides of the relationship.
JPA provides the following cascade types.
| Cascade Type | Purpose |
|---|---|
| PERSIST | Save child automatically |
| MERGE | Update detached child |
| REMOVE | Delete child |
| REFRESH | Reload child from database |
| DETACH | Detach child from Persistence Context |
| ALL | Applies all cascade operations |
1. CascadeType.PERSIST
cascade = CascadeType.PERSIST
Saving parent automatically saves child.
2. CascadeType.MERGE
cascade = CascadeType.MERGE
Updating parent updates child.
3. CascadeType.REMOVE
cascade = CascadeType.REMOVE
Deleting parent deletes child.
4. CascadeType.REFRESH
Reloads both parent and child from the database.
Useful when another process has modified the data.
5. CascadeType.DETACH
Detaches parent and child from the Persistence Context.
Hibernate stops tracking changes.
6. CascadeType.ALL
cascade = CascadeType.ALL
Equivalent to:
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REMOVE,
CascadeType.REFRESH,
CascadeType.DETACH
}
Interview Tip: Use only the cascade types you actually need instead of blindly using CascadeType.ALL.
orphanRemoval = true automatically deletes a child entity when it is removed from the parent’s collection.
Example:
@OneToMany(
mappedBy = "department",
orphanRemoval = true
)
private List<Employee> employees;
department.getEmployees.remove(employee);
After committing the transaction, Hibernate executes:
DELETE FROM employee WHERE id = ?;
Real-world Example
Order
β
OrderItems
Removing an OrderItem from the order should delete it from the database.
Interview Tip: Use orphanRemoval when the child cannot exist independently of the parent.
| Cascade REMOVE | orphanRemoval |
|---|---|
| Deletes children when parent is deleted | Deletes child when removed from the parent’s collection |
| Triggered by deleting the parent | Triggered by removing the relationship |
| Parent deletion required | Parent can still exist |
Example
Cascade REMOVE
entityManager.remove(department);
Result:
Department deleted
β
Employees deleted
orphanRemoval
department.getEmployees.remove(emp);
Result:
Department remains
β
Employee deleted
Interview Question
Can both be used together?
Yes.
This is common for parent-child relationships like:
- Order β OrderItems
- Invoice β InvoiceLines
- Blog β Comments
Avoid CascadeType.ALL when child entities are shared by multiple parents or have an independent lifecycle.
Examples:
β User β Role
Deleting a user should not delete shared roles.
β Student β Course
Deleting one student should not delete the course.
Good Use Cases
β Order β OrderItems
β Blog β Comments
β Invoice β InvoiceItems
Interview Tip: Never use CascadeType.REMOVE or CascadeType.ALL on @ManyToMany relationships, because the associated entities are usually shared.
F. Hibernate Session & Persistence Context
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
| Session | EntityManager |
|---|---|
| Hibernate API | JPA API |
| Hibernate-specific | Standard API |
| More Hibernate features | Vendor-independent |
| Implements ORM operations | Defines persistence operations |
Example:
Using Hibernate:
Session session = sessionFactory.openSession;
Using JPA:
EntityManager em = entityManagerFactory.createEntityManager;
Interview Tip: In Spring Boot, you typically use EntityManager (or Spring Data JPA repositories). Internally, Hibernate provides the implementation.
| SessionFactory | EntityManagerFactory |
|---|---|
| Hibernate-specific | JPA standard |
| Creates Session objects | Creates EntityManager objects |
| Heavyweight | Heavyweight |
| Thread-safe | Thread-safe |
| Usually one per application | Usually one per persistence unit |
Lifecycle
Application
β
SessionFactory
β
Session
or
Application
β
EntityManagerFactory
β
EntityManager
Interview Tip: Create one SessionFactory/EntityManagerFactory and reuse it throughout the application.
Every Hibernate Session (or EntityManager) has its own First-Level Cache, also called the Persistence Context.
Example:
Employee e1 = em.find(Employee.class, 1L);
Employee e2 = em.find(Employee.class, 1L);
Only one SQL query is executed.
Database
β
Persistence Context
β
e1
β
e2 (same object)
e1 == e2 is true.
Advantages
- Reduces database calls
- Improves performance
- Ensures entity identity within a session
- Supports dirty checking
Interview Tip: The first-level cache is enabled by default and cannot be disabled.
Dirty Checking is Hibernate’s mechanism for automatically detecting changes to managed entities.
Example:
Employee emp = em.find(Employee.class, 1L);
emp.setName("John Smith");
No explicit update is required.
On transaction commit, Hibernate generates:
UPDATE employee
SET name = 'John Smith'
WHERE id = 1;
How It Works
Load Entity
β
Persistence Context
β
Modify Object
β
Hibernate detects change
β
UPDATE SQL generated
Advantages
- No manual update calls
- Cleaner code
- Automatic synchronization
Interview Tip: Dirty Checking works only for managed (persistent) entities.
| Flush | Commit |
|---|---|
| Synchronizes changes to the database | Permanently commits the transaction |
| Transaction may still roll back | Ends the transaction |
| Can occur multiple times | Happens once per transaction |
Example
em.persist(emp);
em.flush;
SQL is executed.
But:
transaction.rollback;
The data is not committed.
Easy Rule
Flush β Send SQL
Commit β Make changes permanent
Interview Tip: Commit always performs a flush first (unless there are no pending changes).
Hibernate controls when automatic flushing occurs.
AUTO (Default)
Flushes automatically:
- Before transaction commit
- Before executing queries that may be affected by pending changes
COMMIT
Flushes only during transaction commit.
Better performance when intermediate queries don’t need the latest in-memory changes.
MANUAL (Hibernate-specific)
Flush occurs only when:
session.flush;
is explicitly called.
Commonly used in batch processing.
Interview Tip: AUTO is the default and the most commonly used mode.
detach removes a single entity from the Persistence Context.
Example:
Employee emp = em.find(Employee.class, 1L);
em.detach(emp);
emp.setName("ABC");
No update is generated because Hibernate is no longer tracking the entity.
Interview Tip: A detached entity can later be reattached using merge.
clear removes all managed entities from the Persistence Context.
Example:
em.clear;
Before
Persistence Context
Employee
Department
Order
β
clear
β
Persistence Context Empty
Use Cases
- Batch processing
- Memory optimization
- Preventing stale managed entities
Interview Tip: Unlike detach, which affects one entity, clear affects every managed entity.
refresh reloads an entity from the database and discards any uncommitted changes.
Example:
Employee emp = em.find(Employee.class, 1L);
emp.setName("Temporary");
em.refresh(emp);
After refresh, the entity again contains the latest values from the database.
Use Cases
- Another transaction updated the data
- Discard local modifications
- Reload the latest database state
Interview Tip: refresh overwrites unsaved in-memory changes.
evict is the Hibernate equivalent of JPA’s detach.
Example:
session.evict(employee);
After eviction:
- The entity becomes detached.
- Hibernate stops tracking changes.
- Dirty Checking no longer works for that entity.
detach vs evict
| detach | evict |
|---|---|
| JPA API (EntityManager) | Hibernate API (Session) |
| Removes one managed entity | Removes one managed entity |
| Standard API | Hibernate-specific |
Interview Tip: – Use detach when working with JPA (EntityManager).
- Use evict when working directly with the Hibernate Session API.
G. Hibernate Caching
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
The First-Level Cache is the cache associated with a Session (Hibernate) or EntityManager (JPA).
It is enabled by default and stores all managed entities within the current Persistence Context.
Example:
Employee e1 = entityManager.find(Employee.class, 1L);
Employee e2 = entityManager.find(Employee.class, 1L);
Only one SQL query is executed.
Database
β
Session Cache
β
Employee(id=1)
β
Second find returns cached object
Features
- Enabled by default
- Session scoped
- Cannot be disabled
- Improves performance by avoiding duplicate database queries
Interview Tip: The Persistence Context itself is the First-Level Cache.
The Second-Level Cache is shared across multiple Sessions.
Unlike the First-Level Cache, it survives beyond a single Session.
Application
β
Second-Level Cache
β β
Session1 Session2
If Session1 loads an entity, Session2 can retrieve it from the cache instead of querying the database.
Example
Employee e1 = session1.find(Employee.class, 1L);
SQL executes.
Later:
Employee e2 = session2.find(Employee.class, 1L);
Data is returned from the Second-Level Cache.
Features
- Shared across sessions
- Optional
- Must be configured
- Improves performance for frequently read data
Interview Tip: Not every entity should be cached. Cache only data that is read frequently and changes infrequently.
The Query Cache stores the results of queries, not the entities themselves.
Example:
SELECT e
FROM Employee e
WHERE department = 'IT'
Instead of executing the query repeatedly, Hibernate can return the cached result.
Difference
| Second-Level Cache | Query Cache |
|---|---|
| Caches entities | Caches query results |
| Entity-based | Query-based |
| Independent of query | Depends on query |
Interview Tip: Query Cache usually works together with the Second-Level Cache for maximum benefit.
Cache Eviction removes stale data from the cache.
Example:
sessionFactory.getCache
.evict(Employee.class);
Hibernate removes cached Employee entities.
Types
- Evict one entity
- Evict one cache region
- Evict all cache entries
Why is it needed?
Suppose:
Database
Employee Name = John
Cache also stores:
John
Another application updates the database:
John β David
The cache still contains “John”.
Eviction removes stale data so fresh data is loaded.
Interview Tip: Caching improves performance only if cache consistency is properly maintained.
Hibernate provides strategies to maintain consistency between cache and database.
| Strategy | Use Case |
|---|---|
| READ_ONLY | Immutable data |
| READ_WRITE | Frequently updated data |
| NONSTRICT_READ_WRITE | Occasional updates |
| TRANSACTIONAL | JTA/distributed transactions |
READ_ONLY
Best for:
- Countries
- States
- Languages
Fastest strategy.
READ_WRITE
Supports updates safely.
Uses locking internally.
Suitable for most business data.
NONSTRICT_READ_WRITE
Allows a small chance of stale data.
Higher performance than READ_WRITE.
TRANSACTIONAL
Used in distributed transaction environments.
Rare in typical Spring Boot applications.
Interview Tip: Most Spring Boot applications use either:
- READ_ONLY
- READ_WRITE
Hibernate supports several cache providers.
Ehcache
Most popular.
Easy integration.
Good for standalone applications.
Redis
Distributed cache.
Shared across multiple application instances.
Common in Microservices.
Hazelcast
Distributed in-memory data grid.
Supports clustering.
Comparison
| Provider | Best For |
|---|---|
| Ehcache | Single application |
| Redis | Microservices |
| Hazelcast | Clustered applications |
Interview Tip: Redis is not a Hibernate cache provider by default, but it can be integrated as the Second-Level Cache using appropriate libraries.
Caching should be avoided for data that changes frequently.
Examples:
β Stock prices
β Live trading data
β Real-time sensor data
β Frequently changing inventory
Good Candidates
β Countries
β States
β Product Categories
β Currency Codes
β Configuration Data
Interview Question
Should every entity be cached?
No.
Caching consumes memory and requires synchronization.
Only cache data that is:
- Frequently read
- Rarely updated
- Shared across multiple requests
Interview Tip: Caching is a performance optimization, not a replacement for database design.
H. JPQL, HQL & Native SQL
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
| JPQL | HQL | Native SQL |
|---|---|---|
| JPA standard | Hibernate-specific | Database SQL |
| Works with entities | Works with entities | Works with tables |
| Database independent | Mostly database independent | Database specific |
| Portable | Less portable | Not portable |
JPQL Example
SELECT e
FROM Employee e
WHERE e.salary > :salary
Uses entity names and field names.
HQL Example
FROM Employee
WHERE department='IT'
Very similar to JPQL but includes Hibernate-specific features.
Native SQL
SELECT *
FROM employee
WHERE salary > 50000;
Uses actual database tables and columns.
Interview Tip: – JPQL β Preferred in Spring Data JPA.
- Native SQL β Use for complex database-specific queries.
- HQL β Mostly encountered when using Hibernate APIs directly.
Named Queries are predefined queries declared once and reused multiple times.
Example:
@NamedQuery(
name = "Employee.findAll",
query = "SELECT e FROM Employee e"
)
Usage:
entityManager
.createNamedQuery("Employee.findAll", Employee.class)
.getResultList;
Advantages
- Reusable
- Centralized
- Validated at startup
- Cleaner code
Interview Tip: Named Queries are compiled and validated when the application starts.
DTO Projection retrieves only the required fields instead of the entire entity.
Example DTO:
public class EmployeeDTO {
private String name;
private Double salary;
}
Query:
SELECT new com.demo.EmployeeDTO(e.name, e.salary)
FROM Employee e
Advantages
- Better performance
- Less memory usage
- Smaller API responses
- Avoids LazyInitializationException
Interview Tip: DTO Projection is preferred for REST APIs over returning entities directly.
Constructor Expressions create DTO objects directly from a JPQL query.
Example:
SELECT new com.demo.EmployeeDTO(
e.id,
e.name
)
FROM Employee e
Hibernate directly creates EmployeeDTO objects.
Requirements
- Matching constructor
- Correct package name
- Constructor must be public
Interview Tip: Constructor Expressions are one of the cleanest ways to fetch partial data.
Named Parameters
SELECT e
FROM Employee e
WHERE e.name = :name
query.setParameter("name", "John");
Positional Parameters
SELECT e
FROM Employee e
WHERE e.name = ?1
query.setParameter(1, "John");
Comparison
| Named | Positional |
|---|---|
| More readable | Less readable |
| Easy to maintain | Error-prone |
| Preferred | Rarely used today |
Interview Tip: Always prefer Named Parameters.
Pagination limits the number of records returned by a query.
Example:
query.setFirstResult(0);
query.setMaxResults(10);
Or in Spring Data JPA:
Pageable pageable = PageRequest.of(0, 10);
repository.findAll(pageable);
Advantages
- Faster queries
- Lower memory usage
- Better user experience
Interview Tip: Pagination is essential for large datasets and REST APIs.
Sorting arranges query results in ascending or descending order.
Example:
SELECT e
FROM Employee e
ORDER BY e.salary DESC
Spring Data JPA:
Sort.by("salary").descending
Interview Tip: Sorting can be combined with Pagination using Pageable.
JPQL supports aggregate functions similar to SQL.
Examples:
SELECT COUNT(e)
FROM Employee e
SELECT MAX(e.salary)
FROM Employee e
Other functions:
- COUNT
- SUM
- AVG
- MIN
- MAX
Interview Tip: Aggregate functions usually return scalar values rather than entities.
JPQL supports subqueries in WHERE and HAVING clauses.
Example:
SELECT e
FROM Employee e
WHERE e.salary >
(
SELECT AVG(emp.salary)
FROM Employee emp
)
Returns employees earning more than the average salary.
Interview Tip: Subqueries are supported, but complex joins are often more efficient depending on the database and use case.
Bulk queries update or delete multiple records with a single SQL statement.
Bulk Update:
UPDATE Employee e
SET e.salary = e.salary * 1.10
WHERE e.department = 'IT'
Bulk Delete:
DELETE
FROM Employee e
WHERE e.status = 'INACTIVE'
In Spring Data JPA:
@Modifying
@Query("""
UPDATE Employee e
SET e.salary = :salary
WHERE e.id = :id
""")
Important Behavior
Bulk update/delete queries:
- Execute directly in the database.
- Bypass the Persistence Context.
- Do not trigger Dirty Checking.
- Managed entities may become stale.
Interview Tip: After executing a bulk update or delete, consider clearing the Persistence Context (for example, entityManager.clear or using clearAutomatically = true with @Modifying) so subsequent reads don’t use stale managed entities.
I. Spring Data JPA
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
| CrudRepository | JpaRepository |
|---|---|
| Basic CRUD operations | Extends CrudRepository with JPA-specific features |
| Save, Find, Delete | CRUD + Pagination + Sorting + Batch operations |
| Minimal functionality | Most commonly used |
| Suitable for simple applications | Preferred in enterprise applications |
Example:
public interface EmployeeRepository
extends JpaRepository<Employee, Long> {
}
Interview Tip: In Spring Boot, JpaRepository is the preferred choice because it provides all CRUD functionality along with pagination, sorting, flushing, and batch operations.
PagingAndSortingRepository extends CrudRepository and adds support for:
- Pagination
- Sorting
Example:
Page<Employee> findAll(Pageable pageable);
Sorting:
Sort.by("salary").descending;
Repository Hierarchy
Repository
β
CrudRepository
β
PagingAndSortingRepository
β
JpaRepository
Interview Tip: Since JpaRepository already extends PagingAndSortingRepository, you usually don’t need to use it directly.
Spring Data JPA can automatically generate SQL queries from method names.
Example:
List<Employee> findByDepartment(String department);
Generates:
SELECT *
FROM employee
WHERE department = ?
More examples:
findByName
findBySalaryGreaterThan
findByDepartmentAndStatus
findByNameContaining
findByAgeBetween
findTop5ByOrderBySalaryDesc
Advantages
- No SQL required
- Cleaner code
- Faster development
Interview Tip: For simple queries, always prefer Derived Query Methods over custom JPQL.
@Query is used when Derived Query Methods become too complex.
Example:
@Query("""
SELECT e
FROM Employee e
WHERE e.salary > :salary
""")
List<Employee> findHighSalaryEmployees(double salary);
Advantages
- Supports JPQL
- Supports joins
- Supports subqueries
- Better readability for complex queries
Interview Tip: Use @Query for complex business queries that cannot be expressed cleanly with method names.
Sometimes database-specific SQL is required.
Example:
@Query(
value = """
SELECT *
FROM employee
WHERE salary > :salary
""",
nativeQuery = true
)
List<Employee> findEmployees(double salary);
When to Use
- Complex SQL
- Database-specific functions
- Stored procedures
- Performance tuning
Interview Tip: Use Native SQL only when JPQL cannot meet the requirement, as it reduces portability.
@Modifying is required for UPDATE and DELETE queries.
Example:
@Modifying
@Query("""
UPDATE Employee e
SET e.salary = :salary
WHERE e.id = :id
""")
int updateSalary(Long id, double salary);
Why is it Required?
By default, Spring Data assumes queries are SELECT.
@Modifying tells Spring that the query changes data.
Interview Tip: @Modifying should usually be used together with @Transactional.
Specifications API allows building dynamic queries at runtime.
Example:
Specification<Employee> spec =
(root, query, cb) ->
cb.equal(root.get("department"), "IT");
Advantages
- Dynamic filtering
- Easy to combine conditions
- Cleaner than long query methods
Real-world Example
Search Employees by:
- Name (optional)
- Department (optional)
- Salary (optional)
Specifications let you build the query dynamically based on the provided filters.
Interview Tip: Specifications are widely used in enterprise applications for search screens.
QBE searches using an example object.
Example:
Employee employee = new Employee;
employee.setDepartment("IT");
Example<Employee> example =
Example.of(employee);
repository.findAll(example);
Advantages
- Very simple
- No query writing
- Good for basic search forms
Limitations
- Doesn’t support joins
- Limited support for complex conditions
Interview Tip: QBE is useful for simple filters, while Specifications are better for advanced searches.
Instead of fetching the complete entity, Spring returns only required fields.
Example:
public interface EmployeeView {
String getName;
Double getSalary;
}
Repository:
List<EmployeeView> findByDepartment(String department);
Advantages
- Faster queries
- Less memory usage
- Smaller API responses
Interview Tip: Interface projections are simpler than DTO projections for read-only use cases.
DTO Projection maps query results directly into a DTO class.
Example:
public class EmployeeDTO {
public EmployeeDTO(
String name,
Double salary
) {
}
}
Query:
@Query("""
SELECT new com.demo.EmployeeDTO(
e.name,
e.salary)
FROM Employee e
""")
Advantages
- Better performance
- Avoids exposing entities
- Prevents LazyInitializationException
Interview Tip: For REST APIs, DTO Projection is generally preferred over returning JPA entities directly.
EntityGraph allows fetching related entities eagerly for a specific query.
Example:
@EntityGraph(attributePaths = "employees")
List<Department> findAll;
Instead of multiple SQL queries, Hibernate fetches the required associations efficiently.
Advantages
- Prevents N+1 problem
- Query-specific fetching
- Cleaner than custom fetch joins for simple cases
Interview Tip: Use EntityGraph when different use cases require different fetch plans.
When Spring Data JPA features are insufficient, create a custom repository.
Example:
public interface EmployeeRepositoryCustom {
List<Employee> searchEmployees;
}
Implementation:
public class EmployeeRepositoryImpl
implements EmployeeRepositoryCustom {
}
Repository:
public interface EmployeeRepository extends
JpaRepository<Employee, Long>,
EmployeeRepositoryCustom {
}
Use Cases
- Complex Criteria API queries
- Multiple joins
- Dynamic SQL
- Database-specific logic
Interview Tip: Use custom repositories only when Derived Queries, @Query, Specifications, or QBE cannot solve the requirement.
J. Locking & Transactions
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Optimistic Locking prevents multiple users from overwriting each other’s changes without locking the database row.
It uses a version column.
Example:
@Entity
public class Employee {
@Id
private Long id;
@Version
private Integer version;
}
Database:
id | name | version
-------------------
1 | John | 3
If two users load version 3:
- User A updates successfully β version becomes 4.
- User B tries to update using version 3 β update fails.
Advantages
- No database locking
- High concurrency
- Better performance
Interview Tip: Optimistic Locking is ideal when conflicts are rare.
Pessimistic Locking locks the database row while a transaction is running.
Other transactions must wait until the lock is released.
Example:
@Lock(LockModeType.PESSIMISTIC_WRITE)
Employee findById(Long id);
Advantages
- Prevents concurrent updates
- Guarantees data consistency
Disadvantages
- Reduced concurrency
- Can lead to deadlocks
- Slower under heavy load
Interview Tip: Use Pessimistic Locking only when concurrent updates are frequent and data consistency is critical.
Common JPA lock modes:
| Lock Mode | Purpose |
|---|---|
| NONE | No lock |
| OPTIMISTIC | Version check before commit |
| OPTIMISTIC_FORCE_INCREMENT | Version check + increments version |
| PESSIMISTIC_READ | Read lock |
| PESSIMISTIC_WRITE | Write lock |
| PESSIMISTIC_FORCE_INCREMENT | Write lock + version increment |
Interview Tip: Most enterprise applications primarily use:
- OPTIMISTIC
- PESSIMISTIC_WRITE
The Lost Update Problem occurs when two users update the same record simultaneously, causing one update to overwrite the other.
Example:
Initial Salary = 50,000
β
User A reads 50,000
User B reads 50,000
β
User A updates β 55,000
β
User B updates β 60,000
Final salary becomes 60,000, and User A’s update is lost.
Solution
- Optimistic Locking
- Pessimistic Locking
Interview Tip: Optimistic Locking is the most common solution in Spring Boot applications.
When two users update the same entity simultaneously using @Version, Hibernate throws an OptimisticLockException for the second update.
Example:
Version = 2
β
User A updates
β
Version = 3
β
User B tries Version = 2
β
OptimisticLockException
How to Handle
- Catch the exception
- Reload the latest entity
- Ask the user to retry or merge changes
Interview Tip: This exception indicates a concurrent modification, not a database failure.
@Transactional manages transactions automatically in Spring.
Example:
@Transactional
public void transferMoney {
withdraw;
deposit;
}
If any operation fails:
Rollback
Otherwise:
Commit
Benefits
- Automatic transaction management
- Automatic rollback on runtime exceptions
- Cleaner code
Interview Tip: Apply @Transactional at the service layer, not the controller layer.
Propagation defines how a method behaves when called within another transaction.
Common propagation types:
| Propagation | Behavior |
|---|---|
| REQUIRED | Joins existing transaction or creates a new one (Default) |
| REQUIRES_NEW | Suspends current transaction and starts a new one |
| SUPPORTS | Joins if one exists; otherwise executes without a transaction |
| MANDATORY | Requires an existing transaction; otherwise throws an exception |
| NEVER | Executes without a transaction; throws an exception if one exists |
Interview Tip: REQUIRED is the default and most commonly used propagation.
Isolation controls how concurrent transactions interact.
| Isolation Level | Prevents |
|---|---|
| READ_UNCOMMITTED | None |
| READ_COMMITTED | Dirty Reads |
| REPEATABLE_READ | Dirty Reads + Non-repeatable Reads |
| SERIALIZABLE | Dirty Reads + Non-repeatable Reads + Phantom Reads |
Interview Tip: For most Spring Boot applications:
- READ_COMMITTED is the most commonly used isolation level (and the default for many databases such as PostgreSQL and Oracle).
- REPEATABLE_READ is the default in MySQL InnoDB.
Always be aware that the effective default depends on the underlying database.
A read-only transaction is used for operations that only fetch data.
Example:
@Transactional(readOnly = true)
public List<Employee> getEmployees {
return repository.findAll;
}
Advantages
- Avoids unnecessary dirty checking in many JPA providers
- Can improve performance
- Clearly expresses intent
- Helps optimize read-only workloads
Interview Tip: Use readOnly = true for service methods that only perform read operations. Avoid making entity modifications inside a read-only transaction, as the behavior is provider-specific and changes may not be flushed as expected.
K. Performance Optimization
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Hibernate performance can be improved by reducing unnecessary database operations and efficiently fetching data.
Common Techniques
- Use LAZY loading whenever possible.
- Fetch only required columns using DTO Projection.
- Use Pagination for large datasets.
- Avoid the N+1 Query Problem.
- Use Fetch Join or EntityGraph.
- Enable JDBC Batch Processing.
- Cache frequently accessed data.
- Add proper Database Indexes.
- Avoid unnecessary entity relationships.
- Monitor SQL queries and Hibernate statistics.
Interview Tip: Most Hibernate performance problems are caused by too many SQL queries, not by Hibernate itself.
Instead of executing one SQL statement per record, Hibernate groups multiple statements into batches.
Without batching:
1000 Inserts
β
1000 SQL Queries
With batching:
1000 Inserts
β
20 Batches
β
50 Records per Batch
Configuration:
spring.jpa.properties.hibernate.jdbc.batch_size=50
Advantages
- Fewer database round trips
- Faster inserts and updates
- Better throughput
Interview Tip: Batch processing is especially useful for bulk imports and scheduled jobs.
JDBC Batch Processing allows multiple SQL statements to be sent to the database in a single network call.
Example:
for(Employee employee : employees){
entityManager.persist(employee);
if(count % 50 == 0){
entityManager.flush;
entityManager.clear;
}
}
Why flush and clear?
- flush executes pending SQL.
- clear frees memory by detaching managed entities.
Interview Tip: Without clear, the Persistence Context continues growing and may lead to high memory usage or OutOfMemoryError.
Fetch Size specifies how many rows the JDBC driver retrieves from the database in one round trip.
Example:
hibernate.jdbc.fetch_size=100
Instead of:
1 Row
β
1 Database Call
Hibernate fetches:
100 Rows
β
1 Database Call
Benefits
- Fewer network calls
- Better performance for large result sets
- Lower latency
Interview Tip: Fetch Size is most useful when processing large datasets sequentially.
Instead of loading the complete entity, retrieve only the required fields.
Example:
SELECT new com.demo.EmployeeDTO(
e.name,
e.salary
)
FROM Employee e
Instead of loading:
Employee
β
20 Columns
Only:
Name
Salary
are fetched.
Advantages
- Less memory usage
- Faster queries
- Smaller API responses
- Avoids LazyInitializationException
Interview Tip: DTO Projection is one of the best optimizations for read-only APIs.
Many performance issues occur because Hibernate executes unnecessary SQL.
Common causes:
- N+1 Query Problem
- EAGER loading
- Fetching unused entities
- Calling findById repeatedly inside loops
- Returning entities instead of DTOs
Best Practices
β Use JOIN FETCH
β Use EntityGraph
β Use DTO Projection
β Enable SQL logging during development
β Use proper indexes
Interview Tip: Always check generated SQL before assuming Hibernate is slow.
findAll loads every record into memory.
Example:
repository.findAll;
If the table contains:
10 Million Rows
Hibernate attempts to load all of them into memory, causing:
- High memory usage
- Slow response time
- Possible OutOfMemoryError
Better Approach
Use:
Page<Employee>
or
Slice<Employee>
or
Stream<Employee>
Interview Tip: Never expose findAll directly in production APIs for large tables.
| Feature | Page | Slice | Stream |
|---|---|---|---|
| Total Count | β | β | β |
| Has Next Page | β | β | β |
| Memory Efficient | Moderate | Better | Best |
| Large Data Processing | Good | Good | Excellent |
Page
Page<Employee>
Provides:
- Data
- Total Pages
- Total Records
- Current Page
Slice
Slice<Employee>
Returns:
- Current data
- Whether another page exists
No expensive COUNT(*) query.
Stream
Stream<Employee>
Processes records one by one without loading everything into memory.
Best for:
- Batch jobs
- File exports
- Large reports
Interview Tip: – Page β User-facing pagination
- Slice β Infinite scrolling or “Load More”
- Stream β Large-scale processing
SQL logging helps developers understand the exact SQL generated by Hibernate.
Configuration:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Example Output:
select *
from employee
where id=1;
Benefits
- Debug queries
- Detect N+1 problems
- Verify indexes are being used
- Optimize SQL
Interview Tip: Enable SQL logging in development, but avoid verbose SQL logging in production due to performance and security considerations.
Hibernate provides built-in statistics for performance analysis.
Enable:
spring.jpa.properties.hibernate.generate_statistics=true
Statistics include:
- Query execution count
- Cache hits
- Cache misses
- Entity load count
- Collection fetch count
- Transaction count
Benefits
- Detect slow queries
- Analyze cache efficiency
- Identify performance bottlenecks
Interview Tip: Hibernate Statistics are excellent for troubleshooting, but they are typically disabled in production because of the additional overhead.
Follow these best practices for production applications:
β Prefer LAZY loading.
β Avoid N+1 queries using Fetch Join or EntityGraph.
β Use DTO Projections for read-only APIs.
β Enable JDBC batching for bulk operations.
β Add proper database indexes.
β Use Pagination instead of findAll.
β Cache frequently read, rarely changed data.
β Avoid unnecessary bidirectional relationships.
β Monitor generated SQL.
β Profile and measure before optimizing.
Interview Tip: Performance tuning is about identifying the actual bottleneck. Use logs, execution plans, and metrics before making optimizations.
L. Auditing & Soft Delete
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Spring Data JPA Auditing automatically tracks entity creation and modification details.
Typical fields:
- Created Date
- Modified Date
- Created By
- Modified By
Enable auditing:
@EnableJpaAuditing
Advantages
- No manual timestamp management
- Consistent audit information
- Cleaner code
Interview Tip: Auditing is commonly used in enterprise applications for compliance and tracking.
Automatically stores when an entity was created.
Example:
@CreatedDate
private LocalDateTime createdDate;
On insert:
createdDate
β
2026-07-27 10:30
Interview Tip: The value is set only once during entity creation.
Automatically updates whenever the entity is modified.
Example:
@LastModifiedDate
private LocalDateTime modifiedDate;
Whenever an update occurs:
modifiedDate
β
Updated Automatically
Interview Tip: Unlike @CreatedDate, this field changes after every successful update.
Stores the user who created the entity.
Example:
@CreatedBy
private String createdBy;
Possible value:
admin@company.com
Requirement
Implement an AuditorAware bean so Spring knows the current user.
Interview Tip: In Spring Security applications, AuditorAware usually retrieves the username from the SecurityContext.
Stores the user who last modified the entity.
Example:
@LastModifiedBy
private String modifiedBy;
Automatically updated during modifications.
Interview Tip: Requires AuditorAware, just like @CreatedBy.
@EntityListeners registers lifecycle listeners for an entity.
Example:
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Employee {
}
The listener automatically populates auditing fields.
Other Lifecycle Events
- @PrePersist
- @PostPersist
- @PreUpdate
- @PostUpdate
- @PreRemove
- @PostRemove
- @PostLoad
Interview Tip: Entity listeners are useful for auditing and other cross-cutting concerns without cluttering business logic.
Soft Delete marks a record as deleted instead of physically removing it.
Example Entity:
private boolean deleted = false;
Instead of:
DELETE FROM employee
WHERE id = 1;
Hibernate executes:
UPDATE employee
SET deleted = true
WHERE id = 1;
Using Hibernate:
@SQLDelete(sql =
"UPDATE employee SET deleted = true WHERE id=?")
@Where(clause = "deleted = false")
Advantages
- Data recovery
- Audit history
- Regulatory compliance
- Prevents accidental data loss
Interview Tip: Soft Delete is commonly used for business data that may need to be restored or audited later.
Hibernate Envers automatically maintains an audit history of entity changes.
Enable auditing:
@Audited
@Entity
public class Employee {
}
Whenever an entity changes:
Employee
β
Employee_AUD
Envers stores:
- Old values
- New values
- Revision number
- Timestamp
- User (when integrated with auditing)
Advantages
- Complete change history
- Easy rollback/reference
- Minimal development effort
Interview Tip: Use Spring Data Auditing when you only need to know who changed data and when. Use Hibernate Envers when you need a complete history of what changed, including previous versions of the entity.
M. Best Practices & Design
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
JPA Entities are designed for database persistence, not for API responses.
Returning entities directly can lead to:
- LazyInitializationException
- Infinite JSON recursion
- Exposing sensitive fields (password, salary, etc.)
- Tight coupling between database and API
- Difficulty in changing database schema without affecting clients
Example:
β Returning Entity
@GetMapping
public Employee getEmployee {
return repository.findById(id).get;
}
β Returning DTO
@GetMapping
public EmployeeDTO getEmployee {
return employeeService.getEmployee(id);
}
Interview Tip: Always expose DTOs, not Entities, in REST APIs.
| DTO | Entity |
|---|---|
| Used for API communication | Used for database persistence |
| Contains only required fields | Represents complete database table |
| No business persistence logic | Managed by Hibernate |
| Safe to expose | Should not be exposed directly |
Example
Entity:
Employee
id
name
salary
password
DTO:
EmployeeDTO
name
department
Password and internal fields remain hidden.
Interview Tip: Entity = Database Model
DTO = API Model
Best Practices
β Keep entities lightweight.
β Use wrapper classes (Long, Integer) instead of primitives where null values are meaningful.
β Always define a primary key.
β Use EnumType.STRING instead of ORDINAL.
β Avoid business logic inside entities.
β Use LocalDate/LocalDateTime instead of java.util.Date.
β Implement equals and hashCode carefully (typically based on immutable business keys or primary key after persistence).
β Keep constructors simple and provide a no-argument constructor.
Interview Tip: Entities should primarily model the database and domain, while business logic belongs in the service layer.
Best Practices
β Prefer LAZY fetching.
β Prefer @ManyToOne over @OneToMany where appropriate.
β Avoid unnecessary bidirectional relationships.
β Avoid direct @ManyToMany.
β Use CascadeType selectively.
β Keep collections small whenever possible.
β Maintain both sides of bidirectional relationships in code.
Interview Tip: Most Hibernate performance issues originate from poorly designed entity relationships.
Use @Transactional whenever multiple database operations must succeed or fail as a single unit.
Example:
@Transactional
public void placeOrder {
saveOrder;
updateInventory;
createPayment;
}
If any operation fails:
Rollback
Otherwise:
Commit
Common Use Cases
- Order processing
- Money transfer
- User registration
- Inventory updates
- Bulk operations
Interview Tip: Place @Transactional on service-layer methods, not controllers.
Mistakes
β Using FetchType.EAGER everywhere.
β Returning entities directly from REST APIs.
β Calling findAll on very large tables.
β Ignoring the N+1 Query Problem.
β Using CascadeType.ALL blindly.
β Forgetting database indexes.
β Loading entire entities when only a few columns are needed.
β Executing database queries inside loops.
β Not using pagination.
β Ignoring SQL logs.
Interview Tip: Most Hibernate performance problems are caused by poor usage, not by Hibernate itself.
SQL logging helps understand exactly what Hibernate sends to the database.
Configuration:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
For parameter values (Spring Boot 3 / Hibernate 6):
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.orm.jdbc.bind=TRACE
Example:
select *
from employee
where id = 1;
Benefits
- Detect N+1 queries
- Debug generated SQL
- Verify joins
- Optimize performance
Interview Tip: Enable detailed SQL logging only in development or troubleshooting, not continuously in production.
Recommended Structure
controller
service
repository
entity
dto
mapper
exception
config
util
Naming Conventions
Entity:
Employee
DTO:
EmployeeDTO
Repository:
EmployeeRepository
Service:
EmployeeService
Implementation:
EmployeeServiceImpl
Controller:
EmployeeController
Interview Tip: A consistent project structure improves readability, maintainability, and onboarding for new developers.
N. Production Scenario Questions
Expand a question to read the answer. Tick when youβre prepared (progress saved on this device).
Lazy Loading fetches related data only when required, reducing unnecessary database access and memory usage.
Advantages:
- Better performance
- Lower memory consumption
- Faster initial response
- Avoids loading unused relationships
Use Fetch Join or EntityGraph when related data is actually needed.
Interview Tip: Use LAZY by default and fetch eagerly only for specific use cases.
Solutions include:
β JOIN FETCH
β EntityGraph
β DTO Projection
β Batch Fetching (@BatchSize)
β Optimizing entity relationships
Avoid changing everything to EAGER, as that often creates different performance issues.
Interview Tip: Always inspect the generated SQL to verify that N+1 has been eliminated.
Steps:
1. Enable SQL logging.
2. Analyze the generated SQL.
3. Check the database execution plan.
4. Add proper indexes.
5. Fetch only required columns (DTO Projection).
6. Remove unnecessary joins.
7. Use Pagination if applicable.
8. Cache frequently read data.
Interview Tip: Optimize only after identifying the real bottleneck through profiling and query analysis.
Recommended approach:
- Enable JDBC batching.
- Set hibernate.jdbc.batch_size (e.g., 50 or 100).
- Call flush and clear periodically.
- Use a single transaction or process in manageable chunks depending on memory and business requirements.
Example:
for (...) {
entityManager.persist(employee);
if (count % 50 == 0) {
entityManager.flush;
entityManager.clear;
}
}
Interview Tip: Never persist all 1 lakh entities without periodic flush and clear, as the Persistence Context can consume excessive memory.
Direct @ManyToMany relationships are inflexible.
Problems:
- Difficult to extend
- No place for additional attributes
- Hard to audit
- Complex updates
Preferred design:
Student
β
Enrollment
β
Course
The intermediate entity can store fields like:
- Enrolled Date
- Grade
- Status
Interview Tip: Use an intermediate entity instead of direct @ManyToMany in production.
Steps:
1. Add a deleted flag.
2. Use @SQLDelete to update the flag instead of deleting the row.
3. Use @Where (or in newer Hibernate versions, filters where appropriate) to exclude deleted records from normal queries.
Example:
@SQLDelete(sql =
"UPDATE employee SET deleted=true WHERE id=?")
@Where(clause="deleted=false")
Interview Tip: Soft Delete is ideal for auditability and data recovery.
Use multiple layers of protection:
β Database Unique Constraints
β @Column(unique = true) where appropriate
β Validate in the service layer
β Handle DataIntegrityViolationException
For concurrent scenarios, rely on the database constraint as the final guarantee.
Interview Tip: Never rely only on application-level validation to prevent duplicates.
Use:
β Optimistic Locking (@Version) for most business applications.
β Pessimistic Locking only when concurrent conflicts are frequent and strict locking is required.
Interview Tip: Optimistic Locking is generally preferred because it offers better scalability.
Avoid:
findAll
Use:
β Page when total record count is required.
β Slice when only “next page” information is needed.
β Keyset (cursor-based) pagination for very large datasets and infinite scrolling, as it avoids the performance cost of large offsets.
Interview Tip: For millions of records, keyset pagination is often more efficient than offset-based pagination.
Techniques:
- Enable caching for suitable data.
- Use DTO Projection.
- Solve N+1 queries.
- Use Fetch Join or EntityGraph.
- Batch database operations.
- Optimize indexes.
- Avoid repeated queries inside loops.
Interview Tip: Reducing unnecessary database round trips usually provides the biggest performance gains.
Use:
β DTO Projection
β JPQL Constructor Expressions
β Interface-based Projections
Example:
SELECT new EmployeeDTO(
e.name,
d.name
)
FROM Employee e
JOIN e.department d
Only the required columns are fetched.
Interview Tip: DTO Projection is the preferred approach for read-only REST APIs.
When JOIN FETCH is used with collection relationships (such as @OneToMany), the SQL result contains one row per parent-child combination.
Example:
Department
β
Employee 1
Employee 2
The SQL returns two rows for the same department.
Solutions:
- Use SELECT DISTINCT in JPQL.
- Return a Set where appropriate.
- Avoid unnecessary fetch joins on multiple collections.
Interview Tip: DISTINCT in JPQL helps Hibernate eliminate duplicate parent entities from the result.
Steps:
1. Enable SQL logging.
2. Enable Hibernate Statistics.
3. Look for N+1 queries.
4. Check execution plans in the database.
5. Verify indexes.
6. Monitor query execution time.
7. Profile the application with tools like VisualVM, JProfiler, or APM solutions.
Interview Tip: Always measure first, optimize second.
Techniques:
- Analyze SQL logs in lower environments.
- Enable Hibernate Statistics during troubleshooting.
- Use APM tools (e.g., New Relic, Dynatrace, AppDynamics, Elastic APM).
- Monitor unusually high query counts per request.
- Review slow query logs from the database.
Interview Tip: A sudden increase in SQL queries for a single API request is often the first sign of an N+1 problem.
A practical migration strategy is:
1. Identify frequently used JDBC modules.
2. Create JPA entities for the corresponding tables.
3. Replace DAO classes with Spring Data JPA repositories.
4. Start with simple CRUD operations.
5. Convert complex SQL gradually using JPQL or native queries where necessary.
6. Add integration tests to validate behavior and performance.
7. Optimize fetch strategies, indexes, and transactions after migration.
Interview Tip: Avoid a “big bang” migration. Migrate module by module, verify functionality and performance at each step, and keep complex native SQL where it still provides clear value.”,