Reputation: 3599
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";
}
}
}
Upvotes: 314
Views: 436424
Reputation: 33983
If you're migrating from Groovy to Java and forgot to make the method public
, that will also do it. (It's mentioned here that the annotation only works on public methods but that's not the default in Java like it is in Groovy.)
Upvotes: 0
Reputation: 31
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
}
}
Upvotes: 1
Reputation: 71
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.
Upvotes: 0
Reputation: 540
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.
Upvotes: 2
Reputation: 38177
To fix this in a test, you can use @DataJpaTest
or @AutoConfigureTestDatabase
.
Upvotes: 0
Reputation: 95
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.
Upvotes: 0
Reputation: 871
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.
Upvotes: 4
Reputation: 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.
Upvotes: -1
Reputation: 76
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);
Upvotes: 3
Reputation: 61
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.
Upvotes: 0
Reputation: 35
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.
Upvotes: 0
Reputation: 617
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.
Upvotes: 3
Reputation: 13519
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.
Upvotes: 4
Reputation: 333
Adding the org.springframework.transaction.annotation.Transactional
annotation at the class level for the test class fixed the issue for me.
Upvotes: 21
Reputation: 514
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")
Upvotes: 1
Reputation: 79
I had the same problem and I added tx:annotation-driven
in applicationContext.xml
and it worked.
Upvotes: 8
Reputation: 1365
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
Upvotes: 17
Reputation: 561
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.
Upvotes: 22
Reputation: 63
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.
Upvotes: 0
Reputation:
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.
Upvotes: 3
Reputation: 111
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
}
}
Upvotes: 9
Reputation: 5756
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.
Upvotes: 532
Reputation: 2101
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.
Upvotes: 147
Reputation: 31
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="...." />
Upvotes: 2
Reputation: 1496
I removed the mode from
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
to make this work
Upvotes: 1
Reputation: 750
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
Upvotes: 35