273

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>

     <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";

        }




    }
Michał Bil
  • 3,179
  • 5
  • 20
  • 24

23 Answers23

481

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.

mlg
  • 5,248
  • 1
  • 14
  • 17
  • 126
    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). – Jacob van Lingen Jul 20 '16 at 13:38
  • 12
    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... – milosmns Mar 05 '17 at 16:42
  • 18
    Another thing worth mentioning is that this annotation should be used for public method as it doesn't work otherwise. – Yuriy Kravets Mar 06 '17 at 12:06
  • But why do I need to annotate the method as @Transactional, what will change? – Nishant Tanwar May 22 '17 at 18:41
  • @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. – erluxman Jun 13 '17 at 02:19
  • 25
    Remember, please, that @Transactional works on public methods only. – Andrei_N Aug 31 '17 at 10:50
  • I had a strange issue, that the annotation of the method didn't work, but the annotation of the service solved the issue. – Aceonline Aug 08 '18 at 07:28
  • 1
    @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). – Jacob van Lingen Jan 30 '19 at 11:10
  • 24
    FYI this needs the javax.transaction.Transactional annotation (not the Spring one). – java-addict301 May 15 '19 at 22:20
  • 1
    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. – epol Jul 19 '19 at 15:05
  • Alternatively, programmatically, use [TransactionTemplate](https://stackoverflow.com/a/46415060/3779853) – phil294 Feb 18 '20 at 14:25
  • incase your wondering which transactional. `org.springframework.transaction.annotation.Transactional` – kamasuPaul Jun 23 '23 at 15:38
  • worked like charm – Joan Aug 03 '23 at 10:22
132

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.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
Kishore Guruswamy
  • 1,901
  • 1
  • 12
  • 5
  • 12
    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. – Niko May 30 '17 at 19:41
  • 1
    `@Transactional` on a class-level can mask possible test problems that manipulate different transactions in your services. – Zon Apr 30 '20 at 10:55
  • 10
    I used `@Trasactional` on the repository method since its the place where I am actually interacting with the database and its working fine. – Harish Kumar Saini Jun 05 '20 at 10:57
  • 1
    I had this issue when trying to 'deleteBy'. I changed the method to 'delete' and passed the whole object as argument. – Tatiana B Jan 06 '21 at 14:08
  • 1
    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) – vibhor vaish Mar 12 '21 at 13:47
34

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

Chris March
  • 720
  • 7
  • 7
  • 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! – Thach Van May 17 '16 at 06:16
  • 1
    One L’annotation from your answer has just saved me probably around 3 days. I have just experienced the real power of edenema. Дякс. – Oleksii Kyslytsyn May 09 '19 at 11:16
21

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.

Vikram S
  • 551
  • 5
  • 5
19

You need to add @Transactional to your methode

Akhachkhouch
  • 198
  • 1
  • 4
  • 5
    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](/help/how-to-answer). – Tyler2P Oct 06 '21 at 17:35
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

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Sergej Werfel
  • 1,335
  • 2
  • 14
  • 25
16

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

Leo
  • 283
  • 4
  • 10
  • 2
    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. – Markus Rohlof Feb 11 '22 at 08:56
7

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

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Stone Feng
  • 69
  • 1
  • 3
7

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
        }
    }
YDZOGODOQ
  • 71
  • 1
  • 4
  • really works. Seems that invocation of the Transactional method from another thread requires context.getBean(....). – Igor Delac Mar 19 '23 at 11:04
  • 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.) – Nor.Z May 03 '23 at 13:10
  • 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. – Nor.Z May 03 '23 at 13:10
3

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.

sparkyspider
  • 13,195
  • 10
  • 89
  • 133
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.

user2006754
  • 607
  • 6
  • 9
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);

Samin
  • 76
  • 2
3

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.

Jalil.Jarjanazy
  • 839
  • 1
  • 9
  • 22
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="...." />
Draken
  • 3,134
  • 13
  • 34
  • 54
Nick West
  • 31
  • 4
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.

anothernode
  • 5,100
  • 13
  • 43
  • 62
1

I removed the mode from

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

to make this work

ropo
  • 1,466
  • 2
  • 18
  • 29
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.

fleeblewidget
  • 63
  • 1
  • 7
0

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")

Andrew Regan
  • 5,087
  • 6
  • 37
  • 73
JLane
  • 504
  • 2
  • 11
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.

swapnil
  • 35
  • 7
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.

David Arteaga
  • 31
  • 1
  • 3
0

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

Apostolos
  • 10,033
  • 5
  • 24
  • 39
serv-inc
  • 35,772
  • 9
  • 166
  • 188
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.

-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.

  • 1
    Please read [How to answer](https://stackoverflow.com/help/how-to-answer) and update your answer. – fartem Apr 18 '21 at 07:57