Wednesday, October 2, 2013

Grails Hibernate - null id in entry (don't flush the Session after an exception occurs)

I ran into this issue today when trying to persist one of my objects.  The cause of the problem was interesting.  I was trying to save an object when a property/columns in the table had a unique constraint.  As a result, the object that I was trying to persist would not persist simply because the object's property it failed to meet the unique constraint.

As a result, a call to Save() on the object failed and the ID on the object I was trying to save was not set, but Grails Hibernate still processed the object and associated it with its persistence mechanism leaving it in a "semi-persistent" state with the Grails Hibernate persistence manager (ie: Grails Hibernate now knows about the object you tried to save and it SHOULD have fully evicted the object from its persistence manager because the save failed, but it didn't).

So, the solution that I implemented was to wrap the Save() in a try{} catch{} statement and handling exception.

Basically you need to do something like this:
Create a Filters in conf directory under grails project suppose named 'BaseFilters.groovy' with the following content:
 
import org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.hibernate.FlushMode
import org.hibernate.Session
import org.hibernate.SessionFactory

class BaseFilters {
    private static GrailsWebApplicationContext ctx;

    def filters = {
        all(controller:'*', action:'*') {
            before = {
                Session session = getRequestSession();
                session.setFlushMode(FlushMode.MANUAL)
                request.requestSession = session;
            }
            after = { Map model ->
                try {
                    Session session = request.requestSession;
                    session.flush();
                } catch (Exception ex) {
                    ex.printStackTrace()
                }
            }
            afterView = { Exception e ->

            }
        }
    }

    private Session getRequestSession() {
        createCtx();
        SessionFactory sessionFactory = ctx.sessionFactory
        Session session = sessionFactory.currentSession
        return session;
    }

    private def createCtx() {
        if(!ctx) {
            ctx = ServletContextHolder.servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
        }
    }
}

Originally session flush mode initially set to 'FlushMode.AUTO' which cause the error.
So I change the session flush mode to 'FlushMode' to 'MANUAL'.
And also make your service transaction false such: static transactional = false, add this line just below the class definition.

And in the after section, I just manually try to flush, which is not much needed, you can ignore it.
I just add this section to do some logic manually if need.

No comments:

Post a Comment