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