Grails – Hibernate-safe domain class property editor

July 22, 2014 at 08:00

Grails automatic databinding is super powerful, but, at least in versions 2.2.x and below, is rather obscure and not very-well documented.

Sometimes is not clear how a form input should be named depending on what you want to bind. An example, for a given domain class User and a property roles (that is an association with another to another domain class, Role), where sometimes you need to put roles as the input name (usually when the property is a collection), some other times (usually when the property is univaluated), or even roles[0].id (usually when the property is a sorted collection). Note the use of “usually” here, as sometimes more than one way (sort of) work.

I’ve found that sometimes, for the first case (the input name not having id at the end), you might run into problems when the binding itself is done: since the input name is roles, Grails/Spring identifies the property type as an instance of the domain class Role, but the supplied value is a Long (or its string representation) – concretely, the id of the domain class that we want to associate to the property roles Grails complains about it by saying:

Failed to convert property value of type java.lang.String to required type java.util.Set for property roles; nested exception is java.lang.IllegalStateException: Cannot convert value of type java.lang.String to required type Role for property roles[0]: no matching editors or conversion strategy found

One solution for this is to provide a custom property editor, a feature that, as many others, is not documented by Grails itself, since is directly inherited by Spring (section “Registering additional custom PropertyEditors”).

My first approach for this was to create a generic property editor that could work with any domain class and any property to identify it. The result was the following:

With that approach, you have a class DomainClassLookupPropertyEditor that extends PropertyEditorSupport and that, when given a text to convert it into a domain class instance, searches the existing instance in the database using the declared property. The search is done in a new session to prevent automatic flushing in the middle of the databinding (by default, Grails configures Hibernate in a way that a flush will be done when performing a query).

The second class needs to be registered as a bean (by adding myEditorRegistrar(MyPropertyEditorRegistrar) in resources.groovy ), and on startup it will register a property editor for each domain class declared in the domainClassByPropertyCustomBinders map (where the key of each entry would be the domain class, and the value the property used as a value in the form input).

This approach worked for me for a while, until I ran into a problem I never had before: sometimes, when modifying an existing property by removing some of the previous values (but not all), Grails would throw the following exception:

org.springframework.dao.DuplicateKeyException: a different object with the same identifier value was already associated with the session; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session

It was more or less clear what was going on: the object retrieved by the searchValueInNewSession method was already in the current Hibernate session, so when trying to put one coming from a different session, Hibernate complained about it already been there.

I tried different approaches, such as trying to merge the object coming from the new session, or trying to read the object in the existing with the method load (that theoretically would prevent the flush), but I always ended up with an exception similar to:

org.hibernate.AssertionFailure: collection was not processed by flush()

I think that is the only Hibernate related exception I’ve run into when using Grails that I can’t still properly explain 😳 . I mean, I understand what Hibernate is trying to say, but I don’t know why is triggered). Still, my thoughts were that somehow the object that I retrieved from the existing session had already been part of a flushed session, and that affected only its collections.

In any case, after playing with it a bit I managed to make it work by using the following strategy:

  1. Search the existing object in the current session
  2. If the object is found, use its id to get a new instance (so is not tainted by previous sessions) of it that is lazingly loaded using load (so it doesn’t trigger a flush in the current session)
  3. If is not found, retrieve the instance in a new session (as before)

The full code would then look as the following:

The part where the object is searched in the existing session is a bit cryptic, but is based on a code that I have used before (see Grails – how to see what objets are in Hibernate session) and was already in my always-growing GrailsUtils class 🙂 . So far it has worked properly in all scenarios (single / multiple, directional/unidirectional, mandatory/optional relationships), but I wouldn’t be surprised if I ran into more problems in the future 🙁 . If that’s the case, I’ll update this post with my findings 🙄 .