← Home

JPA CRUD — One Complete Project

Employee Management System covering ORM, JPA, persistence.xml, EntityManagerFactory, EntityManager, Entity lifecycle, DTO, DAO, Service, Factory/Utility, transactions and complete CRUD using persist(), find(), JPQL, merge(), dirty checking and remove().
JPA 2.2 Hibernate MySQL Layered Architecture Complete CRUD Mermaid SVG Diagrams
01 — BIG PICTURE

Complete Layered Architecture

This is the same style of architecture as the reference image, but generated entirely from Mermaid code and rendered as an SVG diagram in the browser.

flowchart TB UI["1. UI / Presentation Layer
UITester.java"] SERVICE["2. Service Layer
EmployeeService.java
EmployeeServiceImpl.java"] FACTORY["3. Utility Layer
Factory.java"] DAO["4. DAO Layer
EmployeeDAO.java
EmployeeDAOImpl.java"] JPA["5. JPA / ORM Layer"] EMF["EntityManagerFactory"] EM["EntityManager"] ENTITY["EmployeeEntity.java
@Entity"] DB[("6. MySQL Database")] UI -->|"1. calls"| SERVICE SERVICE -->|"2. obtains/uses DAO"| FACTORY FACTORY -->|"3. creates"| DAO DAO -->|"4. obtains EMF"| FACTORY FACTORY -->|"5. provides"| EMF EMF -->|"6. creates"| EM EM -->|"7. manages"| ENTITY EM -->|"8. executes SQL"| DB UI -. "Carries EmployeeDTO data" .-> SERVICE SERVICE -. "Business rules" .-> DAO DAO -. "CRUD" .-> EM
Memory: UI → Service → DAO → EntityManager → Hibernate → Database. EntityManagerFactory is the factory that creates EntityManagers.
02 — BOOTSTRAPPING

How a JPA Program Starts

flowchart LR XML["META-INF/persistence.xml"] P["Persistence"] PU["Persistence Unit
employeePU"] EMF["EntityManagerFactory"] EM1["EntityManager"] EM2["EntityManager"] XML --> P P --> PU PU --> EMF EMF --> EM1 EMF --> EM2
EntityManagerFactory emf =
    Persistence.createEntityManagerFactory("employeePU");

EntityManager em =
    emf.createEntityManager();

EntityManagerFactory

A heavyweight factory associated with a persistence unit. It creates EntityManager instances.

EntityManager

Works with the persistence context and provides persist, find, merge, remove and query operations.

persistence.xml

Contains persistence-unit configuration such as provider, database properties and managed entity classes.

03 — ENTITY

Java Entity → Database Table

flowchart LR JAVA["Employee.java
@Entity"] MAPPING["JPA Mapping
@Table @Id @Column"] HIB["Hibernate"] TABLE[("employee table
employee_id
employee_name
role
salary
joining_date")] JAVA --> MAPPING --> HIB --> TABLE
@Entity
@Table(name = "employee")
public class Employee {

    @Id
    @Column(name = "employee_id")
    private int employeeId;

    @Column(name = "employee_name")
    private String employeeName;

    @Column(name = "role")
    private String role;

    private double salary;

    @Temporal(TemporalType.DATE)
    private Date joiningDate;
}
04 — CRUD

Complete CRUD Flow

flowchart TB C["CREATE"] --> P["persist(entity)"] P --> I["INSERT"] I --> DB[("Database")] R["READ"] --> F["find(Entity.class, id)"] F --> S["SELECT by Primary Key"] S --> DB RA["READ ALL"] --> Q["JPQL createQuery()"] Q --> S2["SELECT entities"] S2 --> DB U["UPDATE"] --> D["Managed entity + Dirty Checking"] U --> M["Detached entity + merge()"] D --> UP["UPDATE"] M --> UP UP --> DB DE["DELETE"] --> RF["find()"] RF --> RM["remove(managedEntity)"] RM --> DEL["DELETE"] DEL --> DB
OperationJPA APIWhat it means
Createpersist()Makes a new entity managed and schedules insertion.
Read onefind()Finds an entity using its primary-key value.
Read allcreateQuery()Runs JPQL and returns entities.
Update managedDirty checkingChange a managed entity; JPA detects the change at flush/commit.
Update detachedmerge()Copies detached state into a managed instance.
Deleteremove()Marks a managed entity for deletion.
05 — CREATE

Persist Operation

sequenceDiagram participant UI as UITester participant S as EmployeeService participant D as EmployeeDAO participant EM as EntityManager participant H as Hibernate participant DB as MySQL UI->>S: createEmployee(employee) S->>D: save(employee) D->>EM: begin() D->>EM: persist(employee) EM->>H: synchronize persistence context D->>EM: commit() H->>DB: INSERT employee DB-->>H: success H-->>EM: success EM-->>D: success D-->>S: success S-->>UI: created
em.getTransaction().begin();

em.persist(employee);

em.getTransaction().commit();

persist() does not mean "directly execute INSERT right now". It makes the entity managed; Hibernate synchronizes changes with the database when the persistence context is flushed, commonly at transaction commit.

06 — READ

Find Operation

sequenceDiagram participant UI as UITester participant S as Service participant D as DAO participant EM as EntityManager participant DB as MySQL UI->>S: getEmployee(1) S->>D: findById(1) D->>EM: find(Employee.class, 1) EM->>DB: SELECT by employee_id = 1 DB-->>EM: row EM-->>D: Employee D-->>S: Employee S-->>UI: Employee
Employee employee =
    em.find(Employee.class, 1);
Important: The second argument is the value of the entity's @Id, not an arbitrary column value.
07 — READ ALL

JPQL Query

List<Employee> employees =
    em.createQuery(
        "SELECT e FROM Employee e",
        Employee.class
    ).getResultList();
flowchart LR JPQL["JPQL
SELECT e FROM Employee e"] H["Hibernate"] SQL["SQL
SELECT ... FROM employee"] DB[("MySQL")] RESULT["List<Employee>"] JPQL --> H --> SQL --> DB --> H --> RESULT

JPQL talks about entities and their fields, not database tables directly.

08 — UPDATE

Dirty Checking vs merge()

Managed entity

em.getTransaction().begin();

Employee e =
    em.find(Employee.class, 1);

e.setSalary(75000);

em.getTransaction().commit();

No update() call is required. Hibernate detects the changed state during dirty checking.

Detached entity

em.getTransaction().begin();

em.merge(employee);

em.getTransaction().commit();

merge() copies the state into a managed instance.

flowchart TB F["find()"] --> MAN["MANAGED"] MAN --> CHANGE["setSalary(75000)"] CHANGE --> DIRTY["Dirty Checking"] DIRTY --> UPDATE["UPDATE SQL"] DET["DETACHED"] --> MERGE["merge()"] MERGE --> MAN2["MANAGED COPY"] MAN2 --> UPDATE2["UPDATE SQL"]
09 — DELETE

Remove Operation

em.getTransaction().begin();

Employee employee =
    em.find(Employee.class, id);

if (employee != null) {
    em.remove(employee);
}

em.getTransaction().commit();
flowchart LR ID["Primary Key"] --> FIND["find()"] FIND --> MANAGED["Managed Employee"] MANAGED --> REMOVE["remove()"] REMOVE --> COMMIT["commit()"] COMMIT --> SQL["DELETE SQL"] SQL --> DB[("MySQL")]
Trap: remove() should be applied to a managed entity. A common safe pattern is find → remove → commit.
10 — ENTITY LIFECYCLE

Entity Life Cycle

stateDiagram-v2 [*] --> Transient: new Employee() Transient --> Managed: persist() Managed --> Detached: detach() / clear() / close() Detached --> Managed: merge() Managed --> Removed: remove() Removed --> [*]: commit / flush note right of Transient Not tracked by persistence context end note note right of Managed Tracked by persistence context Dirty checking applies end note note right of Detached Object exists but is not currently tracked end note
StateMeaningTypical transition
TransientNew Java object, not managed.persist() → Managed
ManagedTracked by the persistence context.Dirty checking / remove / detach
DetachedPreviously managed but no longer tracked.merge() → Managed
RemovedManaged entity marked for deletion.commit/flush → database DELETE
11 — DTO

DTO and Entity Flow

flowchart LR CLIENT["UI / Client"] REQ["EmployeeDTO
Request"] SERVICE["Service"] ENTITY["Employee Entity"] DAO["DAO"] EM["EntityManager"] DB[("Database")] RESP["EmployeeDTO
Response"] CLIENT --> REQ --> SERVICE SERVICE --> ENTITY --> DAO --> EM --> DB DB --> EM --> DAO --> ENTITY --> SERVICE --> RESP --> CLIENT
DTO is a data-transfer model. Entity is the persistence model. In a larger application, keeping them separate prevents database-specific details from leaking into the API layer.
12 — TRANSACTIONS

Transaction Flow

flowchart TB BEGIN["begin()"] OP["JPA operation
persist / merge / remove"] FLUSH["Persistence context flush"] SQL["Hibernate generates SQL"] COMMIT["commit()"] ROLLBACK["rollback()"] DB[("Database")] BEGIN --> OP --> FLUSH --> SQL --> DB DB --> COMMIT OP -. "Exception" .-> ROLLBACK ROLLBACK -. "undo transaction" .-> DB
try {

    em.getTransaction().begin();

    em.persist(employee);

    em.getTransaction().commit();

} catch (Exception e) {

    if (em.getTransaction().isActive()) {
        em.getTransaction().rollback();
    }

    throw e;

} finally {

    em.close();
}
13 — COMPONENT RESPONSIBILITIES

Who Does What?

ComponentResponsibility
UI / TesterStarts the operation and displays the result.
DTOCarries data between application layers.
ServiceContains business rules and orchestrates operations.
DAOContains persistence/database access logic.
Factory / UtilityCentralizes object creation/configuration.
EntityManagerFactoryCreates EntityManagers for a persistence unit.
EntityManagerManages persistence context and JPA operations.
EntityJava representation of persistent data.
HibernateJPA provider/ORM implementation that generates database interaction.
MySQLStores the persistent rows.
14 — PROJECT CALL FLOW

What Actually Happens During a Create?

sequenceDiagram autonumber participant UI as UITester.java participant S as EmployeeServiceImpl.java participant D as EmployeeDAOImpl.java participant U as JPAUtility.java participant F as EntityManagerFactory participant EM as EntityManager participant E as Employee Entity participant H as Hibernate participant DB as MySQL UI->>S: createEmployee(EmployeeDTO) S->>S: validate business rules S->>D: save(Employee) D->>U: getEntityManager() U->>F: createEntityManager() F-->>U: EntityManager U-->>D: EntityManager D->>EM: begin() D->>EM: persist(Employee) EM->>E: add to persistence context D->>EM: commit() EM->>H: flush changes H->>DB: INSERT DB-->>H: success H-->>EM: success EM-->>D: success D-->>S: success S-->>UI: success response
15 — COMPLETE CODE MAP

How the Files Connect

flowchart TB MAIN["Main.java / UITester.java"] DTO["EmployeeDTO.java"] SI["EmployeeService.java"] S["EmployeeServiceImpl.java"] DI["EmployeeDAO.java"] D["EmployeeDAOImpl.java"] F["Factory.java"] U["JPAUtility.java"] E["Employee.java / EmployeeEntity.java"] XML["persistence.xml"] EMF["EntityManagerFactory"] EM["EntityManager"] DB[("MySQL")] MAIN --> DTO MAIN --> S S -. implements .-> SI S --> D D -. implements .-> DI D --> F F --> U U --> XML U --> EMF EMF --> EM EM --> E EM --> DB DTO -. "data mapping" .-> E
16A — COMPLETE PROJECT CODE

Every File — persistence.xml → UI Tester

This is the complete runnable version of the same Employee CRUD project. The code is intentionally kept in the same order in which the application is understood: Maven → persistence.xml → Entity → DTO → Utility → DAO → Service → UI Tester.

Before running: create the MySQL database employee_db, change the username/password in persistence.xml, then run UITester.java.

1. Project Structure

EmployeeManagement/
│
├── pom.xml
│
└── src/
    └── main/
        ├── java/
        │   └── com/example/
        │       ├── entity/
        │       │   └── EmployeeEntity.java
        │       │
        │       ├── dto/
        │       │   └── EmployeeDTO.java
        │       │
        │       ├── dao/
        │       │   ├── EmployeeDAO.java
        │       │   └── EmployeeDAOImpl.java
        │       │
        │       ├── service/
        │       │   ├── EmployeeService.java
        │       │   └── EmployeeServiceImpl.java
        │       │
        │       └── utility/
        │           ├── JPAUtility.java
        │           └── Factory.java
        │
        │       └── UITester.java
        │
        └── resources/
            └── META-INF/
                └── persistence.xml

2. Database SQL

CREATE DATABASE employee_db;

USE employee_db;

CREATE TABLE employee (
    employee_id INT PRIMARY KEY,
    employee_name VARCHAR(100) NOT NULL,
    role VARCHAR(100),
    salary DOUBLE,
    joining_date DATE
);

3. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="
         http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>EmployeeManagement</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!-- JPA API -->
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>javax.persistence-api</artifactId>
            <version>2.2</version>
        </dependency>

        <!-- Hibernate JPA implementation -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.6.15.Final</version>
        </dependency>

        <!-- MySQL JDBC Driver -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

    </dependencies>

</project>

4. persistence.xml

Location: src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

<persistence
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
    version="2.2">

    <persistence-unit name="employeePU">

        <provider>
            org.hibernate.jpa.HibernatePersistenceProvider
        </provider>

        <class>com.example.entity.EmployeeEntity</class>

        <properties>

            <property
                name="javax.persistence.jdbc.driver"
                value="com.mysql.cj.jdbc.Driver"/>

            <property
                name="javax.persistence.jdbc.url"
                value="jdbc:mysql://localhost:3306/employee_db"/>

            <property
                name="javax.persistence.jdbc.user"
                value="root"/>

            <property
                name="javax.persistence.jdbc.password"
                value="root"/>

            <property
                name="hibernate.dialect"
                value="org.hibernate.dialect.MySQL8Dialect"/>

            <property
                name="hibernate.show_sql"
                value="true"/>

            <property
                name="hibernate.format_sql"
                value="true"/>

        </properties>

    </persistence-unit>

</persistence>
Connection: employeePU here must exactly match Persistence.createEntityManagerFactory("employeePU").

5. EmployeeEntity.java

package com.example.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import java.util.Date;

@Entity
@Table(name = "employee")
public class EmployeeEntity {

    @Id
    @Column(name = "employee_id")
    private int employeeId;

    @Column(name = "employee_name")
    private String employeeName;

    @Column(name = "role")
    private String role;

    @Column(name = "salary")
    private double salary;

    @Temporal(TemporalType.DATE)
    @Column(name = "joining_date")
    private Date joiningDate;

    public EmployeeEntity() {
    }

    public EmployeeEntity(
            int employeeId,
            String employeeName,
            String role,
            double salary,
            Date joiningDate) {

        this.employeeId = employeeId;
        this.employeeName = employeeName;
        this.role = role;
        this.salary = salary;
        this.joiningDate = joiningDate;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Date getJoiningDate() {
        return joiningDate;
    }

    public void setJoiningDate(Date joiningDate) {
        this.joiningDate = joiningDate;
    }

    @Override
    public String toString() {
        return "EmployeeEntity{" +
                "employeeId=" + employeeId +
                ", employeeName='" + employeeName + '\'' +
                ", role='" + role + '\'' +
                ", salary=" + salary +
                ", joiningDate=" + joiningDate +
                '}';
    }
}

6. EmployeeDTO.java

package com.example.dto;

import java.util.Date;

public class EmployeeDTO {

    private int employeeId;
    private String employeeName;
    private String role;
    private double salary;
    private Date joiningDate;

    public EmployeeDTO() {
    }

    public EmployeeDTO(
            int employeeId,
            String employeeName,
            String role,
            double salary,
            Date joiningDate) {

        this.employeeId = employeeId;
        this.employeeName = employeeName;
        this.role = role;
        this.salary = salary;
        this.joiningDate = joiningDate;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Date getJoiningDate() {
        return joiningDate;
    }

    public void setJoiningDate(Date joiningDate) {
        this.joiningDate = joiningDate;
    }

    @Override
    public String toString() {
        return "EmployeeDTO{" +
                "employeeId=" + employeeId +
                ", employeeName='" + employeeName + '\'' +
                ", role='" + role + '\'' +
                ", salary=" + salary +
                ", joiningDate=" + joiningDate +
                '}';
    }
}

7. JPAUtility.java

This class centralizes the EntityManagerFactory and EntityManager creation.

package com.example.utility;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class JPAUtility {

    private static final EntityManagerFactory EMF =
            Persistence.createEntityManagerFactory("employeePU");

    private JPAUtility() {
        // Utility class - do not create objects.
    }

    public static EntityManager getEntityManager() {
        return EMF.createEntityManager();
    }

    public static EntityManagerFactory getEntityManagerFactory() {
        return EMF;
    }

    public static void close() {
        if (EMF.isOpen()) {
            EMF.close();
        }
    }
}

8. Factory.java

The Factory hides DAO implementation creation from the Service layer.

package com.example.utility;

import com.example.dao.EmployeeDAO;
import com.example.dao.EmployeeDAOImpl;

public final class Factory {

    private Factory() {
        // Utility class - do not create objects.
    }

    public static EmployeeDAO getEmployeeDAO() {
        return new EmployeeDAOImpl();
    }
}

9. EmployeeDAO.java

package com.example.dao;

import com.example.entity.EmployeeEntity;

import java.util.List;

public interface EmployeeDAO {

    void save(EmployeeEntity employee);

    EmployeeEntity findById(int id);

    List<EmployeeEntity> findAll();

    void update(EmployeeEntity employee);

    void delete(int id);
}

10. EmployeeDAOImpl.java — Complete CRUD

package com.example.dao;

import com.example.entity.EmployeeEntity;
import com.example.utility.JPAUtility;

import javax.persistence.EntityManager;
import java.util.List;

public class EmployeeDAOImpl implements EmployeeDAO {

    @Override
    public void save(EmployeeEntity employee) {

        EntityManager em =
                JPAUtility.getEntityManager();

        try {
            em.getTransaction().begin();

            em.persist(employee);

            em.getTransaction().commit();

        } catch (RuntimeException e) {

            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }

            throw e;

        } finally {
            em.close();
        }
    }

    @Override
    public EmployeeEntity findById(int id) {

        EntityManager em =
                JPAUtility.getEntityManager();

        try {
            return em.find(EmployeeEntity.class, id);

        } finally {
            em.close();
        }
    }

    @Override
    public List<EmployeeEntity> findAll() {

        EntityManager em =
                JPAUtility.getEntityManager();

        try {
            return em.createQuery(
                    "SELECT e FROM EmployeeEntity e",
                    EmployeeEntity.class
            ).getResultList();

        } finally {
            em.close();
        }
    }

    @Override
    public void update(EmployeeEntity employee) {

        EntityManager em =
                JPAUtility.getEntityManager();

        try {
            em.getTransaction().begin();

            em.merge(employee);

            em.getTransaction().commit();

        } catch (RuntimeException e) {

            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }

            throw e;

        } finally {
            em.close();
        }
    }

    @Override
    public void delete(int id) {

        EntityManager em =
                JPAUtility.getEntityManager();

        try {
            em.getTransaction().begin();

            EmployeeEntity employee =
                    em.find(EmployeeEntity.class, id);

            if (employee != null) {
                em.remove(employee);
            }

            em.getTransaction().commit();

        } catch (RuntimeException e) {

            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }

            throw e;

        } finally {
            em.close();
        }
    }
}

11. EmployeeService.java

package com.example.service;

import com.example.dto.EmployeeDTO;

import java.util.List;

public interface EmployeeService {

    void createEmployee(EmployeeDTO dto);

    EmployeeDTO getEmployee(int id);

    List<EmployeeDTO> getAllEmployees();

    void updateEmployee(EmployeeDTO dto);

    void deleteEmployee(int id);
}

12. EmployeeServiceImpl.java

package com.example.service;

import com.example.dao.EmployeeDAO;
import com.example.dto.EmployeeDTO;
import com.example.entity.EmployeeEntity;
import com.example.utility.Factory;

import java.util.ArrayList;
import java.util.List;

public class EmployeeServiceImpl
        implements EmployeeService {

    private final EmployeeDAO employeeDAO;

    public EmployeeServiceImpl() {

        employeeDAO =
                Factory.getEmployeeDAO();
    }

    @Override
    public void createEmployee(EmployeeDTO dto) {

        validate(dto);

        EmployeeEntity employee =
                toEntity(dto);

        employeeDAO.save(employee);
    }

    @Override
    public EmployeeDTO getEmployee(int id) {

        EmployeeEntity employee =
                employeeDAO.findById(id);

        if (employee == null) {
            return null;
        }

        return toDTO(employee);
    }

    @Override
    public List<EmployeeDTO> getAllEmployees() {

        List<EmployeeEntity> entities =
                employeeDAO.findAll();

        List<EmployeeDTO> result =
                new ArrayList<>();

        for (EmployeeEntity entity : entities) {
            result.add(toDTO(entity));
        }

        return result;
    }

    @Override
    public void updateEmployee(EmployeeDTO dto) {

        validate(dto);

        EmployeeEntity employee =
                toEntity(dto);

        employeeDAO.update(employee);
    }

    @Override
    public void deleteEmployee(int id) {

        EmployeeEntity employee =
                employeeDAO.findById(id);

        if (employee == null) {
            throw new IllegalArgumentException(
                    "Employee with ID " + id +
                    " does not exist."
            );
        }

        employeeDAO.delete(id);
    }

    private void validate(EmployeeDTO dto) {

        if (dto == null) {
            throw new IllegalArgumentException(
                    "Employee data cannot be null."
            );
        }

        if (dto.getEmployeeId() <= 0) {
            throw new IllegalArgumentException(
                    "Employee ID must be positive."
            );
        }

        if (dto.getEmployeeName() == null ||
                dto.getEmployeeName().trim().isEmpty()) {

            throw new IllegalArgumentException(
                    "Employee name is required."
            );
        }

        if (dto.getSalary() < 0) {
            throw new IllegalArgumentException(
                    "Salary cannot be negative."
            );
        }
    }

    private EmployeeEntity toEntity(
            EmployeeDTO dto) {

        return new EmployeeEntity(
                dto.getEmployeeId(),
                dto.getEmployeeName(),
                dto.getRole(),
                dto.getSalary(),
                dto.getJoiningDate()
        );
    }

    private EmployeeDTO toDTO(
            EmployeeEntity entity) {

        return new EmployeeDTO(
                entity.getEmployeeId(),
                entity.getEmployeeName(),
                entity.getRole(),
                entity.getSalary(),
                entity.getJoiningDate()
        );
    }
}

13. UITester.java — Complete CREATE / READ / UPDATE / DELETE

package com.example;

import com.example.dto.EmployeeDTO;
import com.example.service.EmployeeService;
import com.example.service.EmployeeServiceImpl;
import com.example.utility.JPAUtility;

import java.util.Date;
import java.util.List;

public class UITester {

    public static void main(String[] args) {

        EmployeeService service =
                new EmployeeServiceImpl();

        try {

            // =================================================
            // 1. CREATE
            // =================================================

            System.out.println(
                    "\\n========== CREATE =========="
            );

            EmployeeDTO employee =
                    new EmployeeDTO(
                            1001,
                            "Rahul",
                            "Java Developer",
                            50000.00,
                            new Date()
                    );

            service.createEmployee(employee);

            System.out.println(
                    "Employee created successfully!"
            );


            // =================================================
            // 2. READ ONE
            // =================================================

            System.out.println(
                    "\\n========== READ ONE =========="
            );

            EmployeeDTO found =
                    service.getEmployee(1001);

            if (found != null) {

                System.out.println(
                        "Employee found:"
                );

                System.out.println(found);

            } else {

                System.out.println(
                        "Employee not found."
                );
            }


            // =================================================
            // 3. READ ALL
            // =================================================

            System.out.println(
                    "\\n========== READ ALL =========="
            );

            List<EmployeeDTO> employees =
                    service.getAllEmployees();

            for (EmployeeDTO dto : employees) {
                System.out.println(dto);
            }


            // =================================================
            // 4. UPDATE
            // =================================================

            System.out.println(
                    "\\n========== UPDATE =========="
            );

            EmployeeDTO employeeToUpdate =
                    service.getEmployee(1001);

            if (employeeToUpdate != null) {

                employeeToUpdate.setSalary(
                        75000.00
                );

                employeeToUpdate.setRole(
                        "Senior Java Developer"
                );

                service.updateEmployee(
                        employeeToUpdate
                );

                System.out.println(
                        "Employee updated successfully!"
                );
            }


            // =================================================
            // 5. READ AFTER UPDATE
            // =================================================

            System.out.println(
                    "\\n========== READ AFTER UPDATE =========="
            );

            EmployeeDTO updated =
                    service.getEmployee(1001);

            System.out.println(updated);


            // =================================================
            // 6. DELETE
            // =================================================

            System.out.println(
                    "\\n========== DELETE =========="
            );

            service.deleteEmployee(1001);

            System.out.println(
                    "Employee deleted successfully!"
            );


            // =================================================
            // 7. VERIFY DELETE
            // =================================================

            System.out.println(
                    "\\n========== VERIFY DELETE =========="
            );

            EmployeeDTO deleted =
                    service.getEmployee(1001);

            if (deleted == null) {

                System.out.println(
                        "Employee does not exist anymore."
                );

            } else {

                System.out.println(
                        "Employee still exists: " +
                        deleted
                );
            }

        } catch (Exception e) {

            System.err.println(
                    "Operation failed: " +
                    e.getMessage()
            );

            e.printStackTrace();

        } finally {

            // Close the application-level
            // EntityManagerFactory.
            JPAUtility.close();
        }
    }
}

14. What the UI Tester actually triggers

sequenceDiagram participant UI as UITester participant S as EmployeeServiceImpl participant F as Factory participant D as EmployeeDAOImpl participant U as JPAUtility participant EMF as EntityManagerFactory participant EM as EntityManager participant H as Hibernate participant DB as MySQL UI->>S: createEmployee(dto) S->>S: DTO → Entity S->>D: save(entity) D->>U: getEntityManager() U->>EMF: createEntityManager() EMF-->>U: EM U-->>D: EM D->>EM: begin() D->>EM: persist(entity) D->>EM: commit() EM->>H: flush H->>DB: INSERT DB-->>H: success UI->>S: getEmployee(1001) S->>D: findById(1001) D->>EM: find(Entity.class, 1001) EM->>DB: SELECT DB-->>EM: row EM-->>D: entity D-->>S: entity S->>S: Entity → DTO S-->>UI: DTO UI->>S: updateEmployee(dto) S->>S: DTO → Entity S->>D: update(entity) D->>EM: begin() D->>EM: merge(entity) D->>EM: commit() EM->>H: UPDATE H->>DB: UPDATE UI->>S: deleteEmployee(1001) S->>D: delete(1001) D->>EM: begin() D->>EM: find(Entity.class, 1001) D->>EM: remove(entity) D->>EM: commit() EM->>H: DELETE H->>DB: DELETE

15. Expected Console Output

========== CREATE ==========
Employee created successfully!

========== READ ONE ==========
Employee found:
EmployeeDTO{
    employeeId=1001,
    employeeName='Rahul',
    role='Java Developer',
    salary=50000.0,
    ...
}

========== READ ALL ==========
EmployeeDTO{
    employeeId=1001,
    employeeName='Rahul',
    role='Java Developer',
    salary=50000.0,
    ...
}

========== UPDATE ==========
Employee updated successfully!

========== READ AFTER UPDATE ==========
EmployeeDTO{
    employeeId=1001,
    employeeName='Rahul',
    role='Senior Java Developer',
    salary=75000.0,
    ...
}

========== DELETE ==========
Employee deleted successfully!

========== VERIFY DELETE ==========
Employee does not exist anymore.
Now you can follow one request from top to bottom:
UITester → Service → Factory → DAO → JPAUtility → EntityManagerFactory → EntityManager → Hibernate → MySQL.
16 — COMPLETE DAO CODE

One Place to See All CRUD Methods

public class EmployeeDAOImpl implements EmployeeDAO {

    private EntityManagerFactory emf;

    public EmployeeDAOImpl() {
        emf = Persistence
                .createEntityManagerFactory("employeePU");
    }

    // CREATE
    public void save(Employee employee) {

        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            em.persist(employee);

            em.getTransaction().commit();

        } catch (Exception e) {

            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }

            throw e;

        } finally {
            em.close();
        }
    }

    // READ ONE
    public Employee findById(int id) {

        EntityManager em = emf.createEntityManager();

        try {
            return em.find(Employee.class, id);
        } finally {
            em.close();
        }
    }

    // READ ALL
    public List<Employee> findAll() {

        EntityManager em = emf.createEntityManager();

        try {
            return em.createQuery(
                    "SELECT e FROM Employee e",
                    Employee.class
            ).getResultList();

        } finally {
            em.close();
        }
    }

    // UPDATE
    public void update(Employee employee) {

        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            em.merge(employee);

            em.getTransaction().commit();

        } catch (Exception e) {

            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }

            throw e;

        } finally {
            em.close();
        }
    }

    // DELETE
    public void delete(int id) {

        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Employee employee =
                    em.find(Employee.class, id);

            if (employee != null) {
                em.remove(employee);
            }

            em.getTransaction().commit();

        } catch (Exception e) {

            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }

            throw e;

        } finally {
            em.close();
        }
    }
}
17 — INTERVIEW + EXAM TRAPS

