Groovy’s Collection.minus(Collection)

If you’ve done much Groovy development, you’ve probably used all the handy helper methods that it provides to augment the core JRE classes.  Collections, in particular, have some really helpful additions in the form of operator extension.  For example, the minus operator works like this:

assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]

This is all sorts of handy, but I found an interesting bug in Groovy 1.8.4′s implementation.  Behind the scenes, it uses two different Comparators to do the equality checks needed to perform the subtraction.  However, the Comparators are a little off…

The first Comparator is actually just the DefaultTypeTransformation.compareTo method, and it gracefully handles non-Comparable, non-equal objects with the same hashCode.  It also handles nulls (which are always first), and a bunch of standard type conversions (e.g., char -> String when compared to a String).

The second Comparator, however, it less well behaved.  It’s called NumberAwareComparator, and delegates to DefaultTypeTransformation.compareTo first.  But if that fails, it falls back to a hashCode comparison, which creates a very interesting bug: if two non-equal objects have the same hashCode, NumberAwareComparator will return zero from it’s compareTo method, which will be interpreted as equality in a variety of contexts.

The workaround that I’ve found is to explicitly implement Comparable in the domain model at the appropriate level(s) so that you always have a mutually-comparable collection, and you can implement compareTo in a way consistent with equals.  Implementing Comparable like this will ensure that DefaultTypeTransformation.compareTo will successfully handle the comparison, thus preventing falling through to NumberAwareComparator.

Sometimes, it is the library.  : )

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
    }
  }
}