Spring - 没有可用于当前线程的实际事务的 EntityManager - 无法可靠地处理“持久”调用

Posted

技术标签:

【中文标题】Spring - 没有可用于当前线程的实际事务的 EntityManager - 无法可靠地处理“持久”调用【英文标题】:Spring - No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call 【发布时间】:2015-11-23 00:08:07 【问题描述】:

在我的 Spring MVC Web 应用程序中尝试调用“persist”方法将实体模型保存到数据库时出现此错误。无法在互联网上真正找到与此特定错误相关的任何帖子或页面。 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";

        




    

【问题讨论】:

正如错误所说,没有交易。用@Transaction注释注册方法。 【参考方案1】:

我遇到了同样的问题,我将方法注释为@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 不绑定到当前事务,而是 完全取决于应用程序。

【讨论】:

如果一个没有@Transactional注解的方法在同一个类文件中调用了一个带有@Transactional注解的方法,你也会被这个错误所震惊(这就是我所面临的)。 我用@Transactional 注释了服务类,这也有效。不确定这是否是正确的方法,但它似乎工作正常...... 另外值得一提的是,这个注解应该用于公共方法,否则它不起作用。 请记住,@Transactional 仅适用于公共方法。 仅供参考,这需要 javax.transaction.Transactional 注释(不是 Spring 注释)。【参考方案2】:

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

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

【讨论】:

我也遇到了同样的情况,我没有对测试类进行注释,而是对服务方法进行了注释,因此即使它不是测试,它也会向下流动。 @Transactional 在类级别上可以掩盖可能在您的服务中操纵不同事务的测试问题。 我在存储库方法上使用了@Trasactional,因为它是我实际与数据库交互的地方并且它工作正常。 我在尝试“deleteBy”时遇到了这个问题。我将方法更改为“删除”并将整个对象作为参数传递。 在愚弄 chad derby 的教程时遇到了这个问题。他教导说,默认情况下 jpa 在服务级别具有 @transactional,因此我们不需要在此处添加它。但是在删除的情况下,我们实际上需要在使用 entityManager.remove(object)【参考方案3】:

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

最终我找到了,不同之处在于,我正在执行的服务包含在一个普通的 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 加载服务时遇到同样问题的人

【讨论】:

@Scope(proxyMode = ScopedProxyMode.INTERFACES) 添加到实现接口的DAO 类中非常重要。我花了一整天的时间来找出这个错误,而你的解决方案是唯一有效的。非常感谢! 您回答中的一个 L'annotation 刚刚为我节省了大约 3 天的时间。我刚刚体验了水肿的真正威力。 Дякс.【参考方案4】:

boardRepo.deleteByBoardId(id);

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

我通过在控制器/服务上方添加 @Transactional 注释解决了这个问题。

【讨论】:

【参考方案5】:

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

重点是,我并没有按照 Stone Feng 的建议迁移 &lt;tx:annotation-driven/&gt; 标签。

所以我只是按照这里的建议添加了@EnableTransactionManagement Setting Up Annotation Driven Transactions in Spring in @Configuration Class,现在可以使用了

【讨论】:

【参考方案6】:

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

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

从同一组件中的非事务性方法访问已经事务性注释的方法时,我遇到了同样的错误:

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
        
    

【讨论】:

【参考方案9】:

仅供其他用户寻找错误答案的注释。另一个常见问题是:

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

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

因此,您需要一个调用类和包含 @transactional 方法的类。

【讨论】:

【参考方案10】:

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

CrudRepositor -> void delete(T var1);

【讨论】:

【参考方案11】:

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

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案12】:

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

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

【讨论】:

【参考方案13】:

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

methodWithANumberOfDatabaseActions()  
   methodA( ...)
   methodA( ...)


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

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

这解决了我的错误消息。

【讨论】:

【参考方案14】:

如果你有

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

难以置信但真实。

【讨论】:

【参考方案15】:

我已经拥有@Transactional,但仍然无法正常工作。事实证明,我必须摆脱 parallelism 才能使其工作。

如果您同时做事,不要

【讨论】:

【参考方案16】:

我从

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

完成这项工作

【讨论】:

【参考方案17】:

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

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

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

【讨论】:

【参考方案18】:

这帮助了我们,也许它可以在未来帮助其他人。 @Transaction 对我们不起作用,但这样做了:

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

【讨论】:

【参考方案19】:

当我从 Junit 测试用例中执行 Spring JPA deleteAll() 方法时,我遇到了同样的错误。我只是简单地使用了deleteInBatch()deleteAllInBatch(),它的效果很好。我们不需要在测试用例级别标记@Transactional

【讨论】:

【参考方案20】:

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

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

不确定原因,但这对我来说是这样。

【讨论】:

【参考方案21】:

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

【讨论】:

请阅读How to answer并更新您的答案。【参考方案22】:

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

【讨论】:

【参考方案23】:

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

【讨论】:

以上是关于Spring - 没有可用于当前线程的实际事务的 EntityManager - 无法可靠地处理“持久”调用的主要内容,如果未能解决你的问题,请参考以下文章

javax.persistence.TransactionRequiredException:没有 EntityManager 具有可用于当前线程的实际事务

Spring事务传播行为

spring 事务传播行为

错误:无法使用 spring 任务执行器获取当前线程的事务同步会话

spring事务传播属性与隔离级别

Spring Hibernate - 无法为当前线程获取事务同步会话