Spring + Hibernate with annotations: No Hibernate Session bound to thread

25.5k views Asked by At

I'm new to Spring and I was trying to create a webapp with the following stack: Apache Tomcat 7, MySQL, Spring MVC, Hibernate 3 with JPA annotations.

I am trying to learn by following the book "Spring in Action, Third Edition" by Craig Walls.

First, I wanted to create a page that displays some entries I manually added to my DB, but it looks like my application is not capable of creating/retrieving any Hibernate Session from my SessionFactory. Here is my root cause stack trace:

exception

org.springframework.web.util.NestedServletException: 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
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:656)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

root cause

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
    com.nbarraille.www.dao.HibernateContactDAO.listContact(HibernateContactDAO.java:27)
    com.nbarraille.www.controllers.HomeController.showHomePage(HomeController.java:24)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:613)
    org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

And here are my concerned Classes/Config files:

My HibernateDAO:

@Repository
public class HibernateContactDAO implements ContactDAO {

    private SessionFactory sessionFactory;

    @Autowired
    public HibernateContactDAO(SessionFactory sessionFactory){
        this.sessionFactory = sessionFactory;
    }

    public void addContact(Contact contact) {
        sessionFactory.getCurrentSession().save(contact);
    }

    public List<Contact> listContact() {
        @SuppressWarnings("unchecked")
        List<Contact> cl = sessionFactory.getCurrentSession().createQuery("from Contact").list();
        return cl;
    }

    public void removeContact(Integer id) {
        Contact contact = (Contact) sessionFactory.getCurrentSession().load(Contact.class, id);
        if (null != contact) {
            sessionFactory.getCurrentSession().delete(contact);
        }

    }
}

My Contact class:

@Entity
@Table(name="contacts")
public class Contact implements Serializable {
    private static final long serialVersionUID = -5389913432051078273L;

    @Id
    @Column(name="id")
    @GeneratedValue
    private int id;

    @Column(name="first_name")
    private String firstname;

    @Column(name="last_name")
    private String lastname;

    @Column(name="email")
    private String email;

    @Column(name="telephone")
    private String telephone;


    // Setters/Getters
}

My Controller class:

@Controller

    public class HomeController {

        private ContactDAO contactDAO; // I know I should pass through a service instead of accessing my DAO directly, and I usually do, but I skipped it here to simplify and try to locate the problem

        @Inject
        public HomeController(ContactDAO contactDAO){
            this.contactDAO = contactDAO;
        }

        @RequestMapping({"/", "/home"})
        public String showHomePage(Map<String,Object> model){
            model.put("contacts", contactDAO.listContact());
            return "index";
        }
    }

Here is my Context Data Config file:

<bean id="DBpropertyConfigurer"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="/WEB-INF/jdbc.properties" />
    </bean>

<bean id="dataSource" 
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/nbarraille" />
    <property name="username" value="root" />
    <property name="password" value="password" />
    </bean>
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="com.nbarraille.www.core" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <!-- Adds an advisor to any bean annotated with @Repository so that any platform-specific exception
         are caught and then rethrown as one of Spring's unchecked data access exceptions -->
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

Here is my Dispatcher Servlet config:

<mvc:resources mapping="/resources/**"
                   location="/resources/" />

<mvc:annotation-driven />

<context:component-scan base-package="com.nbarraille.www" />

<bean class="org.springframework.web.servlet.view.tiles2.TilesViewResolver" />

<bean class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/tiles-def.xml</value>
            </list>
        </property>
    </bean>

    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages" />
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>

    <bean id="localeChangeInterceptor"
        class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
        <property name="paramName" value="lang" />
    </bean>

    <bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="defaultLocale" value="en"/>
    </bean>

And finally, here is my web.xml file:

<servlet>
    <servlet-name>nbarraille</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>nbarraille</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/nbarraille-service.xml
        /WEB-INF/nbarraille-data.xml
        /WEB-INF/nbarraille-security.xml
    </param-value>
</context-param>
3

There are 3 answers

3
limc On BEST ANSWER

You don't seem to have transaction configured yet... you can add the following into your Context Data Config file:-

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="txAdvice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:advisor pointcut="execution(* YOUR.PACKAGE..*.*(..))" advice-ref="txAdvice" />
</aop:config>

Change YOUR.PACKAGE to your actual package name, for example:-

execution(* com.project..*.*(..))

This is one lazy way to wrap all your methods in your project with transaction.

By the way, if you are going to lazily query your Hibernate domain objects (ex: parent.getChildren()) in your view, then I would highly suggest you to add this into your web.xml:-

<filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hibernateFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

This filter extends Hibernate session to the view.

0
vector On

For what it's worth ... run into this as well, after much looking had to modify part of web.xml:

    <filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    <init-param>
        <param-name>singleSession</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

... specifically had to set singleSession to true

0
RMachnik On

I have the same problem today. It is connected with hibernate unit of work conception and db transaction. So shortly speaking. If you use framework spring it has own transaction manager and session factory impelementation. But if you would like to use it you shuld remeber to configure it but also correct order of the adnotation. Your service should be @Transactional, your DAO should be @Repository and DAO use @Entity beans. So if you would use spring implementation of transaction manager you should use your transactional service in your controller ;) it is quite simple and in your dao you do sessionfactory.getCurrentSession() to get session ;)