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:
The Most Common Cause: Missing @Transactional
The most frequent scenario is a service method that performs a write operation without the @Transactional annotation:
The fix:
Make sure you import the correct @Transactional. Spring provides two:
| Annotation | Package | Use When |
@Transactional | org.springframework.transaction.annotation | Spring applications (recommended) |
@Transactional | jakarta.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:
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:
Fixes:
- Move
@TransactionaltoprocessCheckout()so the outer method creates the transaction. - Extract
placeOrder()into a separate bean so the call goes through a proxy. - Inject
OrderServiceinto 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:
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:
The fix is to add @Transactional directly to the async method so it creates its own transaction:
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():
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:
Enable transaction debug logging to see exactly what Spring does:
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:
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.Transactionalinstead oforg.springframework.transaction.annotation.Transactional. Both compile, but the Jakarta version lacks Spring-specific features likereadOnlyandpropagation. - Putting
@Transactionalon aprivateorprotectedmethod. Spring's default proxy mode (CGLIB or JDK dynamic proxy) only interceptspublicmethods. The annotation is silently ignored on non-public methods. - Calling a
@Transactionalmethod 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
@Transactionalon a class applies to all public methods. This can create unintended write transactions on read-only methods. Prefer method-level annotations with explicitreadOnlyflags. - Not having a
PlatformTransactionManagerbean. Without a transaction manager,@Transactionalhas nothing to delegate to. Spring Boot auto-configuresJpaTransactionManager, but plain Spring requires explicit bean definition. - Using
EntityManagerin@Asyncmethods without adding@Transactionalto 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
@EnableTransactionManagementis 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
DEBUGlogging onorg.springframework.transactionto trace transaction boundaries during troubleshooting.