Things You Must Not Mix Up

JPA vs Hibernate?

JPA is the specification/API. Hibernate is an implementation/provider of JPA.

EntityManagerFactory vs EntityManager?

EntityManagerFactory creates EntityManagers. EntityManager works with entities and the persistence context.

Does EntityManager have update()?

No. Managed entities are normally updated through dirty checking. Detached state can be re-associated through merge().

Why find() before remove()?

remove() expects a managed entity. find() obtains the entity through the current persistence context.

What does merge() return?

merge() returns a managed instance containing the merged state. Do not assume the original detached object becomes managed.

What is dirty checking?

Hibernate tracks managed entities and detects changes to their state, generating SQL during flush when required.

Where is persistence.xml?

META-INF/persistence.xml on the classpath.

What is persistence-unit name?

The logical configuration name used to bootstrap the EntityManagerFactory.

What happens if find() cannot locate the ID?

For EntityManager.find(), the result is null when no matching entity is found.

Why begin/commit?

Database-changing operations are performed within a transaction in this resource-local JPA example.

18 — FINAL MEMORY MAP

Learn This Sequence

persistence.xml
EntityManagerFactory
EntityManager
Entity
Hibernate
MySQL
CREATE
begin → persist → commit
READ
find()
READ ALL
JPQL → getResultList()
UPDATE
managed → dirty checking
UPDATE DETACHED
merge()
DELETE
find → remove → commit
The entire topic in one sentence: JPA uses persistence configuration to create an EntityManagerFactory, which creates EntityManagers that manage entity objects inside a persistence context; transactions synchronize those changes with the database through the JPA provider such as Hibernate.
19 — SCENARIO-BASED INTERVIEW QUESTIONS

