JPA EntityManager and EntityManagerFactory Explained with Examples

Illustration for JPA EntityManager and EntityManagerFactory Explained with Examples
By Last updated:

When working with JPA (Java Persistence API), two core components define the way applications interact with databases: EntityManagerFactory and EntityManager. These are the backbone of JPA’s ORM mechanism, handling entity lifecycle management, queries, and persistence context.

In this tutorial, we’ll explore EntityManager and EntityManagerFactory in depth — their architecture, purpose, configurations, CRUD operations, performance considerations, and best practices.


What is EntityManagerFactory?

  • EntityManagerFactory is a heavyweight object responsible for creating and managing instances of EntityManager.
  • It is thread-safe and should be created once per application.
  • Typically initialized from the persistence.xml configuration.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("examplePU");

Analogy: Think of EntityManagerFactory as a power station — it generates electricity (EntityManagers) when required.


What is EntityManager?

  • EntityManager is a lightweight object used for interacting with the persistence context and performing CRUD operations.
  • It is not thread-safe and should be used per transaction/request.
EntityManager em = emf.createEntityManager();

Analogy: EntityManager is like a classroom teacher tracking students (entities) in an attendance register (persistence context).


Relationship Between EntityManagerFactory and EntityManager

  • The EntityManagerFactory creates EntityManager instances.
  • Each EntityManager maintains its own Persistence Context.
  • Multiple EntityManagers can exist simultaneously, each working independently.

Setting Up EntityManager with persistence.xml

<persistence xmlns="https://jakarta.ee/xml/ns/persistence" version="3.0">
  <persistence-unit name="examplePU" transaction-type="RESOURCE_LOCAL">
    <class>com.example.model.User</class>
    <properties>
      <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
      <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:testdb"/>
      <property name="jakarta.persistence.jdbc.user" value="sa"/>
      <property name="jakarta.persistence.jdbc.password" value=""/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

Example Entity

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Column(unique = true, nullable = false)
    private String email;

    // Getters & Setters
}

CRUD with EntityManager

EntityManagerFactory emf = Persistence.createEntityManagerFactory("examplePU");
EntityManager em = emf.createEntityManager();

// Create
em.getTransaction().begin();
User user = new User();
user.setName("Alice");
user.setEmail("alice@example.com");
em.persist(user);
em.getTransaction().commit();

// Read
User found = em.find(User.class, user.getId());

// Update
em.getTransaction().begin();
found.setName("Alice Updated");
em.merge(found);
em.getTransaction().commit();

// Delete
em.getTransaction().begin();
em.remove(found);
em.getTransaction().commit();

em.close();
emf.close();

Persistence Context in Action

  • The persistence context ensures entities are automatically tracked.
  • Once an entity is managed, changes are auto-synced with the database.
em.getTransaction().begin();
User user = em.find(User.class, 1L); // Managed
user.setName("Updated Name");        // Change auto-detected
em.getTransaction().commit();

Querying with EntityManager

JPQL

List<User> users = em.createQuery("SELECT u FROM User u WHERE u.name = :name", User.class)
                     .setParameter("name", "Alice")
                     .getResultList();

Criteria API

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.select(root).where(cb.equal(root.get("email"), "alice@example.com"));
List<User> results = em.createQuery(query).getResultList();

Native SQL

List<Object[]> results = em.createNativeQuery("SELECT * FROM users").getResultList();

Fetching Strategies

  • Lazy Fetching (default): Loads related entities only when accessed.
  • Eager Fetching: Loads related entities immediately.
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;

Analogy: Lazy loading = ordering food only when hungry; eager loading = ordering the full buffet upfront.


Real-World Integration

Spring Boot

Spring Boot automatically manages EntityManagerFactory and EntityManager.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByEmail(String email);
}

Jakarta EE

@Stateless
public class UserService {
    @PersistenceContext
    private EntityManager em;

    public void saveUser(User user) {
        em.persist(user);
    }
}

Common Pitfalls

  • Closing EntityManager too early → Detached entities.
  • Using EntityManager across multiple threads → Not thread-safe.
  • Overusing eager fetching → Performance bottlenecks.
  • Ignoring transaction boundaries → Data inconsistency.

Best Practices

  • Use one EntityManagerFactory per application.
  • Create EntityManager per request/transaction.
  • Default to lazy fetching.
  • Profile queries with SQL logs for optimization.

📌 JPA Version Notes

  • JPA 2.0 → Added Criteria API, Metamodel.
  • JPA 2.1 → Added stored procedures, entity graphs.
  • Jakarta Persistence (EE 9/10/11) → Migrated from javax.persistencejakarta.persistence.

Conclusion and Key Takeaways

  • EntityManagerFactory is the heavyweight factory, created once per application.
  • EntityManager is lightweight, created per transaction/request.
  • Persistence Context ensures entities are tracked and synchronized.
  • Use lazy fetching, careful transaction management, and best practices for production.

FAQ

1. What’s the difference between JPA and Hibernate?
JPA is a specification; Hibernate is an implementation.

2. How does JPA handle the persistence context?
It tracks entities like a register, syncing them with the database.

3. What are drawbacks of eager fetching?
Loads unnecessary data, causing memory issues and performance degradation.

4. How can I solve the N+1 select problem?
Use JOIN FETCH, entity graphs, or batch fetching strategies.

5. Can I use JPA without Hibernate?
Yes, with providers like EclipseLink or OpenJPA.

6. What’s the best inheritance mapping strategy in JPA?
SINGLE_TABLE is fastest; JOINED is normalized; TABLE_PER_CLASS is rare.

7. How does JPA handle composite keys?
Using @EmbeddedId or @IdClass.

8. What changes with Jakarta Persistence?
Namespace changed from javax.persistencejakarta.persistence.

9. Is JPA suitable for microservices?
Yes, but transactions should be short-lived with DTOs for efficiency.

10. When should I avoid JPA?
For heavy analytics or NoSQL databases where ORM doesn’t fit.