Java Spring - 没有可用于当前线程的实际事务的 EntityManager - 无法可靠地处理“持久”调用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32269192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring - No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
提问by Micha? Bil
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.
在 Spring MVC Web 应用程序中尝试调用“persist”方法将实体模型保存到数据库时出现此错误。无法在互联网上真正找到与此特定错误相关的任何帖子或页面。EntityManagerFactory bean 似乎有问题,但我对 Spring 编程还很陌生,所以对我来说似乎一切都初始化得很好,并且根据 web.xml 中的各种教程文章。
dispatcher-servlet.xml
调度程序-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
注册控制器.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";
}
}
采纳答案by mlg
I had the same problem and I annotated the method as @Transactional
and it worked.
我遇到了同样的问题,我将该方法注释为@Transactional
有效。
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):
更新:检查 spring 文档,它看起来默认情况下 PersistenceContext 是事务类型,所以这就是为什么该方法必须是事务性的(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.
@PersistenceContext 注解有一个可选的属性类型,默认为 PersistenceContextType.TRANSACTION。此默认值是您接收共享 EntityManager 代理所需的。替代方案 PersistenceContextType.EXTENDED 是完全不同的事情:这会导致所谓的扩展 EntityManager,它不是线程安全的,因此不能在并发访问的组件中使用,例如 Spring 管理的单例 bean。扩展的 EntityManager 只应该用于有状态的组件,例如,驻留在会话中,EntityManager 的生命周期不与当前事务绑定,而是完全取决于应用程序。
回答by Chris March
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.
最终我找到了它,不同之处在于,我正在执行的服务包含在一个普通的 jar 中,问题原来是 AspectJ 没有将服务实例化视为相同。实际上,代理只是调用底层方法,而没有在方法调用之前执行所有正常的 Spring 魔法。
In the end the @Scope annotation placed on the service as per the example solved the issue:
最后,根据示例放置在服务上的@Scope 注释解决了该问题:
@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
我希望这篇文章可以帮助其他人在从 jar 加载服务时遇到同样的问题
回答by Kishore Guruswamy
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.
尝试在 Spring 数据存储库中使用 deleteBy 自定义方法时遇到此异常。该操作是从 JUnit 测试类尝试的。
The exception does not occur upon using the @Transactional
annotation at the JUnit class level.
@Transactional
在 JUnit 类级别使用注释时不会发生异常。
回答by ropo
I removed the mode from
我从
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
to make this work
使这项工作
回答by Stone Feng
I had the same problem and I added tx:annotation-driven
in applicationContext.xml
and it worked.
我遇到了同样的问题,我添加tx:annotation-driven
了applicationContext.xml
它并且它起作用了。
回答by Sergej Werfel
I had the same error because I switched from XML- to java-configuration.
我有同样的错误,因为我从 XML- 切换到 java-configuration。
The point was, I didn't migrate <tx:annotation-driven/>
tag, as Stone Feng suggested.
关键是,我没有<tx:annotation-driven/>
像 Stone Feng 建议的那样迁移标签。
So I just added @EnableTransactionManagement
as suggested here
Setting Up Annotation Driven Transactions in Spring in @Configuration Class, and it works now
所以我只是@EnableTransactionManagement
按照这里的建议
在 @Configuration Class 中添加了在 Spring 中设置注释驱动的事务,现在可以使用了
回答by Nick West
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="...." />
回答by YDZOGODOQ
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:
我通过调用自引用组件上的 executeQuery() 修复了错误:
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
}
}
回答by YDZOGODOQ
I had the same error code when I used @Transaction
on a wrong method/actionlevel.
当我@Transaction
在错误的方法/操作级别上使用时,我有相同的错误代码。
methodWithANumberOfDatabaseActions() {
methodA( ...)
methodA( ...)
}
@Transactional
void methodA( ...) {
... ERROR message
}
I had to place the @Transactional
just above the method methodWithANumberOfDatabaseActions()
, of course.
当然,我必须将 放在@Transactional
方法的正上方methodWithANumberOfDatabaseActions()
。
That solved the error message in my case.
这解决了我的情况下的错误消息。
回答by fleeblewidget
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.
就我而言,我正在处理通过远程调用的微服务,而我在服务级别的 @Transactional 注释没有被远程代理接收。
Adding a delegate class between the service and dao layers and marking the delegate method as transactional fixed this for me.
在服务层和 dao 层之间添加一个委托类并将委托方法标记为事务性为我解决了这个问题。