50 Real JPA Interview Scenarios

Practice every question as Situation → Root cause → JPA concept → Fix → Interview answer. These are tied directly to the Employee CRUD project.

Interview mindset: The interviewer is usually testing whether you understand the entity state, persistence context and transaction boundary—not whether you memorized method names.
01. persist() succeeds but no row appears

What is happening: Check that a transaction was started and committed. persist() makes the entity managed; database synchronization happens during flush/commit.

What would you do: begin → persist → commit

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

02. persist() is called without begin()

What is happening: Resource-local JPA write operations require an active transaction.

What would you do: Start the transaction before persist().

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

03. find() returns null

What is happening: No entity exists for that primary-key value. find() normally returns null when nothing is found.

What would you do: Check the ID and database row.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

04. find() is called twice with the same EntityManager

What is happening: The persistence context is the first-level cache, so the same managed entity can be reused.

What would you do: Same EntityManager → same persistence context.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

05. find() uses two EntityManagers

What is happening: Each EntityManager has its own persistence context.

What would you do: Do not assume first-level cache is shared.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

06. Salary changes but no update() is called

What is happening: A managed entity is tracked by dirty checking.

What would you do: find → modify → commit.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

07. Salary changes after EntityManager.close()

What is happening: The entity is detached and the old persistence context no longer tracks it.

