Hql For Groovier Hibernate

If you’ve done much with Groovy, you probably know about the slick Sql class that it provides for interfacing with a SQL datastore across JDBC. It’s not perfect, but it’s certainly easy.  I was working on a Hibernate project and wanted the same thing for my HQL queries, so I threw together a simple Hql class to provide the same sort of behaviour.

Like Sql, it can be parameterized with a connection source (a SessionFactory in this case) or a specific connection (a Session).  If given a SessionFactory, it’ll use getCurrentSession() to grab a Session when one is needed, which allows you to wire a single Hql instance up in your Spring config, supply it your SessionFactory, and then inject the Hql into all your persistence-aware beans and just use it.

It also provides a public getSession() method for returning the current session to those same beans, which means you can just inject the Hql instance and not have to also inject the SessionFactory instance if you’re feeling lazy (or doing some quick prototyping).  I will leave whether that’s an appropriate long-term approach for a different discussion.

Here she be:

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

/**
 * I aim to be sort of like Groovy's builtin Sql class, except for Hql
 * @author bboisvert@gmail.com
 *
 */
class Hql {

  private Session __sess
  private SessionFactory __sf

  def Hql(SessionFactory sf) {
    __sf = sf
  }

  def Hql(Session s) {
    __sess = s
  }

  SessionFactory getSessionFactory() {
    __sf
  }

  Session getSession() {
    __sess ?: __sf.currentSession
  }

  Object unique(String hql, List params=null) {
    new Statement(hql, params).query(session).uniqueResult()
  }

  Object unique(GString gstring) {
    new Statement(gstring).query(session).uniqueResult()
  }

  List list(String hql, List params=[], Integer firstResult=null, Integer maxResults=null) {
    new Statement(hql, params, firstResult, maxResults).query(session).list()
  }

  List list(GString gstring, Integer firstResult=null, Integer maxResults=null) {
    new Statement(gstring, firstResult, maxResults).query(session).list()
  }

  private class Statement {
    String hql
    List params
    Integer firstResult
    Integer maxResults

    def Statement(String hql, List params, Integer firstResult=null, Integer maxResults=null) {
      this.hql = hql
      this.params = params ?: []
      this.firstResult = firstResult
      this.maxResults = maxResults
    }

    def Statement(GString gstring, Integer firstResult=null, Integer maxResults=null) {
      def hql = new StringBuilder()
      def params = []
      def prefix
      gstring.strings.eachWithIndex { str, i ->
        if (i > 0) {
          def v = gstring.getValue(i - 1)
          if (v != null) {
            hql.append(prefix).append("?")
            params += v
          } else {
            if (prefix.trim().endsWith('=')) {
              prefix = prefix.replaceFirst(/=(\s*)$/, ' is$1')
            }
            hql.append(prefix).append("null")
          }
        }
        prefix = str
      }
      hql.append(gstring.strings.last())
      hql = hql.toString()
      this.hql = hql
      this.params = params
      this.firstResult = firstResult
      this.maxResults = maxResults
    }

    Query query(Session sess) {
      def q = sess.createQuery(hql)
      params.eachWithIndex { o, i ->
        q.setParameter(i, o)
      }
      if (firstResult != null) {
        q.firstResult = firstResult
      }
      if (maxResults != null) {
        q.maxResults = maxResults
      }
      q
    }
  }
}