Technipelago AB

Technipelago Blog Stuff that we learned...


Invoke JPA lifecycle events in Grails

Publicerad den 24 Sep 2008

If you use JPA instead of GORM, Grails will not invoke JPA lifecycle events automatically, i.e. @PrePersist, @PreUpdate, @PostRemove, etc.

That was a requirement in my project and I don’t know if this will be fixed in future versions of Grails. An issue exists that are targeted for Grails 1.1 (http://jira.codehaus.org/browse/GRAILS-2094)

However I solved it with a Hibernate event listener. Now I can annotate my JPA entities as usual and have Grails call them at the right time. The same entities are used by other Java Enterprise components in Glassfish so it’s good to have this work transparently from both sides.

Here’s the code:


package my.package;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import org.hibernate.event.PreInsertEvent;
import org.hibernate.event.PreInsertEventListener;
import org.hibernate.event.PreUpdateEvent;
import org.hibernate.event.PreUpdateEventListener;

/**
 * If you use JPA instead of GORM, grails will not invoke
 * JPA lifecycle events automatically.
 * This event listener will bridge the gap.
 * It will listen to Hibernate events and invoke
 * corresponding JPA lifecycle events,
 * i.e. @PreInsert, @PreUpdate, @PostRemove, etc.
 * Put this code in BootStrap.groovy
 * to install the listener
 * (modify/duplicate for each event).
 *
 * def init = { servletContext ->
 * def mel = new my.package.MyEventListener()
 * def ctx = WebApplicationContextUtils.
 *     getRequiredWebApplicationContext(servletContext)
 * def sessionFactory  = ctx.getBean('sessionFactory')
 * EventListeners eventListeners = sessionFactory.eventListeners
 * // PreInsert
 * def listeners = eventListeners.preInsertEventListeners
 * listeners = Arrays.copyOf(listeners, listeners.length + 1)
 * listeners[-1] = mel
 * eventListeners.preInsertEventListeners = listeners
 * // PreUpdate
 * listeners = eventListeners.preUpdateEventListeners
 * listeners = Arrays.copyOf(listeners, listeners.length + 1)
 * listeners[-1] = mel
 * eventListeners.preUpdateEventListeners = listeners
 * }
 *
 */
public class MyEventListener implements PreInsertEventListener, PreUpdateEventListener {
    /**
     * Invoke JPA entity methods annotated with @PreInsert.
     * @param event the Hibernate event
     * @see javax.persistence.PrePersist
     * @return normally false, true if method cannot be invoked.
     */
    public boolean onPreInsert(final PreInsertEvent event) {
        Object m = event.getEntity();
        boolean veto = false;
        for (Method meth : m.getClass().getDeclaredMethods()) {
            if (meth.getAnnotation(PrePersist.class) != null) {
                try {
                    meth.invoke(m);
                } catch (Exception ex) {
                    Logger.getLogger(MyEventListener.class.getName()).log(Level.SEVERE, null, ex);
                    veto = true;
                }
            }
        }
        return veto;
    }

    /**
     * Invoke JPA entity methods annotated with @PreUpdate.
     * @param event the Hibernate event
     * @see javax.persistence.PreUpdate
     * @return normally false, true if method cannot be invoked.
     */
    public boolean onPreUpdate(final PreUpdateEvent event) {
        Object m = event.getEntity();
        boolean veto = false;
        for (Method meth : m.getClass().getDeclaredMethods()) {
            if (meth.getAnnotation(PreUpdate.class) != null) {
                try {
                    meth.invoke(m);
                } catch (Exception ex) {
                    Logger.getLogger(MyEventListener.class.getName()).log(Level.SEVERE, null, ex);
                    veto = true;
                }
            }
        }
        return veto;
    }
}

Tags: grails jpa


« Back