What would you do: Merge the state in a new persistence context.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

08. merge() is called but the original object is still detached

What is happening: merge copies state into a managed instance and returns that managed instance.

What would you do: Capture EmployeeEntity managed = em.merge(detached).

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

09. merge() return value is ignored

What is happening: Later changes to the original detached object are not automatically tracked.

What would you do: Continue with the object returned by merge().

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

10. remove() fails for an object

What is happening: remove requires a managed entity in the current persistence context.

What would you do: find → remove → commit.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

11. remove() is called without a transaction

What is happening: A database-changing operation is outside the required transaction.

What would you do: begin → remove → commit.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

12. An exception occurs halfway through a transaction

What is happening: The incomplete transaction should be rolled back.

What would you do: Rollback if active, then propagate the exception.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

13. commit() throws an exception

What is happening: The persistence operation did not complete successfully.

What would you do: Do not swallow the exception; handle rollback appropriately.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

14. EntityManagerFactory is created in every DAO method

What is happening: EntityManagerFactory is heavyweight and should not be repeatedly created.

What would you do: Create one application-level factory per persistence unit.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

15. EntityManager is never closed

What is happening: Persistence resources remain open unnecessarily.

What would you do: Close it in finally or an appropriate resource-management pattern.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

16. EntityManagerFactory is never closed

