Spring - 没有具有可用于当前线程的实际事务的 EntityManager - 无法可靠地处理 'persist' 调用

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

我在 Spring MVC Web 应用程序中尝试调用 "persist" 方法将实体模型保存到数据库时遇到此错误。在 Internet 上无法真正找到与此特定错误相关的任何 post 或页面。 EntityManagerFactory bean 似乎出了点问题,但我对 Spring 编程还很陌生,所以对我来说,根据网络上的各种教程文章,似乎一切都已正确初始化。

调度员-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";

        }




    }

我遇到了同样的问题,我将方法注释为 @Transactional,它起作用了。

更新:检查 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.

这个错误困扰了我三天,我遇到的情况也出现了同样的错误。按照我能找到的所有建议,我尝试了配置但无济于事。

最终我找到了,不同之处在于,我正在执行的服务包含在一个普通的 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();
    }

}

我 posted 的方法是删除方法,但注释以相同的方式影响所有持久性方法。

我希望这 post 可以帮助那些在从 jar 加载服务时遇到同样问题的其他人

我在尝试使用 spring 数据存储库中的 deleteBy 自定义方法时遇到此异常。该操作是从 JUnit 测试 class.

中尝试的

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

我从

中删除了模式
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />

使这项工作

我遇到了同样的问题,我在 applicationContext.xml 中添加了 tx:annotation-driven,它起作用了。

我有同样的错误,因为我从 XML- 配置切换到 java- 配置。

重点是,我没有按照 Stone Feng 的建议迁移 <tx:annotation-driven/> 标签。

所以我只是按照此处的建议添加了 @EnableTransactionManagement Setting Up Annotation Driven Transactions in Spring in @Configuration Class,现在有效

对我们来说,问题归结为多个配置文件中的相同上下文设置。检查您是否在多个配置文件中复制了以下内容。

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

我在错误的 method/actionlevel 上使用 @Transaction 时出现了相同的错误代码。

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

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

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

这解决了我的错误消息。

我已经遇到这个问题好几天了,我在网上找到的任何地方都没有帮助我,我在这里发布我的答案以防它对其他人有帮助。

在我的例子中,我正在处理一个通过远程调用的微服务,而我在服务级别的 @Transactional 注释没有被远程代理接收。

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

boardRepo.deleteByBoardId(id);

遇到了同样的问题。 GOT javax.persistence.TransactionRequiredException: 当前线程没有可用的实际事务的 EntityManager

我通过在 controller/service 上方添加 @Transactional 注释来解决它。

这对我们有帮助,也许将来可以帮助其他人。 @Transaction 对我们不起作用,但这确实有效:

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

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

仅供 其他 用户搜索该错误的答案。另一个常见问题是:

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

(使用 AspectJ 有多种方式方法,但重构会更容易)

所以您需要调用 class 和 class 来保存 @transactional 方法。

如果你有

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

和超class

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

对于和我有同样问题的人,我是从另一个 class 中调用 public 方法 method1method1 然后在同一个 class 中调用另一个 public 方法 method2method2 被注释为 @Transactional,但 method1 不是。 method1所做的只是转换一些参数并直接调用method2,所以这里没有数据库操作。

我将 @Transactional 注释移至 method1 后,问题就解决了。

不确定原因,但这对我有用。

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

CrudRepositor -> void delete(T var1);

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

要在测试中解决此问题,您可以使用 @DataJpaTest 或 @AutoConfigureTestDatabase。

我已经有了 @Transactional,但仍然无法使用。结果我不得不摆脱 parallelism 才能让它工作。

如果您并行处理事情,不要

这就像你在使用共享的 EntityManager 时自动装配它所以为了持久化 spring 告诉这个 EntityManager bean 是一个共享 bean 并且为了持久化它需要持有这个 bean 直到数据持久化没有完成,因此我们必须使用 @Transactional 以便它开始并提交事务中的持久性,以便数据或操作得到完全保存或完全回滚。

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