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

26

294

I get this error when trying to invoke "persist" method to save entity model to database in my Spring MVC web application. Can't really find any post or page in internet that can relate to this particular error. It seems like something's wrong with EntityManagerFactory bean but i'm fairly new to Spring programming so for me it seems like everything is initialized fine and according to various tutorial articles in web.

dispatcher-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="
 http://www.springframework.org/schema/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-4.0.xsd
  http://www.springframework.org/schema/jdbc
  http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
  http://www.springframework.org/schema/data/jpa
  http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
  http://www.springframework.org/schema/data/repository
  http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd
  http://www.springframework.org/schema/jee
  http://www.springframework.org/schema/jee/spring-jee-3.2.xsd">

    <context:component-scan base-package="wymysl.Controllers" />
    <jpa:repositories base-package="wymysl.repositories"/>
    <context:component-scan base-package="wymysl.beans" />
    <context:component-scan base-package="wymysl.Validators" />
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    <bean class="org.springframework.orm.hibernate4.HibernateExceptionTranslator"/>

    <bean id="passwordValidator" class="wymysl.Validators.PasswordValidator"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
        <property name="username" value="system" />
        <property name="password" value="polskabieda1" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml" />
        <property name="dataSource" ref="dataSource" />

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" />
                <property name="showSql" value="true" />
                <property name="generateDdl" value="false" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.jdbc.fetch_size">50</prop>
                <prop key="hibernate.jdbc.batch_size">10</prop>
            </props>
        </property>
    </bean>

    <mvc:annotation-driven />

    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages" />
    </bean>

    <bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/jsp/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:resources mapping="/resources/*" location="/resources/css/" cache-period="31556926"/>
</beans>

RegisterController.java

@Controller
public class RegisterController {

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    PasswordValidator passwordValidator;

    @InitBinder
    private void initBinder(WebDataBinder binder) {
        binder.setValidator(passwordValidator);
    }

    @RequestMapping(value = "/addUser", method = RequestMethod.GET)
    public String register(Person person) {
        return "register";
    }

    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public String register(@ModelAttribute("person") @Valid @Validated Person person, BindingResult result) {
        if (result.hasErrors()) {
            return "register";
        } else {
            entityManager.persist(person);
            return "index";
        }
    }
}
Mucor answered 28/8, 2015 at 10:44 Comment(2)
As the error says, there is no transaction. Annotate the register method with @Transaction.Gerous
@Gerous very informingAcademia
N
510

I had the same problem and I annotated the method as @Transactional and it worked.

UPDATE: checking the spring documentation it looks like by default the PersistenceContext is of type Transaction, so that's why the method has to be transactional (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):

The @PersistenceContext annotation has an optional attribute type, which defaults to PersistenceContextType.TRANSACTION. This default is what you need to receive a shared EntityManager proxy. The alternative, PersistenceContextType.EXTENDED, is a completely different affair: This results in a so-called extended EntityManager, which is not thread-safe and hence must not be used in a concurrently accessed component such as a Spring-managed singleton bean. Extended EntityManagers are only supposed to be used in stateful components that, for example, reside in a session, with the lifecycle of the EntityManager not tied to a current transaction but rather being completely up to the application.