What is happening: Application-level persistence resources are not released.

What would you do: Close it during application shutdown.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

17. persistence.xml cannot be found

What is happening: It is not available on the runtime classpath at META-INF/persistence.xml.

What would you do: Place it under src/main/resources/META-INF.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

18. Persistence-unit name is wrong

What is happening: The Java name passed to createEntityManagerFactory must match the XML name.

What would you do: Make employeePU match exactly.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

19. EmployeeEntity is not detected

What is happening: The entity may not be registered in the persistence unit.

What would you do: Register its fully qualified class when explicit registration is used.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

20. Hibernate says the entity has no identifier

What is happening: The entity lacks a valid primary-key mapping.

What would you do: Add @Id to the identifier.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

21. Java field and DB column names differ

What is happening: ORM needs an explicit mapping when names do not match.

What would you do: Use @Column(name = "employee_id").

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

22. Hibernate targets the wrong table

What is happening: Entity-to-table mapping disagrees with the schema.

What would you do: Verify @Table(name = "employee").

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

23. JPQL uses SELECT * FROM employee

What is happening: JPQL is entity-oriented, unlike SQL.

What would you do: Use SELECT e FROM EmployeeEntity e.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

24. JPQL uses employee_name

What is happening: JPQL refers to entity attributes, not physical column names.

