2011-02-25 4 views
8

Sono nuovo per Spring e stavo cercando di creare una webapp con il seguente stack: Apache Tomcat 7, MySQL, Spring MVC, Hibernate 3 con annotazioni JPA.Spring + Hibernate con annotazioni: No Hibernate Session legata al thread

Sto cercando di imparare seguendo il libro "Spring in Action, Third Edition" di Craig Walls.

In primo luogo, volevo creare una pagina che visualizza alcune voci che ho aggiunto manualmente al mio DB, ma sembra che la mia applicazione non sia in grado di creare/recuperare una sessione di ibernazione dalla mia SessionFactory. Qui è la mia radice traccia causa dello stack:

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) 

E qui sono le mie classi interessate/i file di configurazione:

mio 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); 
     } 

    } 
} 

mio referente classe:

@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 
} 

Classe My Controller:

@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"; 
     } 
    } 

Ecco il mio contesto di file dati di configurazione:

<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" /> 

Ecco il mio config Dispatcher Servlet:

<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> 

E, infine, ecco il mio file web.xml:

<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> 

risposta

12

Sembra che tu non abbia ancora configurato la transazione ... puoi aggiungere quanto segue al tuo Contesto dati di configurazione 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 al nome del pacchetto, ad esempio: -

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

Questo è un modo pigro per avvolgere tutti i metodi nella vostra progetto con transazione.

A proposito, se avete intenzione di interrogare pigramente gli oggetti del dominio Hibernate (es: parent.getChildren()) a suo avviso, allora vi suggerisco di aggiungere questo nella vostra 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> 

Questo filtro estende la sessione di Hibernate alla vista.

+1

Corretto. Forse anche considerare l'uso della notazione '@ Transactional' sul tuo DAO come alternativa a aop. –

+0

Aggiungendo la cosa del filtro mi sono liberato di quell'errore, grazie. Cosa sta facendo esattamente? Non ho ancora aggiunto il supporto per le transazioni, perché non so cosa sia. – nbarraille

+1

@nbarraille: questo filtro garantisce che il livello di visualizzazione abbia accesso alla sessione di Sospensione. Tieni presente che avrai bisogno di questo filtro ** E ** transazione configurata se vuoi che le tue cose funzionino correttamente. Avere questo filtro non significa che tu abbia una transazione su ogni richiesta dell'utente. Leggi questo per maggiori informazioni: http://static.springsource.org/spring/docs/1.2.9/api/org/springframework/orm/hibernate3/support/OpenSessionInViewFilter.html – limc

0

Per quel che vale ... incorrere in questo pure, dopo molto guardando dovuto modificare parte di 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> 

... specificatamente dovuto impostare singleSession a vero

0

Ho lo stesso problema oggi.È connesso con la concezione dell'unità di lavoro in ibernazione e la transazione db. Quindi, a breve. Se si utilizza spring framework, dispone del proprio gestore transazioni e dell'impostazione della factory di sessione. Ma se ti piacerebbe usarlo, ricordati di configurarlo, ma anche di correggere l'ordine dell'adnotazione. Il tuo servizio dovrebbe essere @Transactional, il tuo DAO dovrebbe essere @Repository e DAO usare i bean @Entity. Quindi, se dovessi usare l'implementazione primaverile di transaction manager dovresti usare il tuo servizio transazionale nel tuo controller;) è abbastanza semplice e nel tuo dao fai sessionfactory.getCurrentSession() per ottenere la sessione;)