Spring -当前线程没有实际事务可用的EntityManager -不能可靠地处理'调用

我得到这个错误时,试图调用“持久”方法来保存实体模型到数据库在我的Spring MVC web应用程序。在互联网上找不到任何与这个特定错误有关的帖子或页面。似乎EntityManagerFactory bean有问题,但我对Spring编程相当陌生,所以对我来说,似乎一切都初始化得很好,根据各种教程文章在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";


}








}
313867 次浏览

我也有同样的问题,我将方法注释为@Transactional,它工作了。

更新:检查spring文档,默认情况下PersistenceContext的类型是Transaction,所以这就是为什么方法必须是事务性的(http://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html):

@PersistenceContext注释有一个可选的属性类型, 默认为PersistenceContextType.TRANSACTION。默认值为 接收共享EntityManager代理所需要的东西。的 选择,PersistenceContextType。EXTENDED,是完全的 不同的事情:这会导致所谓的扩展EntityManager, 哪个不是线程安全的,因此不能在并发中使用 访问组件,例如spring管理的单例bean。扩展 EntityManagers只能用于有状态组件 类的生命周期中驻留在会话中的 EntityManager不是绑定到当前事务,而是绑定到当前事务

这个错误骗了我三天,我面对的情况导致了同样的错误。按照我能找到的所有建议,我尝试了一下这个配置,但没有用。

最终我找到了,不同的是,我正在执行的服务包含在一个普通的jar中,问题是AspectJ没有以相同的方式处理服务实例化。实际上,代理只是简单地调用底层方法,而没有在方法调用之前执行所有正常的Spring魔法。

最后,按照示例放置在服务上的@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();
}


}

我发布的方法是一个删除方法,但是注释以同样的方式影响所有持久性方法。

我希望这篇文章能帮助那些在从jar中加载服务时遇到同样问题的人

当我试图在spring数据存储库中使用deleteBy自定义方法时,我得到了这个异常。该操作是从JUnit测试类尝试的。

在JUnit类级别使用@Transactional注释时不会发生异常。

我移除了模式

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

要做到这一点

我也有同样的问题,我在applicationContext.xml中添加了tx:annotation-driven,它工作了。

我也犯了同样的错误,因为我从XML配置切换到java配置。

关键是,我没有迁移<tx:annotation-driven/>标签,就像Stone Feng建议的那样。

所以我只是在这里添加了@EnableTransactionManagement 在@Configuration类中设置Spring中的注释驱动事务,它现在工作

对我们来说,问题在于多个配置文件中的相同上下文设置。检查您没有在多个配置文件中复制以下内容。

<context:property-placeholder location="classpath*:/module.properties"/>
<context:component-scan base-package="...." />

当我从同一个组件中的非事务性方法访问一个已经带有事务注释的方法时,我也犯了同样的错误:

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();

我通过在自引用组件上调用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
}
}

当我在错误的方法/动作级别上使用@Transaction时,我有相同的错误代码。

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


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

当然,我必须将@Transactional放在methodWithANumberOfDatabaseActions()方法的上方。

在我的例子中,这解决了错误消息。

我有这个问题好几天了,我在网上找不到任何帮助我的东西,我把我的答案贴在这里,以防它能帮助到其他人。

在我的例子中,我正在处理一个通过远程调用的微服务,并且远程代理没有拾取服务级别上的@Transactional注释。

在服务层和dao层之间添加一个委托类,并将委托方法标记为事务性,这为我解决了这个问题。

boardRepo.deleteByBoardId (id);

面对同样的问题。有transactionrequiredexception:当前线程没有实际事务可用的EntityManager

我通过在控制器/服务上面添加@ transactional注释来解决这个问题。

这对我们有帮助,也许将来也能帮助到其他人。@Transaction不是为我们工作,但这是:

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

在测试类的类级别添加org.springframework.transaction.annotation.Transactional注释为我解决了这个问题。

只是为其他用户搜索这个错误的答案。另一个常见的问题是:

通常不能在同一个类中调用@transactional方法。

(有使用AspectJ的方法和方法,但重构会更容易)

所以你需要一个调用类和一个保存@transactional方法的类。

如果你有

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

和父类

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

你打电话给我

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

你不会得到一个Spring TransactionInterceptor(给你一个事务)。

这是你需要做的:

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

难以置信,但事实如此。

当我从Junit测试用例执行Spring JPA deleteAll()方法时,我得到了相同的错误。我简单地使用了deleteInBatch() &deleteAllInBatch()和它的完美工作。我们不需要在测试用例级别标记@Transactional

对于任何与我有相同问题的人,我正在从另一个类中调用公共方法method1method1然后在同一类中调用另一个公共方法method2method2被注释为@Transactional,但method1没有。 method1所做的只是转换一些参数并直接调用method2,所以这里没有DB操作

当我将@Transactional注释移动到method1时,这个问题就解决了。

不知道原因,但这对我来说很有用。

如果没有@Transactional注释,您可以通过从DB中查找实体,然后从DB中删除该实体来实现相同的目标。

CrudRepositor→void delete(T var1);

调用存储库方法是在使用@Component的类中调用的,将该方法从类中取出,并将其放入另一个使用@Service的类中。

要在测试中修复这个问题,可以使用@DataJpaTest@AutoConfigureTestDatabase

我已经有@Transactional,但仍然不能工作。事实证明,我必须摆脱< >强并行< / >强才能使它工作。

如果你在并行地做事情,<强> < / >强不

这就像你在使用共享的EntityManager,当你让它自动连接时,为了持久化spring告诉这个EntityManager bean是一个共享bean,为了持久化它需要持有这个bean,直到数据持久化没有完成,因此我们必须使用@Transactional,这样它就会在事务中启动并提交持久化,这样数据或操作就会完全保存或完全回滚。

您需要将@Transactional添加到方法中