What would you do: Use e.employeeName.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

25. getResultList() returns an empty list

What is happening: The query succeeded but no matching entities were found.

What would you do: An empty list is normal and is not null.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

26. getSingleResult() finds multiple rows

What is happening: A single-result API cannot represent multiple results.

What would you do: Use getResultList or make the query uniquely constrained.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

27. DTO is passed directly to DAO

What is happening: DTO and Entity have different responsibilities.

What would you do: Map DTO → Entity before persistence.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

28. Why not expose Entity directly to UI?

What is happening: DTOs provide an API boundary and avoid leaking persistence details.

What would you do: Use request/response DTOs.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

29. Business validation is inside DAO

What is happening: DAO should focus on persistence access.

What would you do: Keep business rules in Service.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

30. Service contains all JPQL

What is happening: Business logic becomes coupled to persistence implementation.

What would you do: Keep database access in DAO.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

31. Factory creates a new DAO for every request

What is happening: A lightweight DAO can be recreated; the heavyweight EMF should not be.

What would you do: Separate DAO lifecycle from JPA infrastructure lifecycle.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

32. Two EntityManagers modify the same employee

What is happening: They have separate persistence contexts.

What would you do: Managed state is not shared automatically.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

33. A managed entity is changed twice before commit

What is happening: Dirty checking synchronizes the managed state at flush.

