EntityManager in JPA and Hibernate
Complete guide to JPA EntityManager with persistence context, entity lifecycle methods, CRUD operations, dirty checking, flush, clear, detach, refresh, getReference, examples, diagrams, and interview questions.
What is EntityManager?
EntityManager is the main JPA interface used to interact with the database.
It manages entity objects and provides operations like:
persist
find
merge
remove
detach
refresh
flush
clear
Simple meaning:
EntityManager
=
Manager of Entity Lifecycle
It is responsible for moving entities between different states:
Transient
Managed
Detached
Removed
Why EntityManager is Important
Spring Data JPA hides many internal details behind repositories.
Example:
employeeRepository.save(employee);
Internally, JPA/Hibernate uses EntityManager.
So to deeply understand Hibernate, you must understand EntityManager.
High Level Architecture
flowchart TD
A["Spring Boot Service"]
B["EntityManager"]
C["Persistence Context"]
D["Hibernate Session"]
E["JDBC"]
F["Database"]
A --> B
B --> C
B --> D
D --> E
E --> F
EntityManager Responsibilities
| Responsibility | Description |
|---|---|
| Persist | Insert new entity |
| Find | Retrieve entity by primary key |
| Merge | Reattach detached entity |
| Remove | Delete entity |
| Flush | Synchronize changes to DB |
| Clear | Remove all entities from persistence context |
| Detach | Remove one entity from persistence context |
| Refresh | Reload entity from DB |
| Lock | Apply optimistic or pessimistic lock |
Sample Entity
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "employees")
public class Employee {
@Id
private Long id;
private String name;
private String department;
private Double salary;
public Employee() {
}
public Employee(Long id, String name, String department, Double salary) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
}
// getters and setters
}
Repository Using EntityManager
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class EmployeeJpaRepository {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void save(Employee employee) {
entityManager.persist(employee);
}
public Employee findById(Long id) {
return entityManager.find(Employee.class, id);
}
@Transactional
public Employee update(Employee employee) {
return entityManager.merge(employee);
}
@Transactional
public void delete(Long id) {
Employee employee = entityManager.find(Employee.class, id);
if (employee != null) {
entityManager.remove(employee);
}
}
}
Entity Lifecycle Diagram
stateDiagram-v2
[*] --> Transient
Transient --> Managed : persist
Managed --> Detached : detach or close
Detached --> Managed : merge
Managed --> Removed : remove
Removed --> [*]
1. persist Method
persist() is used to make a new entity managed.
It schedules an INSERT operation.
@Transactional
public void createEmployee() {
Employee employee =
new Employee(1L, "Venu", "Engineering", 100000.0);
entityManager.persist(employee);
}
Generated SQL:
INSERT INTO employees
(id, name, department, salary)
VALUES
(1, 'Venu', 'Engineering', 100000);
persist Flow
flowchart LR
A["New Object"]
B["persist"]
C["Managed Entity"]
D["Insert On Flush"]
A --> B
B --> C
C --> D
Important Point About persist
After calling persist():
Employee employee =
new Employee(1L, "Venu", "Engineering", 100000.0);
entityManager.persist(employee);
employee.setSalary(120000.0);
Hibernate tracks the object.
During commit:
INSERT INTO employees
(id, name, department, salary)
VALUES
(1, 'Venu', 'Engineering', 120000);
Because entity became managed.
2. find Method
find() loads entity by primary key.
public Employee getEmployee(Long id) {
return entityManager.find(Employee.class, id);
}
Generated SQL:
SELECT *
FROM employees
WHERE id = 1;
find With First Level Cache
@Transactional
public void findExample() {
Employee e1 =
entityManager.find(Employee.class, 1L);
Employee e2 =
entityManager.find(Employee.class, 1L);
System.out.println(e1 == e2);
}
Output:
true
SQL executes only once because first level cache returns the same object.
find Flow
flowchart TD
A["find entity"]
B["Check First Level Cache"]
C["Return Cached Entity"]
D["Execute Select Query"]
E["Store In Persistence Context"]
F["Return Entity"]
A --> B
B -->|Hit| C
B -->|Miss| D
D --> E
E --> F
3. merge Method
merge() is used to copy detached entity state into a managed entity.
Commonly used for updates.
@Transactional
public Employee updateEmployee(Employee detachedEmployee) {
Employee managedEmployee =
entityManager.merge(detachedEmployee);
return managedEmployee;
}
Important:
merge does not make the same detached object managed.
merge returns a new managed object.
merge Example
Employee detachedEmployee =
new Employee(1L, "Venu", "Architecture", 150000.0);
Employee managedEmployee =
entityManager.merge(detachedEmployee);
System.out.println(detachedEmployee == managedEmployee);
Output:
false
merge Flow
flowchart TD
A["Detached Entity"]
B["merge"]
C["Find Managed Copy"]
D["Copy Values"]
E["Return Managed Entity"]
F["Update On Flush"]
A --> B
B --> C
C --> D
D --> E
E --> F
merge Internal Working
Detached Object
↓
merge
↓
Hibernate checks persistence context
↓
If found, copy values
↓
If not found, load from DB
↓
Return managed object
↓
Dirty checking updates DB
4. remove Method
remove() deletes a managed entity.
@Transactional
public void deleteEmployee(Long id) {
Employee employee =
entityManager.find(Employee.class, id);
if (employee != null) {
entityManager.remove(employee);
}
}
Generated SQL:
DELETE FROM employees
WHERE id = 1;
remove Flow
flowchart LR
A["Managed Entity"]
B["remove"]
C["Removed State"]
D["Delete On Flush"]
A --> B
B --> C
C --> D
remove Detached Entity Problem
This does not work correctly:
@Transactional
public void deleteDetached(Employee employee) {
entityManager.remove(employee);
}
If employee is detached, JPA may throw:
IllegalArgumentException
Correct way:
@Transactional
public void deleteDetached(Employee detachedEmployee) {
Employee managedEmployee =
entityManager.merge(detachedEmployee);
entityManager.remove(managedEmployee);
}
5. detach Method
detach() removes one entity from persistence context.
After detach, dirty checking will not work.
@Transactional
public void detachExample() {
Employee employee =
entityManager.find(Employee.class, 1L);
entityManager.detach(employee);
employee.setSalary(200000.0);
}
No UPDATE SQL.
detach Flow
flowchart LR
A["Managed Entity"]
B["detach"]
C["Detached Entity"]
D["Changes Not Tracked"]
A --> B
B --> C
C --> D
6. clear Method
clear() removes all managed entities from persistence context.
@Transactional
public void clearExample() {
Employee e1 =
entityManager.find(Employee.class, 1L);
Employee e2 =
entityManager.find(Employee.class, 2L);
entityManager.clear();
e1.setSalary(200000.0);
e2.setSalary(300000.0);
}
No UPDATE SQL.
Because both entities are detached.
clear Diagram
flowchart TD
A["Persistence Context"]
B["Employee 1"]
C["Employee 2"]
D["clear"]
E["Empty Persistence Context"]
A --> B
A --> C
A --> D
D --> E
7. refresh Method
refresh() reloads entity state from database.
It overwrites local changes.
@Transactional
public void refreshExample() {
Employee employee =
entityManager.find(Employee.class, 1L);
employee.setSalary(999999.0);
entityManager.refresh(employee);
System.out.println(employee.getSalary());
}
If database salary is 100000, output:
100000
Local unsaved change is lost.
refresh Flow
flowchart TD
A["Managed Entity"]
B["Local Change"]
C["refresh"]
D["Select From Database"]
E["Overwrite Entity State"]
A --> B
B --> C
C --> D
D --> E
8. flush Method
flush() synchronizes persistence context changes to database immediately.
It does not commit the transaction.
@Transactional
public void flushExample() {
Employee employee =
entityManager.find(Employee.class, 1L);
employee.setSalary(200000.0);
entityManager.flush();
System.out.println("SQL executed before commit");
}
Generated SQL happens at flush:
UPDATE employees
SET salary = 200000
WHERE id = 1;
flush Flow
flowchart LR
A["Managed Entity Changed"]
B["flush"]
C["Dirty Checking"]
D["SQL Executed"]
E["Transaction Still Active"]
A --> B
B --> C
C --> D
D --> E
flush vs commit
| Concept | Meaning |
|---|---|
| flush | Sends SQL to database |
| commit | Permanently saves transaction |
| rollback | Cancels uncommitted changes |
Important:
flush is not commit
9. getReference Method
getReference() returns a lazy proxy.
It may not hit database immediately.
@Transactional
public void referenceExample() {
Employee employee =
entityManager.getReference(Employee.class, 1L);
System.out.println(employee.getId());
}
Usually no SQL when accessing only ID.
But if you access another field:
System.out.println(employee.getName());
Hibernate executes SQL.
getReference Flow
flowchart TD
A["getReference"]
B["Create Proxy"]
C["Access ID Only"]
D["No SQL"]
E["Access Other Field"]
F["Execute Select Query"]
A --> B
B --> C
C --> D
B --> E
E --> F
find vs getReference
| Feature | find | getReference |
|---|---|---|
| Return Type | Entity | Proxy |
| DB Hit | Immediate | Lazy |
| If Not Found | Returns null | Exception on access |
| Usage | Normal read | Reference relation |
Practical Use of getReference
Suppose you create an order for an existing customer.
You do not need to load full customer data.
@Transactional
public void createOrder(Long customerId) {
Customer customer =
entityManager.getReference(Customer.class, customerId);
Order order = new Order();
order.setCustomer(customer);
order.setAmount(500.0);
entityManager.persist(order);
}
This avoids unnecessary SELECT for customer.
Complete CRUD Service
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class EmployeeService {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void create() {
Employee employee =
new Employee(1L, "Venu", "Engineering", 100000.0);
entityManager.persist(employee);
}
@Transactional(readOnly = true)
public Employee read(Long id) {
return entityManager.find(Employee.class, id);
}
@Transactional
public Employee update(Long id) {
Employee employee =
entityManager.find(Employee.class, id);
employee.setDepartment("Architecture");
employee.setSalary(150000.0);
return employee;
}
@Transactional
public void delete(Long id) {
Employee employee =
entityManager.find(Employee.class, id);
if (employee != null) {
entityManager.remove(employee);
}
}
}
Important update point:
employee.setSalary(150000.0);
No save() needed because entity is managed.
Dirty checking performs update.
CRUD Flow Diagram
flowchart TD
A["Create"]
B["persist"]
C["Insert"]
D["Read"]
E["find"]
F["Select"]
G["Update"]
H["Dirty Checking"]
I["Update SQL"]
J["Delete"]
K["remove"]
L["Delete SQL"]
A --> B
B --> C
D --> E
E --> F
G --> H
H --> I
J --> K
K --> L
EntityManager and Transactions
EntityManager operations need transaction for write operations.
| Operation | Transaction Needed |
|---|---|
| find | Not mandatory |
| persist | Yes |
| merge | Yes |
| remove | Yes |
| flush | Yes |
| refresh | Usually yes |
EntityManager in Spring Boot
Use:
@PersistenceContext
private EntityManager entityManager;
Spring injects a transaction-scoped EntityManager.
That means:
One EntityManager per transaction
Common Mistake
Wrong:
public void updateEmployee(Long id) {
Employee employee =
entityManager.find(Employee.class, id);
employee.setSalary(200000.0);
}
If method is not inside transaction, update may not be flushed.
Correct:
@Transactional
public void updateEmployee(Long id) {
Employee employee =
entityManager.find(Employee.class, id);
employee.setSalary(200000.0);
}
EntityManager vs Repository
| Feature | EntityManager | Spring Data Repository |
|---|---|---|
| Level | Low level | High level |
| Control | More | Less |
| Boilerplate | More | Less |
| Best For | Complex operations | Simple CRUD |
| Learning Hibernate | Best | Hides internals |
When to Use EntityManager Directly
Use EntityManager when:
✅ Bulk operations
✅ Custom JPQL
✅ Native SQL
✅ Batch processing
✅ Fine control over flush and clear
✅ getReference optimization
✅ Detach or refresh needed
Batch Processing Example
@Transactional
public void increaseSalaryForAll(List<Employee> employees) {
int batchSize = 50;
for (int i = 0; i < employees.size(); i++) {
Employee employee = employees.get(i);
employee.setSalary(employee.getSalary() + 1000);
if (i > 0 && i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
}
}
Why?
flush sends SQL
clear frees memory
Batch Flow
flowchart TD
A["Process 50 Entities"]
B["flush"]
C["SQL Executed"]
D["clear"]
E["Memory Released"]
F["Next Batch"]
A --> B
B --> C
C --> D
D --> E
E --> F
JPQL Example
public List<Employee> findByDepartment(String department) {
return entityManager
.createQuery(
"select e from Employee e where e.department = :department",
Employee.class
)
.setParameter("department", department)
.getResultList();
}
Generated SQL:
SELECT *
FROM employees
WHERE department = ?;
Native Query Example
public List<Employee> findHighSalaryEmployees(Double salary) {
return entityManager
.createNativeQuery(
"SELECT * FROM employees WHERE salary > ?",
Employee.class
)
.setParameter(1, salary)
.getResultList();
}
Bulk Update Example
@Transactional
public int updateDepartmentSalary(String department, Double salary) {
return entityManager
.createQuery(
"update Employee e set e.salary = :salary where e.department = :department"
)
.setParameter("salary", salary)
.setParameter("department", department)
.executeUpdate();
}
Important:
Bulk update bypasses persistence context.
After bulk update:
entityManager.clear();
Recommended to avoid stale cached entities.
Bulk Update Diagram
flowchart TD
A["Persistence Context Has Old Entity"]
B["JPQL Bulk Update"]
C["Database Updated Directly"]
D["Persistence Context Still Old"]
E["clear"]
F["Reload Fresh Data"]
A --> B
B --> C
C --> D
D --> E
E --> F
Interview Questions
Q1. What is EntityManager?
EntityManager is the JPA interface used to manage entity lifecycle and interact with persistence context.
Q2. What is Persistence Context?
It is the first level cache where managed entities are stored.
Q3. Difference between persist and merge?
| persist | merge |
|---|---|
| New entity | Detached entity |
| Same object becomes managed | Returns managed copy |
| Insert | Update or insert |
| No return value | Returns entity |
Q4. Difference between flush and commit?
flush sends SQL to database.
commit makes transaction permanent.
Q5. Difference between detach and clear?
detach removes one entity.
clear removes all entities.
Q6. Difference between find and getReference?
find loads immediately.
getReference returns lazy proxy.
Q7. Does merge make detached object managed?
No.
It returns another managed object.
Best Practices
✅ Use repositories for simple CRUD
✅ Use EntityManager for advanced control
✅ Always use @Transactional for write operations
✅ Use flush() and clear() for batch jobs
✅ Use getReference() when only foreign key reference is needed
✅ Use refresh() carefully because it overwrites local changes
❌ Do not use detached entity with remove()
❌ Do not forget clear() after bulk updates
❌ Do not assume flush() means commit
Summary
EntityManager is the heart of JPA.
It controls:
Entity Lifecycle
Persistence Context
Dirty Checking
First Level Cache
Database Synchronization
Main methods:
persist -> make new entity managed
find -> load entity
merge -> copy detached state into managed entity
remove -> delete managed entity
detach -> remove one entity from context
clear -> remove all entities
refresh -> reload from database
flush -> synchronize SQL
getReference -> lazy proxy reference
Key rule:
Managed Entity + Transaction = Automatic Dirty Checking