Naval answered 13/9, 2015 at 17:33 Comment(12)
If a method without the @Transactional annotion calls a method with the @Transactional annotation in the same classfile, you also will be struck by this error (that's what I was facing).Carmon
I annotated the service class with @Transactional, that also works. Not sure if it's the right way to go but it seems like it's working fine...Unific
Another thing worth mentioning is that this annotation should be used for public method as it doesn't work otherwise.Genera
But why do I need to annotate the method as @Transactional, what will change?Decalcomania
@NishantTanwar you can read all data, i.e. data won't change while reading, but if you are doing something that changes data, you have to make sure the query/process you are invoking is the only thing that is changing data to maintain the ACID property of database.Divestiture
Remember, please, that @Transactional works on public methods only.Snakebite
I had a strange issue, that the annotation of the method didn't work, but the annotation of the service solved the issue.Cythera
@Lokesh: Annotate calling method with @Transactional as well. Or make sure you call from a different class (or break up the service in smaller services, so you do call the method at a different class).Carmon
FYI this needs the javax.transaction.Transactional annotation (not the Spring one).Separation
I am facing this issue with a method (inside a service) that called both the Delete methods from the repository and a couple of Save methods. The saves worked fine and this error showed up only when calling delete. Why is that? The solution does not work and the answer does not contemplate the reasons as to why Repository delete methods are treated differently from the rest.Demission
Alternatively, programmatically, use TransactionTemplateDanner
incase your wondering which transactional. org.springframework.transaction.annotation.TransactionalMatriarch
D
143

I got this exception while attempting to use a deleteBy custom method in the spring data repository. The operation was attempted from a JUnit test class.

The exception does not occur upon using the @Transactional annotation at the JUnit class level.

Differentiation answered 27/5, 2016 at 20:37 Comment(5)
I was in the same situation, instead of annotating the test-class I annotated the service method so it trickled down even if it wasn't a test.Absurdity
@Transactional on a class-level can mask possible test problems that manipulate different transactions in your services.Thong
I used @Trasactional on the repository method since its the place where I am actually interacting with the database and its working fine.Donohoe
I had this issue when trying to 'deleteBy'. I changed the method to 'delete' and passed the whole object as argument.Gee
faced this issue while foolowing chad derby's tutorial . He taught that by default jpa has @transactional at service level so we didn't need to add it here. But in case of deletion we actually need to while using entityManager.remove(object)Bibliotheca
M
35

This error had me foxed for three days, the situation I faced produced the same error. Following all the advice I could find, I played with the configuration but to no avail.

Eventually I found it, the difference, the Service I was executing was contained in a common jar, the issue turned out to be AspectJ not treating the Service instantiation the same. In effect the proxy was simply calling the underlying method without all the normal Spring magic being executed before the method call.

In the end the @Scope annotation placed on the service as per the example solved the issue:

@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Transactional
public class CoreServiceImpl implements CoreService {
    @PersistenceContext
    protected EntityManager entityManager;

    @Override
    public final <T extends AbstractEntity> int deleteAll(Class<T> clazz) {
        CriteriaDelete<T> criteriaDelete = entityManager.getCriteriaBuilder().createCriteriaDelete(clazz);
        criteriaDelete.from(clazz);
        return entityManager.createQuery(criteriaDelete).executeUpdate();
    }

}

The method I have posted is a delete method but the annotations affect all persistence methods in the same way.

I hope this post helps someone else who has struggled with the same issue when loading a service from a jar

Masker answered 7/2, 2016 at 7:59 Comment(2)
Adding @Scope(proxyMode = ScopedProxyMode.INTERFACES) to the DAO class which implements an interface is really important. I spent the whole day to figure out this error and your solution is the only one works. Thank you very much!Lucillelucina
One L’annotation from your answer has just saved me probably around 3 days. I have just experienced the real power of edenema. Дякс.Musgrove
G
22

boardRepo.deleteByBoardId(id);

Faced the same issue. GOT javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread

I resolved it by adding @Transactional annotation above the controller/service.

Glyptics answered 30/11, 2018 at 16:35 Comment(0)
A
22

You need to add @Transactional to your methode

Aaren answered 6/10, 2021 at 16:31 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Pia
Z
20

Adding the org.springframework.transaction.annotation.Transactional annotation at the class level for the test class fixed the issue for me.

Zechariah answered 14/11, 2019 at 16:49 Comment(1)
Be aware that this will open a read-write transaction for every method call from outside the class. This might not be what you want, especially in high load situations. You might want to think about adding the annotation to the problematic method instead of the class.Anaheim
T
17

I had the same error because I switched from XML- to java-configuration.

The point was, I didn't migrate <tx:annotation-driven/> tag, as Stone Feng suggested.

So I just added @EnableTransactionManagement as suggested here Setting Up Annotation Driven Transactions in Spring in @Configuration Class, and it works now

Trass answered 12/10, 2016 at 13:41 Comment(0)
C
8

I had the same problem and I added tx:annotation-driven in applicationContext.xml and it worked.

Catsup answered 13/8, 2016 at 8:44 Comment(0)
W
8

I had the same error when accessing an already transactional-annotated method from a non-transactional method within the same component:

Before:
    @Component
    public class MarketObserver {
        @PersistenceContext(unitName = "maindb")
        private EntityManager em;

        @Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
        public void executeQuery() {
          em.persist(....);
        }


        @Async
        public void startObserving() {
          executeQuery(); //<-- Wrong
        }
    }

    //In another bean:
     marketObserver.startObserving();

I fixed the error by calling the executeQuery() on the self-referenced component:

Fixed version:
    @Component
    public class MarketObserver {
        @PersistenceContext(unitName = "maindb")
        private EntityManager em;

        @Autowired
        private GenericApplicationContext context;

        @Transactional(value = "txMain", propagation = Propagation.REQUIRES_NEW)
        public void executeQuery() {
          em.persist(....);
        }


        @Async
        public void startObserving() {
          context.getBean(MarketObserver.class).executeQuery(); //<-- Works
        }
    }
Waver answered 20/12, 2017 at 18:40 Comment(3)
really works. Seems that invocation of the Transactional method from another thread requires context.getBean(....).Pettish
For the: Calling method AA that calls method BB with @Transactional, AA have to have @Transactional too... \ Idk why we need that, cuz I really dont need that in AA (especially when multi TransactionManager involved). \ Idk what mechanism Spring uses in @Transaction makes this answer work. \ (& Idk why in some other cases @Transaction in AA may not needed.)Hamilton
Also, for a similar mechanism, I could got away with that Error by the following:: \ If I Separate & Externalized the methods: \ method AA with no @Transactional stays in one class UU & \ method BB with @Transactional is put into another @Component class VV \ UU have VV by @Autowired & UU.AA invoke VV.BB -> then it works.Hamilton
U
4

Just a note for other users searching for answers for thie error. Another common issue is:

You generally cannot call an @transactional method from within the same class.

(There are ways and means using AspectJ but refactoring will be way easier)

So you'll need a calling class and class that holds the @transactional methods.

Univalent answered 4/2, 2020 at 18:11 Comment(0)
E
4

I already had the @Transactional but still wasn't working. Turns out I had to get rid of parallelism to make it work.

If you are doing things in parallel, DON'T.

Etherize answered 23/6, 2021 at 14:4 Comment(0)
T
3

If you have

@Transactional // Spring Transactional
class MyDao extends Dao {
}

and super-class

class Dao {
    public void save(Entity entity) { getEntityManager().merge(entity); }
}

and you call

@Autowired MyDao myDao;
myDao.save(entity);

you won't get a Spring TransactionInterceptor (that gives you a transaction).

This is what you need to do:

@Transactional 
class MyDao extends Dao {
    public void save(Entity entity) { super.save(entity); }
}

Unbelievable but true.

Tefillin answered 12/5, 2020 at 13:43 Comment(1)
Same for opposite - if you override a @Transactional method without @Transactional, even if you call super, it will fail (seems logical in this case, though)Kawai
U
3

Without @Transactional annotation you can achieve the same goal with finding the entity from the DB and then removing that entity you got from the DB.

CrudRepositor -> void delete(T var1);

Unattended answered 29/1, 2021 at 9:48 Comment(0)
S
2

For us, the problem came down to same context settings in multiple configuration files. Check you've not duplicated the following in multiple config files.

<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />
Sharecrop answered 19/4, 2017 at 12:41 Comment(0)
G
2

I had the same error code when I used @Transaction on a wrong method/actionlevel.

methodWithANumberOfDatabaseActions() { 
   methodA( ...)
   methodA( ...)
}

@Transactional
void methodA( ...) {
  ... ERROR message
}

I had to place the @Transactional just above the method methodWithANumberOfDatabaseActions(), of course.

That solved the error message in my case.

Glynisglynn answered 30/7, 2018 at 12:5 Comment(0)
M
1

I removed the mode from

<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />

to make this work

Mikaelamikal answered 22/6, 2016 at 12:38 Comment(0)
F
1

This helped us, maybe it can help others in the future. @Transaction was not working for us, but this did:

@ConditionalOnMissingClass("org.springframework.orm.jpa.JpaTransactionManager")

Flyer answered 10/9, 2019 at 13:45 Comment(0)
C
1

Before processing entity just trigger detach on the entity then do the operation on that entity like below

@Component
@RepositoryEventHandler
@AllArgsConstructor
public class SpringDataRestConfig {

    EntityManager entityManager;

    @HandleBeforeSave  // update or Patch
    @HandleBeforeCreate //Post
    public void handlePersonSave(Employee employee) {
        entityManager.detach(employee);
        // do the operation on the entity
    }
}
Cullie answered 11/3, 2024 at 22:46 Comment(0)
A
0

I had this issue for days and nothing I found anywhere online helped me, I'm posting my answer here in case it helps anyone else.

In my case, I was working on a microservice being called through remoting, and my @Transactional annotation at the service level was not being picked up by the remote proxy.

Adding a delegate class between the service and dao layers and marking the delegate method as transactional fixed this for me.

Appellation answered 30/10, 2018 at 15:36 Comment(0)
M
0

I got the same error when I executed the Spring JPA deleteAll() method from Junit test cases. I simply used the deleteInBatch() & deleteAllInBatch() and its perfectly works. We do not need to mark @Transactional at the test cases level.

Malathion answered 9/12, 2020 at 5:12 Comment(0)
Z
0

For anyone with the same issue as I had, I was calling a public method method1 from within another class. method1 then called another public method method2 within the same class. method2 was annotated with @Transactional, but method1 was not. All that method1 did was transform some arguments and directly call method2, so no DB operations here.

The issue got solved for me once I moved the @Transactional annotation to method1.

Not sure the reason for this, but this did it for me.

Zamboanga answered 27/1, 2021 at 23:8 Comment(0)
B
0

To fix this in a test, you can use @DataJpaTest or @AutoConfigureTestDatabase.

Berman answered 17/5, 2021 at 15:39 Comment(0)
R
0

It's like you are using the shared EntityManager when you are getting it Autowired so for persisting spring tells that this EntityManager bean is a shared bean and for persisting it needs a hold of this bean till the data persist doesn't get completed so for that we have to use @Transactional so that it gonna start and commit the persistence in a transaction so the data or operation gets completely saved or get rollback completely.

Rem answered 27/7, 2021 at 10:11 Comment(0)
T
0

I got a similar error but with a 'remove' call since it dealt with a deleteByEntityId() method in the Spring Data repository.

The error was resolved by explicitly annotating the deleteByEntityId() method in the CustomEntityRepository with @Transactional. It would instruct Spring to wrap the delete method within a transactional context when invoked.

Tinstone answered 18/12, 2023 at 20:47 Comment(0)
G
0

Adding the jakarta.transaction.Transactional(or javax.transaction.Transactional for earlier java versions) on the service class method alone would solve the issue as the all the processing done inside said method would be within the transaction.

Gautious answered 13/2, 2024 at 16:0 Comment(0)
S
-1

Calling the repository method was being called within a class with @Component, taking that method out of that class and placing it inside another with @Service worked.

Skitter answered 17/4, 2021 at 14:25 Comment(1)
Please read How to answer and update your answer.Fayfayal

© 2022 - 2025 — McMap. All rights reserved.