What would you do: Do not treat every setter as immediate SQL.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

34. persist() is called for an existing ID

What is happening: persist is intended for new entity state; an existing key can cause an INSERT constraint failure.

What would you do: Use the lifecycle operation appropriate to the state.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

35. merge() is called on a new object

What is happening: merge is not strictly update-only; it merges state into a managed instance and can result in insertion.

What would you do: Explain merge as state synchronization.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

36. merge() then the original object is modified

What is happening: The original remains detached.

What would you do: Use the managed instance returned by merge().

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

37. clear() then a managed object is modified

What is happening: clear detaches all entities from that persistence context.

What would you do: Those objects are no longer automatically tracked.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

38. Lazy data is accessed after EntityManager.close()

What is happening: Unfetched lazy state may need an open persistence context.

What would you do: Load required data inside the correct persistence boundary.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

39. A Java reference is assumed to be automatically managed

What is happening: Management belongs to the persistence context, not the Java reference.

What would you do: Know transient, managed, detached and removed states.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

40. SQL appears before commit

What is happening: Hibernate can flush before commit when synchronization is required.

What would you do: Flush and commit are related but different concepts.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

41. Database-generated state is not visible in Java

What is happening: Database state and in-memory state can differ.

What would you do: Understand refresh when current DB state is needed.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

