Spring Boot
Spring Data
Entity Manager
Java Development
Persistence API

How to access entity manager with spring boot and spring data

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Spring Data JPA lets you stay productive through repository interfaces, derived queries, and paging support. Even so, some cases still need direct access to EntityManager, especially when you need custom JPQL, batch updates, Criteria API queries, or explicit control over flushing and persistence context behavior.

Core Sections

The normal way to obtain EntityManager

In a Spring Boot application, you usually inject EntityManager into a Spring-managed bean with @PersistenceContext. That keeps the persistence context tied to the current transaction and lets Spring handle setup for you.

java
1package com.example.bookstore.persistence;
2
3import jakarta.persistence.EntityManager;
4import jakarta.persistence.PersistenceContext;
5import org.springframework.stereotype.Repository;
6
7@Repository
8public class BookQueryRepository {
9
10    @PersistenceContext
11    private EntityManager entityManager;
12
13    public Book findByIsbn(String isbn) {
14        return entityManager.createQuery(
15                "select b from Book b where b.isbn = :isbn", Book.class)
16            .setParameter("isbn", isbn)
17            .getSingleResult();
18    }
19}

This is the idiomatic approach. Avoid constructing EntityManager yourself in application code. In a Spring app, manual creation almost always means you are stepping outside the transaction model that the framework is already maintaining.

Why repositories are not always enough

A plain JpaRepository handles most CRUD work well, but direct EntityManager access becomes useful when the query logic is too dynamic or too specialized for a derived method name. Typical examples include:

  • dynamic filtering with Criteria API
  • optimized fetch joins to avoid repeated lazy loading
  • bulk update or delete queries
  • explicit flush or clear calls during batch processing
  • native queries for database-specific features

Spring Data removes boilerplate, but it does not replace JPA itself. When repository methods become awkward, moving the advanced part into a custom component keeps the codebase clearer.

Using a custom repository implementation

The common pattern is to extend the Spring Data repository with a custom interface and then implement that custom part with EntityManager.

java
1package com.example.bookstore.persistence;
2
3import java.util.List;
4
5public interface BookRepositoryCustom {
6    List<Book> findRecentlyPublished(int limit);
7}
java
1package com.example.bookstore.persistence;
2
3import org.springframework.data.jpa.repository.JpaRepository;
4
5public interface BookRepository extends JpaRepository<Book, Long>, BookRepositoryCustom {
6}
java
1package com.example.bookstore.persistence;
2
3import jakarta.persistence.EntityManager;
4import jakarta.persistence.PersistenceContext;
5import java.util.List;
6
7public class BookRepositoryImpl implements BookRepositoryCustom {
8
9    @PersistenceContext
10    private EntityManager entityManager;
11
12    @Override
13    public List<Book> findRecentlyPublished(int limit) {
14        return entityManager.createQuery(
15                "select b from Book b order by b.publishedAt desc", Book.class)
16            .setMaxResults(limit)
17            .getResultList();
18    }
19}

This keeps the standard repository interface available for normal CRUD while isolating the lower-level JPA work to a place where that complexity is expected.

Transactions decide how the persistence context behaves

Reading with EntityManager may work outside an explicit transaction in some configurations, but any write-oriented logic should be executed inside a transactional boundary. Without that, changes may not be flushed when you expect, and lazy associations may fail later.

java
1package com.example.bookstore.service;
2
3import com.example.bookstore.persistence.Book;
4import com.example.bookstore.persistence.BookRepository;
5import org.springframework.stereotype.Service;
6import org.springframework.transaction.annotation.Transactional;
7
8@Service
9public class BookService {
10
11    private final BookRepository bookRepository;
12
13    public BookService(BookRepository bookRepository) {
14        this.bookRepository = bookRepository;
15    }
16
17    @Transactional
18    public void renameBook(Long id, String newTitle) {
19        Book book = bookRepository.findById(id)
20            .orElseThrow(() -> new IllegalArgumentException("Book not found"));
21
22        book.setTitle(newTitle);
23    }
24}

Inside a transaction, the managed entity is tracked automatically. You usually do not need to call save after mutating it, because JPA dirty checking will pick up the change before commit.

@PersistenceContext versus constructor injection

You will also see EntityManager injected through a constructor. That can work in Spring because the framework provides a proxy, but @PersistenceContext still communicates intent more clearly in JPA-heavy code.

java
1@Repository
2public class AuditRepository {
3    private final EntityManager entityManager;
4
5    public AuditRepository(EntityManager entityManager) {
6        this.entityManager = entityManager;
7    }
8}

Either style is acceptable in Spring Boot. The main rule is simpler: inject it only into Spring-managed beans such as @Repository, @Service, or @Component classes.

Common Pitfalls

  • Injecting EntityManager into a class created with new bypasses Spring and leaves the field unset.
  • Putting advanced JPQL or Criteria code directly inside controllers makes persistence logic harder to test and maintain.
  • Running write operations without @Transactional can produce missing updates, lazy-loading failures, or confusing flush behavior.
  • Using bulk update queries without clearing or refreshing the persistence context can leave managed entities out of sync with the database.
  • Replacing all repository methods with direct EntityManager usage adds boilerplate without improving the design.

Summary

  • Access EntityManager in Spring Boot through dependency injection, usually with @PersistenceContext.
  • Keep normal CRUD in Spring Data repositories and reserve EntityManager for advanced JPA cases.
  • Custom repository implementations are the cleanest place for manual JPQL or Criteria logic.
  • Transaction boundaries matter because they define how the persistence context tracks entities.
  • Use direct EntityManager access deliberately, not as a default replacement for repositories.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.