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

Posted

技术标签:

【中文标题】javax.persistence.TransactionRequiredException:没有 EntityManager 具有可用于当前线程的实际事务【英文标题】:javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread 【发布时间】:2017-10-09 15:04:11 【问题描述】:

我使用 Hibernate 创建了我的第一个 Spring MVC 项目。我的 DAO 层使用 JPA EntityManager 与数据库交互。

GenericDao.java:

@Repository
public abstract class GenericDao<T> implements GeneralDao<T> 

    private Class<T> className;

    public GenericDao(Class<T> className) 
        this.className = className;
    

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() 
        return entityManager;
    

    @Override
    public void add(T object) 
        try 
            getEntityManager().persist(object);
         catch (HibernateException e) 
            throw new DaoException(ErrorMessage.ADD_ENTITY_FAIL, e);
        
    

    @Override
    public void update(T object) 
        try 
            getEntityManager().merge(object);
         catch (HibernateException e) 
            throw new DaoException(ErrorMessage.UPDATE_ENTITY_FAIL, e);
        
    

    @Override
    public void remove(T object) 
        try 
            getEntityManager().remove(object);
         catch (HibernateException e) 
            throw new DaoException(ErrorMessage.REMOVE_ENTITY_FAIL, e);
        
    

    @Override
    public T getById(int id) 
        try 
            return getEntityManager().find(this.className, id);
         catch (HibernateException e) 
            throw new DaoException(ErrorMessage.GET_BY_ID_ENTITY_FAIL, e);
        
    

    public abstract List<T> getAll() throws DaoException;


GenericService.java

@Service
public abstract class GenericService<T> implements GeneralService<T> 
    private static Logger logger = Logger.getLogger(GenericService.class);

    @Autowired
    private GenericDao<T> dao;

    @Transactional
    @Override
    public void add(T object) throws ServiceException 
        try 
           dao.add(object);
         catch (DaoException e) 
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        
    

    @Transactional
    @Override
    public void update(T object) throws ServiceException 
        try 
            dao.update(object);
         catch (DaoException e) 
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        
    

    @Transactional
    @Override
    public void remove(T object) throws ServiceException 
        try 
            dao.remove(object);
         catch (DaoException e) 
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        
    

    @Transactional(readOnly = true)
    @Override
    public T getById(int id) throws ServiceException 
        try 
            return dao.getById(id);
         catch (DaoException e) 
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        
    

    @Transactional(readOnly = true)
    @Override
    public List<T> getAll() throws ServiceException 
        try 
            return dao.getAll();
         catch (DaoException e) 
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        
    


UserServiceImpl.java:

@Service
public class UserServiceImpl extends GenericService<User> implements UserService 
    private static Logger logger = Logger.getLogger(UserServiceImpl.class);

    @Autowired
    private UserDao userDao;

    @Transactional
    @Override
    public String checkUser(String userLogin, String userPassword) throws ServiceException 
        String namePage = "errorAuthorization";
        List<User> userList;
        try 
           userList = userDao.getByLoginAndPassword(userLogin, userPassword);
          catch (DaoException e) 
            logger.debug(e);
            throw new ServiceException(e.getMessage());
        
        if(userList.size() != 0) 
            return UserRoleChecker.defineUserPage(userList.get(0));
        
        return namePage;
    

    @Transactional
    @Override
    public void addUser(String userLogin, String userPassword, String userMail) throws ServiceException 
        Role role = new Role(0L, RoleType.USER);
        User user = new User(0L, userLogin, userPassword, userMail, role);
        add(user);
    


UserController.java:

