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


135

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

        }




    }

1
如错误所示,没有事务。用注释注册方法@Transaction
罗希特

Answers:


259

我遇到了同样的问题,并为该方法添加了注释,@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管理的Singleton bean)中使用。扩展的EntityManager仅应用于有状态的组件(例如,驻留在会话中),并且EntityManager的生命周期不依赖于当前事务,而完全取决于应用程序。


71
如果没有注解@Transactional的方法@Transactional在同一个类文件中调用带有注解的方法,那么您也会被该错误所打击(这就是我要面对的问题)。
雅各布·范·林根

5
我用注释了服务类@Transactional,它也有效。不知道这是否是正确的方法,但似乎工作正常……
milosmns

8
值得一提的另一件事是,此批注应用于公共方法,否则将无法正常工作。
Yuriy Kravets

7
请记住,@ Transactional仅适用于公共方法。
Andrei_N

7
仅供参考,这需要javax.transaction.Transactional注释(不是Spring注释)。
java-addict301 '19

85

尝试在spring数据存储库中使用deleteBy自定义方法时遇到此异常。尝试从JUnit测试类进行该操作。

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


8
我处在相同的情况下,我没有注释测试类,而是注释了服务方法,因此即使不是测试,它也可以遍历。
Paul Nelson Baker's

@Transactional在类级别上,可以掩盖可能操纵服务中不同事务的测试问题。
Zon

1
我使用@Trasactional存储库方法是因为它实际上是我与数据库进行交互的地方,并且可以正常工作。
Harish Kumar Saini

22

这个错误使我困惑了三天,遇到的情况也产生了同样的错误。遵循所有可以找到的建议,我使用了配置,但无济于事。

最终,我发现了这一点,不同之处在于,我正在执行的服务包含在一个通用的jar中,结果是AspectJ没有将Service实例视作相同。实际上,代理只是简单地调用基础方法,而没有在方法调用之前执行所有普通的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();
    }

}

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

我希望这篇文章可以帮助其他在从罐子中加载服务时遇到相同问题的人


添加@Scope(proxyMode = ScopedProxyMode.INTERFACES)到实现接口的DAO类中非常重要。我花了一整天的时间来找出此错误,而您的解决方案是唯一可行的方法。非常感谢你!
Thach Van

1
您回答中的一个L'annotation大概为我节省了3天。我刚刚体验了埃德纳玛的真正力量。Дякс。
Oleksii Kyslytsyn


8

boardRepo.deleteByBoardId(id);

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

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


7

我遇到了同样的问题,我加入tx:annotation-driven进来applicationContext.xml并成功了。


4

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

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

3

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


3

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

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

(有很多方法和方法可以使用AspectJ,但是重构会更容易)

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


2

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

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

2

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

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

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

当然,我不得不将方法放在@Transactional方法之上methodWithANumberOfDatabaseActions()

就我而言,这解决了错误消息。


0

我从

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

使这项工作


0

我已经连续几天遇到这个问题,在网上找不到的任何内容对我有帮助,因此我在此处发布我的答案,以防万一。

就我而言,我正在开发通过远程调用的微服务,并且远程代理未使用我在服务级别的@Transactional批注。

在服务层和dao层之间添加一个委托类,并将委托方法标记为事务性,这对我来说就是固定的。


0

这帮助了我们,也许将来可以帮助其他人。@Transaction没有为我们工作,但是这样做:

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


0

如果你有

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

令人难以置信,但却是真实的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.