Spring Framework
EntityManager
Transaction Management
Database Persistence
Error Resolution

Spring - No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The error "No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call" means you are calling EntityManager.persist() (or merge(), remove(), flush()) outside of an active JPA transaction. Spring's SharedEntityManagerCreator checks for a transactional context before allowing write operations, and throws this exception when none is found. The fix is almost always adding @Transactional to the correct method, but there are several ways to get the annotation wrong.

Why This Error Occurs

JPA requires all entity state changes to happen within a transaction. When you call persist(), the EntityManager must be bound to an active transaction so it knows which unit of work the operation belongs to. Spring provides a shared EntityManager proxy that delegates to a thread-bound instance, and that instance only exists when a transaction is active on the current thread.

The error flow:

plaintext
11. Your code calls entityManager.persist(entity)
22. Spring's EntityManager proxy checks for active transaction
33. No transaction found on current thread
44. TransactionRequiredException is thrown

The Most Common Cause: Missing @Transactional

The most frequent scenario is a service method that performs a write operation without the @Transactional annotation:

java
1// BROKEN: no transaction boundary
2@Service
3public class UserService {
4    @PersistenceContext
5    private EntityManager entityManager;
6
7    public void createUser(String name) {
8        User user = new User(name);
9        entityManager.persist(user);  // throws TransactionRequiredException
10    }
11}

The fix:

java
1// FIXED: @Transactional creates a transaction boundary
2@Service
3public class UserService {
4    @PersistenceContext
5    private EntityManager entityManager;
6
7    @Transactional
8    public void createUser(String name) {
9        User user = new User(name);
10        entityManager.persist(user);  // works
11    }
12}

Make sure you import the correct @Transactional. Spring provides two:

AnnotationPackageUse When
@Transactionalorg.springframework.transaction.annotationSpring applications (recommended)
@Transactionaljakarta.transaction (or javax.transaction)Jakarta EE / Java EE environments

Both work with Spring, but the Spring version provides more configuration options (readOnly, propagation, isolation, timeout). Do not accidentally import jakarta.transaction.Transactional when you intend Spring-specific behavior.

Other Common Causes

Missing @EnableTransactionManagement

Without this annotation on your configuration class, Spring does not create transactional proxies:

java
1@Configuration
2@EnableTransactionManagement  // required
3@EnableJpaRepositories(basePackages = "com.example.repository")
4public class JpaConfig {
5
6    @Bean
7    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
8            DataSource dataSource) {
9        LocalContainerEntityManagerFactoryBean emf =
10            new LocalContainerEntityManagerFactoryBean();
11        emf.setDataSource(dataSource);
12        emf.setPackagesToScan("com.example.model");
13        emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
14        return emf;
15    }
16
17    @Bean
18    public PlatformTransactionManager transactionManager(
19            EntityManagerFactory emf) {
20        return new JpaTransactionManager(emf);
21    }
22}

Spring Boot auto-configures this, so this issue is more common in plain Spring applications.

Self-Invocation Bypasses the Proxy

When a method within the same bean calls a @Transactional method directly, the call does not go through the Spring proxy, so no transaction is created:

java
1@Service
2public class OrderService {
3
4    @Transactional
5    public void placeOrder(Order order) {
6        entityManager.persist(order);
7    }
8
9    // This calls placeOrder without going through the proxy
10    public void processCheckout(Cart cart) {
11        Order order = cart.toOrder();
12        placeOrder(order);  // no proxy, no transaction, throws exception
13    }
14}

Fixes:

  1. Move @Transactional to processCheckout() so the outer method creates the transaction.
  2. Extract placeOrder() into a separate bean so the call goes through a proxy.
  3. Inject OrderService into itself (self-injection) to force proxy routing.

Wrong Transaction Manager

If you have multiple data sources, each with its own EntityManagerFactory, you must specify which transaction manager to use:

java
1@Transactional("secondaryTransactionManager")
2public void writeToSecondaryDb(Entity entity) {
3    secondaryEntityManager.persist(entity);
4}

Without the qualifier, Spring uses the default transaction manager, which may be bound to a different EntityManagerFactory, leaving your actual EntityManager without a transaction.

Asynchronous Execution

@Async methods run on a different thread. If the calling method's transaction does not propagate to the async thread (and it does not, by default), the async method has no transaction:

java
1@Async
2public void asyncPersist(Entity entity) {
3    entityManager.persist(entity);  // no transaction on this thread
4}

The fix is to add @Transactional directly to the async method so it creates its own transaction:

