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.persistence
→jakarta.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.persistence
→ jakarta.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.