I have problem using simple Spring MVC Hibernate CRUD application. I use annotation Transactional in Service layer, method, calling from Service layer is DAO method, it should be wrapped in this transaction and use the same Session but it doesn't. if I open new Session - it works, but I want to work in Session, created in Session layer.
Exception: Message Request processing failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
Root Cause
org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
org.springframework.orm.hibernate5.SpringSessionContext.currentSession(SpringSessionContext.java:143)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:479)
by.itacademy.newsproject.dao.impl.SQLNewsDAOImpl.create(SQLNewsDAOImpl.java:36)
by.itacademy.newsproject.service.impl.NewsServiceImpl.create(NewsServiceImpl.java:35)
by.itacademy.newsproject.controller.NewsController.createNews(NewsController.java:93)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
Note The full stack trace of the root cause is available in the server logs.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name> project-spring-hib</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/application-context.xml
/WEB-INF/spring/hibernateConfig.xml
</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
application-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: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.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
</beans>
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"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<!-- Add support for reading web resources: css, images, js, etc ... -->
<mvc:resources location="/resources/" mapping="/resources/**"/>
<!-- Specifying base package of the Components like Controller, Service,
DAO -->
<context:component-scan base-package="by.itacademy.newsproject.*"/>
<!-- View Resolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
hibernateConfig.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Getting Database properties -->
<context:property-placeholder location="classpath:hibernate.properties"/>
<!-- Define Database DataSource / connection pool -->
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.user}" />
<property name="password" value="${jdbc.pass}" />
<!-- these are connection pool properties for C3P0 -->
<property name="minPoolSize" value="${jdbc.minPoolSize}" />
<property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
<property name="maxIdleTime" value="${jdbc.maxIdleTime}" />
</bean>
<!-- Setup Hibernate session factory -->
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="by.itacademy.newsproject.entity" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
<!-- Setup Hibernate transaction manager -->
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<!-- Enable configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="myTransactionManager"/>
</beans>
News
package by.itacademy.newsproject.entity;
import java.io.Serializable;
import java.time.LocalDate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "newspaper")
public class News implements Serializable {
private static final long serialVersionUID = -4037020843129228781L;
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "date")
private LocalDate date;
@Column(name = "section")
private String section;
@Column(name = "author")
private String author;
@Column(name = "brief")
private String brief;
@Column(name = "content")
private String content;
public News() {
}
public News(int id, LocalDate date, String section, String author, String brief, String content) {
this.id = id;
this.date = date;
this.section = section;
this.author = author;
this.brief = brief;
this.content = content;
}
public News(LocalDate date, String section, String author, String brief, String content) {
this.date = date;
this.section = section;
this.author = author;
this.brief = brief;
this.content = content;
}
public News(String section, String author, String brief, String content) {
this.section = section;
this.author = author;
this.brief = brief;
this.content = content;
}
// getters, setters, equals, hashcode
Controller:
NewsController
package by.itacademy.newsproject.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import by.itacademy.newsproject.entity.News;
import by.itacademy.newsproject.entity.ParameterForm;
import by.itacademy.newsproject.service.NewsService;
import by.itacademy.newsproject.service.ServiceException;
@Controller
@RequestMapping(value = "/news")
public class NewsController {
private static final Logger logger = Logger.getLogger(NewsController.class);
private NewsService newsService;
@Autowired
public void setNewsService(NewsService newsService) {
this.newsService = newsService;
}
/**
* FILL_NEWS
*
* Forwards to JSP page with form to fill data which we need to save into DB.
* This COMMAND works together with CREATE_NEWS to finish saving News into DB.
*
* Puts News object into Model to handle it inside JSP page.
*
* @author
*
*/
@GetMapping(value = "/fill_news")
public String fillNews(Model model, HttpSession session) {
News news = new News();
session.setAttribute(ParameterSession.CURRENT_COMMAND, ParameterCommand.FILL_NEWS);
model.addAttribute("news", news);
return "fill_news";
}
/**
* CREATE_NEWS
*
* Accepts data from JSP file: section, author, brief, content. ID and DATE
* generates automatically. Date - current server date, ID - auto increment next
* in DAO layer.
* <p>
* Puts data to DB and returns status result message (RESULT_OPERATION).
* <p>
* Receives data from JSP form page, creates bean
* <p>
* Transfers bean to Service layer.
*
* Creates session with message if operation successfully performed
*
* <p>
* Session value RESULT_OPERATION to save result message if operation performed
* successfully
*
* <p>
* Session value CURRENT_COMMAND needs if locale changes on this page, the same
* page should be displayed. Our case - WELCOME_PAGE because operation executed
* successfully.
*
* <p>
* Uses Post/Redirect/Get
*
*
*/
@PostMapping(value = "/create_news")
public String createNews(@ModelAttribute("news") News news, HttpSession session) {
logger.info("createNews running, news=" + news);
news.setDate(ParameterForm.DATE);
try {
newsService.create(news);
session.setAttribute(ParameterSession.RESULT_OPERATION,
ParameterSession.RESULT_OPERATION_MSG_CREATE_SUCCESS);
session.setAttribute(ParameterSession.CURRENT_COMMAND, ParameterCommand.WELCOME_PAGE);
} catch (ServiceException e) {
session.setAttribute(ParameterSession.RESULT_OPERATION,
ParameterSession.RESULT_OPERATION_MSG_CREATE_FAILED);
logger.error("Error creating news / " + e);
}
return "redirect:/news/welcome_page";
}
.......
Service:
package by.itacademy.newsproject.service.impl;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import by.itacademy.newsproject.dao.DAOException;
import by.itacademy.newsproject.dao.NewsDAO;
import by.itacademy.newsproject.entity.News;
import by.itacademy.newsproject.service.NewsService;
import by.itacademy.newsproject.service.ServiceException;
@Service
public class NewsServiceImpl implements NewsService {
private static final Logger logger = Logger.getLogger(NewsServiceImpl.class);
@Autowired
public void setNewsDAO(NewsDAO newsDAO) {
this.newsDAO = newsDAO;
}
private NewsDAO newsDAO;
@Override
@Transactional
public void create(News news) throws ServiceException {
try {
newsDAO.create(news);
} catch (DAOException e) {
logger.error("Error creating news / ", e);
throw new ServiceException(e);
}
}
......
DAO: SQLNewsDAOImpl
package by.itacademy.newsproject.dao.impl;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import by.itacademy.newsproject.dao.DAOException;
import by.itacademy.newsproject.dao.NewsDAO;
import by.itacademy.newsproject.entity.News;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class SQLNewsDAOImpl implements NewsDAO {
private static final Logger logger = Logger.getLogger(SQLNewsDAOImpl.class);
@Autowired
public void setMySessionFactory(SessionFactory mySessionFactory) {
this.mySessionFactory = mySessionFactory;
}
private SessionFactory mySessionFactory;
private static final String HQL_SELECT_ALL = "from News";
private static final String HQL_SELECT_BY_ID = "from News where id =:newsId";
private static final String HQL_DELETE_BY_ID = "delete from News where id =:newsId";
@Override
public void create(News news) throws DAOException {
// if I open new Session - it works! but I need to use Session, that opened using
// @Transactional from preveous Service layer
// Session currentSession = sessionFactory.openSession();
Session currentSession = mySessionFactory.getCurrentSession();
currentSession.saveOrUpdate(news);
}
......