Full Stack • Java • System Design • Cloud • AI Engineering

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