@Controller
public class UserController 
    private static String className = UserController.class.getName();
    private static Logger logger = Logger.getLogger(UserController.class.getName());

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/check_user", method = RequestMethod.POST)
    public ModelAndView authorizationUser(HttpServletRequest request, HttpServletResponse response) 
        ModelAndView modelAndView = new ModelAndView();
        String returnPage;
        try 
            returnPage = userService.checkUser(request.getParameter(RequestParameter.USER_LOGIN), request.getParameter(RequestParameter.USER_PASSWORD));
         catch (ServiceException e) 
            logger.debug(e);
            returnPage = ErrorHandler.returnErrorPage(e.getMessage(), className);
        
        modelAndView.setViewName(returnPage);
        return modelAndView;
    

    @RequestMapping(value = "/add_user", method = RequestMethod.POST)
    public ModelAndView registrationUser(HttpServletRequest request, HttpServletResponse response) 
        ModelAndView modelAndView = new ModelAndView();
        String returnPage = Page.SUCCESSFUL_REGISTRATION;
        try 
            userService.addUser(request.getParameter(RequestParameter.USER_LOGIN), request.getParameter(RequestParameter.USER_PASSWORD), request.getParameter(RequestParameter.USER_MAIL));
          catch (ServiceException e) 
            logger.debug(e);
           returnPage = ErrorHandler.returnErrorPage(e.getMessage(), className);
        
        modelAndView.setViewName(returnPage);
        return modelAndView;
    


root-context.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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:annotation-config />

    <context:component-scan base-package="by.netcracker.artemyev.dao" />
    <context:component-scan base-package="by.netcracker.artemyev.service" />
    <context:component-scan base-package="by.netcracker.artemyev.web" />

    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/airline?useSSL=false" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="initialSize" value="5"/>
        <property name="maxTotal" value="10"/>
    </bean>

    <bean id="entityManager" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="by.netcracker.artemyev" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="database" value="MYSQL" />
                <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="debug">true</prop>
                <prop key="connection.isolation">2</prop>
                <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
        <property name="jpaDialect" ref="jpaDialect" />
    </bean>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

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

    <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManager" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaDialect" ref="jpaDialect" />
    </bean>

</beans>

日志:

org.springframework.web.servlet.FrameworkServlet 2017-05-10 22:23:59,107 DEBUG - Could not complete request
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:282)
    at com.sun.proxy.$Proxy27.persist(Unknown Source)
    at by.netcracker.artemyev.dao.GenericDao.add(GenericDao.java:35)
    at by.netcracker.artemyev.service.GenericService.add(GenericService.java:24)
    at by.netcracker.artemyev.service.impl.UserServiceImpl.addUser(UserServiceImpl.java:48)
    at by.netcracker.artemyev.web.UserController.registrationUser(UserController.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:475)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:495)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:767)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1354)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

为什么我会遇到这个问题以及如何解决?

【问题讨论】:

我对配置投票。 根据您的堆栈跟踪,我们可以看到事务拦截器没有到位,这就是没有活动事务的原因。所以存在某种错误配置。我会说检查您使用哪个 @Transaction 注释是 Spring 注释还是 Javax 注释。 你能展示扩展 GenericDao 的类吗? 【参考方案1】:

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

【讨论】:

【参考方案2】:

您已在问题中显示root-context.xml,但此上下文文件没有 ViewResolver bean 配置。您必须在 web.xml 中配置 Dispatcher Servlet 的 servlet 上下文配置文件(例如 dispatcher-servlet.xml)。请将以下配置添加到您的dispatcher-servlet.xml

<tx:annotation-driven />

如果您的 dispatcher-servlet.xml 中没有 xmlns:tx 定义,就像其他 xmlns: 定义一样,请添加它。请确保您在调度程序 servlet 上下文中进行了正确的组件扫描配置。

<context:component-scan base-package="by.netcracker.artemyev.*" />

您也可以参考帖子@Transactional doesn't work in Spring Web MVC? 报告了类似问题。

【讨论】:

【参考方案3】:

我在维护遗留代码时遇到了这个问题,最后我从复杂的注释中意识到。

它需要应用级别的@EnableTransactionManagement。否则@Transactional 不起作用

【讨论】:

以上是关于javax.persistence.TransactionRequiredException:没有 EntityManager 具有可用于当前线程的实际事务的主要内容,如果未能解决你的问题,请参考以下文章