42. External SQL changes a managed row

What is happening: The persistence context may still contain older state.

What would you do: Use refresh when appropriate.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

43. One business operation performs several DAO calls

What is happening: Separate transactions can partially commit a business operation.

What would you do: Use one appropriate transaction boundary for the use case.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

44. Delete employee and insert audit record must both succeed

What is happening: The two changes should be atomic.

What would you do: Put both in the same transaction boundary.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

45. Works locally but fails elsewhere

What is happening: Environment/configuration may differ.

What would you do: Check JDBC URL, DB, credentials, driver, port and schema.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

46. Hibernate cannot connect to MySQL

What is happening: The issue may be connectivity rather than entity mapping.

What would you do: Check MySQL status, URL, credentials, driver and port.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

47. Table or column does not exist

What is happening: Entity mapping and physical schema disagree.

What would you do: Check @Table, @Column and the actual schema.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

48. One EntityManager is created per tiny operation

What is happening: Related work can end up in different persistence contexts and transactions.

What would you do: Choose a sensible persistence-context/transaction scope.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

49. Explain UI-to-database flow

What is happening: UI calls Service; Service validates/maps; DAO uses EntityManager; Hibernate translates JPA operations into SQL; MySQL persists data.

What would you do: UI → Service → DAO → EntityManager → Hibernate → MySQL.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

50. Why use layered architecture?

What is happening: Each layer has a focused responsibility, reducing coupling and improving maintenance and testing.

What would you do: UI interaction; Service business logic; DAO persistence; Utility infrastructure.

Follow-up: Explain which entity state and persistence-context/transaction boundary are involved, then show the smallest code change that fixes the problem.

Rapid-Fire Coding Drill

// Find employee 1001.
// Increase salary if found.
// No update() call is required because the entity is managed.

em.getTransaction().begin();

EmployeeEntity employee =
        em.find(EmployeeEntity.class, 1001);

if (employee != null) {
    employee.setSalary(
        employee.getSalary() + 10000
    );
}

em.getTransaction().commit();
Concept being tested: find() → managed entity → dirty checking → flush/commit.