在实现springMVC和hibernate整合的项目时,点击初始jsp页面控件进行跳转报错:
严重: 在路径为/hm3的上下文中,Servlet[springmvc]的Servlet.service()引发了具有根本原因的异常Request processing failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:133)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:456)
at com.cake.dao.CakeDao.findCake(CakeDao.java:47)
at com.cake.service.CakeService.getAllCake(CakeService.java:26)
at com.cake.controller.CakeController.list(CakeController.java:106)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
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:116)
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.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol C o n n e c t i o n H a n d l e r . p r o c e s s ( A b s t r a c t P r o t o c o l . j a v a : 868 ) a t o r g . a p a c h e . t o m c a t . u t i l . n e t . N i o E n d p o i n t ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint ConnectionHandler.process(AbstractProtocol.java:868)atorg.apache.tomcat.util.net.NioEndpointSocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor W o r k e r . r u n ( U n k n o w n S o u r c e ) a t o r g . a p a c h e . t o m c a t . u t i l . t h r e a d s . T a s k T h r e a d Worker.run(Unknown Source) at org.apache.tomcat.util.threads.TaskThread Worker.run(UnknownSource)atorg.apache.tomcat.util.threads.TaskThreadWrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
可以明显看出主要问题就是 Could not obtain transaction-synchronized Session for current thread。Could not obtain transaction-synchronized Session for current thread原因及解决方案提供的解释是因为代码中调用了sessionFactory.getCurrentSession()方法,而这个方法必须添加到事务中,也就是这个代码:
<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
但这并没有解决我的问题,因为定义事务的代码早就已经添加到ApplicationContext.xml里了,但这个博客提供给我一个新的搜索方向——定义的事务不生效,也就是虽然我添加了<tx:annotation-driven transaction-manager=“transactionManager” />但并没有将session成功添加到事务。
后来查询发现在spring-mvc.xml文件中配置context:component-scan的package属性会影响到springMVC容器对Bean的扫描,从而把service、dao的bean重新加载并覆盖原有的service、dao,造成了事务失效。
我的配置是这样的:
<context:component-scan base-package="com.cake" >
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
有两种改法,第一种方法是将base-package设定为仅有controller的com.cake.controller,即避开service、dao,保证事务的生效,代码如下:
<context:component-scan base-package="com.cake.controller" >
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
第二种方法显然更为正规一点,就是将默认为true的use-default-filters设定为false,禁用默认的扫描方法,代码如下:
<context:component-scan base-package="com.cake" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
关于use-default-filters的详解可以去看context:component-scan标签的use-default-filters属性的作用以及原理分析
这样设置后可以运行成功了,总结一下就是若调用了sessionFactory.getCurrentSeesion()方法需要在ApplicationContext.xml将其添加到事务中,并在spring-MVC.xml中将context:component-scan的use-default-filters设置为false,保证事务的有效运行。