hibernategwtrequestfactory

Requestfactory hibernate session-per-request


I am trying to update my application to use a session-per-request pattern so I can move to newer GWT versions (my entities don't save correctly past 2.4 - GWT >2.4 RequestFactory not saving child object changes

I have implemented a request filter, and it appeared to be working correctly - I can pull data down to the client no problem. However, when I try to save an entity, it errors out because it does not find an active transaction:

org.hibernate.HibernateException: saveOrUpdate is not valid without active transaction

I pulled most of the info on how to implement this pattern from https://developer.jboss.org/wiki/OpenSessionInView . Here's my filter:

public class HibernateSessionRequestFilter implements Filter {  

private static Log log = LogFactory.getLog(HibernateSessionRequestFilter.class);  

private SessionFactory sf;  

public void doFilter(ServletRequest request,  
                     ServletResponse response,  
                     FilterChain chain)  
        throws IOException, ServletException {  

    try {  
        System.out.println("Starting a database transaction");  
        sf.getCurrentSession().beginTransaction();  

        // Call the next filter (continue request processing)  
        chain.doFilter(request, response);  

        // Commit and cleanup  
        System.out.println("Committing the database transaction");  
        sf.getCurrentSession().getTransaction().commit();  

    } catch (StaleObjectStateException staleEx) {  
        log.error("This interceptor does not implement optimistic concurrency control!");  
        log.error("Your application will not work until you add compensation actions!");  
        // Rollback, close everything, possibly compensate for any permanent changes  
        // during the conversation, and finally restart business conversation. Maybe  
        // give the user of the application a chance to merge some of his work with  
        // fresh data... what you do here depends on your applications design.  
        throw staleEx;  
    } catch (Throwable ex) {  
        // Rollback only  
        ex.printStackTrace();  
        try {  
            if (sf.getCurrentSession().getTransaction().isActive()) {  
                System.out.println("Trying to rollback database transaction after exception");  
                sf.getCurrentSession().getTransaction().rollback();  
            }  
        } catch (Throwable rbEx) {  
            log.error("Could not rollback transaction after exception!", rbEx);  
        }  

        // Let others handle it... maybe another interceptor for exceptions?  
        throw new ServletException(ex);  
    }  
}  

public void init(FilterConfig filterConfig) throws ServletException {  
    System.out.println("Initializing filter...");  
    System.out.println("Obtaining SessionFactory from static HibernateUtil singleton");  
    sf = HibernateUtil.getSessionFactory();  
}  

public void destroy() {}  

}  

My web.xml:

<servlet>
    <servlet-name>requestFactoryServlet</servlet-name>
    <servlet-class>com.example.server.util.ExampleRequestFactoryServlet</servlet-class>
    </servlet>

  <servlet-mapping>
    <servlet-name>requestFactoryServlet</servlet-name>
    <url-pattern>/gwtRequest</url-pattern>
  </servlet-mapping>

    <filter>  
      <filter-name>HibernateFilter</filter-name>  
      <filter-class>com.example.server.util.HibernateSessionRequestFilter</filter-class>  
    </filter>  

    <filter-mapping>  
      <filter-name>HibernateFilter</filter-name>  
      <url-pattern>/gwtRequest</url-pattern>  
    </filter-mapping> 

A save is very straightforward:

// client
private void saveScale(ScaleProxy scale) {
    scaleRequest.save(scale)
    .fire(new Receiver<Void>() {
        @Override
        public void onSuccess(Void response) {
            Window.alert("Scale saved.");
        }           
    });
}

// server
public static void save(Scale scale) {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();

    session.saveOrUpdate(scale);
}

Any other info I can provide? I appreciate any ideas or insight!


Solution

  • You need one session per request, but not one single transaction. Open and close transactions in each service method, for proper behavior in case if error.