Reputation: 37116
I research using transactional annotation in Spring + Hibernate application.
I had problem with using annotation. I had so problem:
No Hibernate Session bound to thread
my partner to advice me use this filter in web.xml:
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
after I made it problem was resolved. He said me that always when he uses @Transactional
annotation he writes this filter.
But in my old application I found using @Transactional
without this filter. And it is working application.
Who can explain me it?
application which doesn't work without filter described here: HTTP Status 500 - Request processing failed; nested exception is org.hibernate.HibernateException: No Hibernate Session bound to thread
update:
model class:
@Entity
@Table(name = "RECORDS")
public class Record
{
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private int recordId;
@Column(name = "VALUE1")
private String vol1;
@Column(name = "VALUE2")
private String vol2;
@Column(name = "VALUE3")
private String vol3;
//////////
get and set
}
update
trace:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/crud] threw exception [Request processing failed; nested exception is org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here] with root cause
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:544)
at com.wp.crud.dao.MainDaoImpl.getAllRecords(MainDaoImpl.java:49)
at com.wp.crud.controller.MainServiceImpl.getAllRecords(MainServiceImpl.java:50)
at com.wp.crud.controller.MainController.setupForm(MainController.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
my java code:
controller method
@RequestMapping(value = "/index")
public String setupForm()
{
mainService.getAllRecords());
}
service interface
@Transactional
public interface MainService
{
public List<Record> getAllRecords();
}
service impl
@Service("mainService")
@Transactional
public class MainServiceImpl implements MainService
{
@Autowired
private MainDao mainDao;
@Transactional
public List<Record> getAllRecords()
{
return mainDao.getAllRecords();
}
}
Configuration:
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
data.xml
<!-- Настраивает управление транзакциями с помощью аннотации @Transactional -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Менеджер транзакций -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!-- Настройки бина dataSource будем хранить в отдельном файле -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:location="/WEB-INF/jdbc.properties" />
<!-- Непосредственно бин dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}" p:password="${jdbc.password}" />
<!-- Настройки фабрики сессий Хибернейта -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
root-context.xml
<context:component-scan base-package="com.wp.crud.dao"/>
<context:component-scan base-package="com.wp.crud.controller"/>
<!--
Файл с настройками ресурсов для работы с данными (Data Access Resources)
-->
<import resource="data.xml"/>
servlet-context.xml
<annotation-driven />
<!-- Всю статику (изображения, css-файлы, javascript) положим в папку webapp/resources и замаппим их на урл вида /resources/** -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Отображение видов на jsp-файлы, лежащие в папке /WEB-INF/views -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!-- Файл с настройками контроллеров -->
<beans:import resource="controllers.xml" />
controllers.xml
<context:component-scan base-package="com.wp.crud.controller"/>
P.S.
Services located in controller package.
Upvotes: 0
Views: 728
Reputation: 125202
If you need, or want to use, the OpenSessionInViewFilter
depends on your code. If you use your domain objects in your view and have lazy loading collections (the default) or references you will need this filter. By default the hibernate session is gone after finishing the @Transactional
, however to perform lazy-loading a hibernate session is needed. The OpenSessionInViewFilter
keeps the session(s) open until the view has been rendered.
If you don't have anything lazy or have specific queries/methods to retrieve all the data needed to render the view (i.e. force eager fetching of collection or objects with a HQL query) you don't need an OpenSessionInViewFilter
.
So whether you need it or not depends on your use-case and technology choice.
The problem in your case is the fact that you are scanning the same package twice. This leads to duplicate bean instances (one in the ContextLoaderListener
and one in the DispatcherServlet
the first is the bean you want to use but due to the duplication the second is the one you get). The first is the one with transactions applied.
Change your component scan elements to the following
root-context.xml
<context:component-scan base-package="com.wp.crud">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
controllers.xml
<context:component-scan base-package="com.wp.crud" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
This will make the ContextLoaderListener
load everything BUT @Controller
annotated beans and the DispatcherServlet
loads only @Controller
annotated beans.
Upvotes: 1
Reputation: 57421
You should mark necessary methods with @Transactional
to define where to start/end transactions.
In your application context you should define that it's annotation driven and define base package to scan. Thus Spring scans the packages and wraps methods marked as @Transactional
with Proxies to start and commit/rollbacck transactions.
Upvotes: 0