java
1@Async
2@Transactional
3public void asyncPersist(Entity entity) {
4    entityManager.persist(entity);  // new transaction on the async thread
5}

Spring Data JPA Repository Alternative

If you use Spring Data JPA repositories, transactions are handled automatically for built-in methods like save(), delete(), and saveAll():

java
public interface UserRepository extends JpaRepository<User, Long> {
}
java
1@Service
2public class UserService {
3    private final UserRepository userRepository;
4
5    public UserService(UserRepository userRepository) {
6        this.userRepository = userRepository;
7    }
8
9    public void createUser(String name) {
10        User user = new User(name);
11        userRepository.save(user);  // transaction managed by SimpleJpaRepository
12    }
13}

SimpleJpaRepository methods are annotated with @Transactional internally. However, if your service method performs multiple repository calls that should be atomic, you still need @Transactional on the service method.

Diagnostic Checklist

When you encounter this error, work through this checklist:

plaintext
11. Is @Transactional on the method or class?
2   - Check import: org.springframework.transaction.annotation.Transactional
3
42. Is @EnableTransactionManagement present?
5   - Spring Boot: auto-configured, usually not the issue
6   - Plain Spring: must be explicit
7
83. Is the method called through a Spring proxy?
9   - Internal (same-class) calls bypass the proxy
10   - Verify with debug logging: set org.springframework.transaction to DEBUG
11
124. Is the correct TransactionManager being used?
13   - Multiple data sources require explicit qualifier
14
155. Is the method running on a different thread?
16   - @Async, @Scheduled, and manual thread creation start without a transaction
17
186. Is the method public?
19   - Spring AOP proxies only intercept public methods by default

Enable transaction debug logging to see exactly what Spring does:

properties
logging.level.org.springframework.transaction=DEBUG
logging.level.org.springframework.orm.jpa=DEBUG

This produces output showing when transactions begin, commit, and roll back, making it straightforward to see whether your method is executing within a transaction boundary.

Full Working Configuration

For reference, here is a complete Spring Boot configuration that avoids this error:

java
1@SpringBootApplication
2public class Application {
3    public static void main(String[] args) {
4        SpringApplication.run(Application.class, args);
5    }
6}
java
1@Entity
2@Table(name = "users")
3public class User {
4    @Id
5    @GeneratedValue(strategy = GenerationType.IDENTITY)
6    private Long id;
7    private String name;
8
9    public User() {}
10    public User(String name) { this.name = name; }
11}
java
1@Service
2public class UserService {
3    @PersistenceContext
4    private EntityManager entityManager;
5
6    @Transactional
7    public User createUser(String name) {
8        User user = new User(name);
9        entityManager.persist(user);
10        return user;
11    }
12
13    @Transactional(readOnly = true)
14    public User findUser(Long id) {
15        return entityManager.find(User.class, id);
16    }
17}

The readOnly = true flag on the read method is a best practice: it hints to the persistence provider that no flush is needed, which can improve query performance.

Common Pitfalls

  • Importing jakarta.transaction.Transactional instead of org.springframework.transaction.annotation.Transactional. Both compile, but the Jakarta version lacks Spring-specific features like readOnly and propagation.
  • Putting @Transactional on a private or protected method. Spring's default proxy mode (CGLIB or JDK dynamic proxy) only intercepts public methods. The annotation is silently ignored on non-public methods.
  • Calling a @Transactional method from within the same class. The internal call bypasses the proxy, so no transaction context is created. Restructure the code so the call goes through a different bean.
  • Forgetting that @Transactional on a class applies to all public methods. This can create unintended write transactions on read-only methods. Prefer method-level annotations with explicit readOnly flags.
  • Not having a PlatformTransactionManager bean. Without a transaction manager, @Transactional has nothing to delegate to. Spring Boot auto-configures JpaTransactionManager, but plain Spring requires explicit bean definition.
  • Using EntityManager in @Async methods without adding @Transactional to the async method itself. Transaction context does not propagate across threads.

Summary

  • This error means persist() or another write operation was called without an active JPA transaction on the current thread.
  • The most common fix is adding @Transactional (from Spring's package) to the service method.
  • Verify that @EnableTransactionManagement is present, the method is public, and the call goes through a Spring proxy.
  • Self-invocation within the same bean bypasses the proxy and skips transaction creation.
  • For multiple data sources, specify the transaction manager explicitly.
  • Enable DEBUG logging on org.springframework.transaction to trace transaction boundaries during troubleshooting.

Course illustration
Course illustration

All Rights Reserved.