Grails Tip # 6: Service layer transitions

On my last Grails project, as good Grails developers, we faithfully put core application logic inside various service methods. Delightfully, the benefits of doing so paid off and we began re-using services though out the application.

One day, while using our application on a fairly complex set of test data (both in volume and the fact our domain model graph is complex), we stumbled on a huge performance problem. After profiling the controller-action, the problem was traced down to a call to a service method. Simplified, it look like:

class ArticlesController {
   def service

   def handleAction = {
      // stuff ...
      // more stuff ...
      for(int i=0;i < loopLimit;i++){
         service.serviceMethod(someParameters)
      }
      render(view: "article"...)
   }
}

This was a commonly re-used service method, but the interesting part of this method was the fact it was being called within a loop, a loop that could potentially be called several hundred times. The looping itself is not a problem (nor could it be avoided).

By trial and error, we discovered that the actual call to the serviceMethod() was causing the performance issue. If we
1) Simply removed the service method and embedded the application logic in the service method into the loop or
2) Made the service class non-transactional

The performance issue disappeared.  We realized that the under-the-hood work that goes on to setup the transactional service boundry, when there is a complex or large volume of data in the Hibernate session, causes a dramatic perfomrance impact when we repeadly cross the controller- service layer boundry.

The simplest and cleanest solution was to loop inside the service method, thus the application cross the boundary only once. This has the side effect that any errors will roll-back the entire set of data and not just the single record being updated but we were ok with this.

class ArticleService {
   static transactional = true
   def serviceMethod(def params){
      // do stuff..
   }

   def serviceMethodMultiple(def params){
      for(int i=0; i < loopLimit; i++){
         serviceMethod(params)
      }
   }
}

And in the controller:

class ArticlesController {
   def service

   def handleAction = {
      // stuff ...
      // more stuff ...
      service.serviceMethodMultiple(someParameters)
      render(view: "article"...)
   }
}

From the standpoint of a intermediate Grails developer, this isn’t a problem that could have been detected beforehand since it only manifests itself when there is a complex domain model graph already in the session.

We chalked this up as a life lesson. The final solution, avoid any code that may repeatedly crosses into the service layer, especially for services marked as transactional. Instead, create a new method that does the looping inside the service boundary. Identification and avoidance was the best action forward as we were in no position to fix any potential underlying issue with the framework (if there even was any).

Grails Tip # 4: java.lang.Boolean.call()

No signature of method: java.lang.Boolean.call()

As specified by , Grails provides convenience method to check if a object contains errors and set the CSS class on the element.

I came across this problem where suddenly, under some conditions, I was seeing the following error:

No signature of method: java.lang.Boolean.call()

Further down the stacktrace, I notice something more interesting.

Caused by: groovy.lang.MissingMethodException: No signature of method: java.lang.Boolean.call() is applicable for argument types: (java.util.LinkedHashMap, java.lang.String) values: [[bean:{}, field:asc], input-error]Possible solutions: wait(), any(), dump(), wait(long), wait(long, int), and(java.lang.Boolean)

At first glance, the stacktrace looks like complicated garbage. However, after a few minutes of looking though the stacktrace, the parameter’s in the exception cause looked suspiciously like those specified in a invocation of the GSP’s hasErrors method.

I soon realized that this exception only occurs during situations where errors would be displayed on the GSP. Looking though the code flow, I soon realized something suspicious.

		def save = {
			.
			.
			.
			
			command.validate()
			if (command.hasErrors()){
				render (view:input, model:[command:command, hasErrors:true])
				return
			}
		}
	

Notice that the if the command object has errors after validation, the model being returned to generate the GSP contains both the command and a variable hasError which is assigned to true.

I ran a quit test, what happens if try and execute the value of a boolean as a method.

		true()
	

And volila, a No signature of method: java.lang.Boolean.call() error. It appears, when there is a variable in the model with the same name as a GSP method, under some conditions, Grails replaces the method invocation with the value of the variable and then it executes the method.

In this example, it appears that hasErrors(…) was being replaced by the value of the hasError variable, true, and then being executed. Thus the final statement looked like the following: true(bean:command, …) This of course, would result in a no method signature found error.

After changing the name of the hasError variable to something else, say, showErrors, the GSP rendered as expected. By reading this article, I hope that you can save yourself a couple of hours of struggle.

Grails Tip # 3: refresh()


refresh() is not an undo()

In your application, you may come across a time when you need to revert or undo
changes done on a domain object. discard() comes to mind, but there may be instances where
that is not an option. refresh() also looks like an tantalizing option, but there is one critical
feature of refresh() you need to be aware:

If you have a trasient object associated to the domain object you are calling
refresh() on, you will get a
org.hibernate.AssertionFailure: null identifier
error.

Consider the following example to see why you might need to use refresh
and what you have to watch out for.

The Domain

Consider the following domain, a Car has several models
(for each year) and most of the details of a “car” is
in the model domain. There are a few description fields on
the Car domain object however.

The Code

The Car domain object:

		package com.ex
			
		class Car {
			String name
			String description
			static hasMany = [models:Model]
		}
	

The Model domain object:

		package com.ex
			
		class Model {
			String name
			int year
			Engine engine
			. (other properties)
			.
			.
			static belongsTo = [car:Car]
		}
	

Users are able to modify car details and model details at the same time.
They can also add new models to the car as well. All changes to the
Car (and it’s models) must be atomic. That is, all changes
must be committed as one transaction. Lets take a
look of the UI:

<%= image_tag "articles/grailstip3/flow.png" %>

When the user decides to change their changes,
they exectue the saveChanges action on CarController.

Because of the belongsTo relationship on Models to Cars, we
can save any user changes to Car model and it’s models by
calling save() on the Car object.

		package com.ex
			
		class CarController {
			def saveChanges = {
				.
				.
				.
				// save car under edit
				car.save(failOnError:true)
				.
				.
				.
			}
		}
	

The Problem

Due to a requirement, the users have the ability
to revert changes on a particular Model, but
still save their changes on the Car.

		package com.ex
			
		class CarController {
			def saveChanges = {
				.
				.
				.
				// model to revert
				if (some condition)
					// rever model
				.
				.
				.
				// save car under edit
				car.save(failOnError:true)
				.
				.
				.
			}
		}
	

One might think we can call discard(), but this will
not work in this situation because further down
the code, we end up calling save on Model’s parent,
the Car. Even if we discard a particular Model of a Car,
it will still be saved when Car is saved due to the
cascade behaviour brought on by belongsTo relationship.

We also are unable to change the cascade settings between
Car and Model. In this particular instance, we could
prevent cascade on save, but in other cases, we do need it.

The other other option is to call refresh() on the Model
being reverted. And we do this.

		package com.ex
			
		class CarController {
			def saveChanges = {
				.
				.
				.
				// model to revert
				if (some condition)
					model.refresh()
				.
				.
				.
				// save car under edit
				car.save(failOnError:true)
				.
				.
				.
			}
		}
	

Done right? well, not quite. Under some use cases, we
discover this exception being thrown:
org.hibernate.AssertionFailure: null identifier

The error only occurs when new objects are associated to
a Model. For example, if the user adds a new Engine to
the reverted Model. What’s going on?

When you call refresh on the Model, Hibernate will traverse
though the object’s object graph and overwrite its data
with what is in the database.

If there is a transient object associated to the Model,
Hibernate will not find an id associated with this entity
and thus will throw a null identifier assertion failure.

The Solution

To be able to use the refresh() method, you must break all
associations with transient objects in the graph in the
current Hibernate session.

This can be done by calling clear() on any hasMany associations
and re-assigning any variables assigned to transient objects to null.

		package com.ex
			
		class CarController {
			def saveChanges = {
				.
				.
				.
				// model to revert
				if (some condition){
					model.engine = null
					model.tries.clear()
					model.refresh()
				}
				.
				.
				.
				// save car under edit
				car.save(failOnError:true)
				.
				.
				.
			}
		}
	

Many people believe this to be hackish — a workaround
and, well, though this might be true, Hibernate’s API is being
used correctly and instances such as this, transient objects
are known and can be properly be removed from the session.

Though Grails itself does not offer an “undo” method, I
would argue the solution above deals with the problem at hand
using the API as it was designed. However, one of the reasons
I’ve posted this is to see if there are any others who have faced
similar problems have found a different way of solving it.

Grails Tip # 2: Hibernate Session


A different object with the same identifier value was already associated with the session

The Domain

Say we are managing a store with shelves full of items. Whenever an
item on a shelf is empty, we need to create a restock order form.
Lets represent this as 3 domain classes: Shelf, Item, RestockOrder

The Code

A restock order is created when an existing item on a shelf runs out,
BUT, a order is also created for brand new items. The client has requested
that when creating new items, restock orders can also be created.

The Shelf domain object:

			package com.ex
			
			class Shelf {
				int number
				static hasMany = [items:Item, restockOrders:RestockOrder]
			}
		

The Item domain object:

			package com.ex
			
			class Item {
				String name
				String description
				double price
			}
		

The RestockOrder domain object:

			package com.ex
			
			Item item
			static belongsTo =[shelf:Shelf]
			static mapping = {
				item cascade:'save-update'
			}	
		

The editing of a Shelf takes place over several pages, similar
to a web-flow, thus the Shelf object is created and stored in
the HTTP session (therefore detached from the Hibernate session).
On subsequent HTTP requests, the user adds more data to this
object in the session. Finally, after filling out the last
page, we are able to persist the domain object to the session.

On the last page, if the user made important changes to Shelf,
the user is showing a confirmation page comparing the old values
with the current one. This was done by simply populating
the model with the current version in the session and also
re-loading the same model from the database. If the user did not
make any changes that require confirmation, we save right away.

<%= image_tag "articles/grailstip2/flow.png" %>
In ShelfController:
The index() method loads the required shelf and puts it into the
HTTP session, detaching it from Hibernate session.

		    def index = {	
				Shelf s = Shelf.findByNumber(1)
				session.putValue("shelf", s)
			}
		

The save controller

			def save = {
				// get from sesion
				def shelfUnderEdit = session.getAt("shelf")
				
				// bind update
				shelfUnderEdit.parameters = params
				
				Shelf originalShelf = Shelf.findByNumber(1)
				
				// user hasn't confirmed, might have to show confirmation page.
				if (showConfirmation(shelf, original) && params.noComfirmation){
					render("showUpdates", model:[shelfUnderEdit:shelfUnderEdit, originalShelf:originalShelf])
					return
				}
				
				// save
				shelfUnderEdit.save(failOnError:true)
			}
		

However, when we try to run the code above, we get the
“A different object with the same identifier value was already associated with the session”
error.

The Problem

Hibernate tries to keep one instance of a persisted object
in the session at one time. In this example, for any “Shelf”
in the database, Hibernate tries to make sure only 1 instance
of a Shelf object represents that Shelf.

The problem resides in the save(), there is the original Shelf we
detached and put into the HTTP session. When we try to re-attach
it, Hibernate finds that another one is found in the session.
This second object was created when we loaded the same Shelf from
the database to do our comparison. Two objects representing the
same Shelf now resides in the session and much to the dislike to
Hibernate.

The Solution

Though there are several ways to solve this problem, the method
we took was to discard the re-load Shelf object that was used
for the comparison. Therefore, when save() is called on the
Shelf that is under edit, it is reattached to the Hibernate
session and is the only object representing that particular
Shelf in the database. Thus Hibernate is able to persist the
changes without error.

			def save = {
				// get from sesion
				def shelfUnderEdit = session.getAt("shelf")
				
				// bind update
				shelfUnderEdit.parameters = params
				
				Shelf originalShelf = Shelf.findByNumber(1)
				
				// user hasn't confirmed, might have to show confirmation page.
				if (showConfirmation(shelf, original) && params.noComfirmation){
					render("showUpdates", model:[shelfUnderEdit:shelfUnderEdit, originalShelf:originalShelf])
					return
				}
				
				originalShelf.discard()
				
				// save
				shelfUnderEdit.save(failOnError:true)
			}
		

References

Grails Tip # 1: Transient Values


Not Null Property References A Null Or Transient Value

Introduction

I recently started working on Grails applications and as expected, I’ve seen the same problems come up over and over again. So, I thought it would be a good idea to accumulate the common (and some uncommon) problems we”ve seen and write about them. The domain objects and code used in the posts are purpose built examples simulating the actual problems that we’ve seen developing with Grails.

The goal is not to explain the full underlying details of GORM and Hibernate, but instead, show examples of what caused issues for me and hopefully can save you time in solving your problems. Here we go with tip number 1:

org.hibernate.PropertyValueException: not-null property references a null or transient value

Example 1

The Domain

Say we are managing a store with shelves full of items. Whenever an item on a shelf is empty, we need to create a restock order form. Lets represent this as 3 domain classes: Shelf, Item, RestockOrder

The Code

A restock order is created when an existing item on a shelf runs out, BUT, a order is also created for brand new items. The client has requested that when creating new items, restock orders can also be created.

The Shelf domain object:

		package com.ex
		
		class Shelf {
			int number
			static hasMany = [items:Item]
		}
	

The Item domain object:

		package com.ex
		
		class Item {
			String name
			String description
			double price
		}
	

The RestockOrder domain object:

		package com.ex
		
		class RestockOrder {	
			Item item
			Shelf shelf
		}
	

A Shelf has a list of items and is represented by the hasMnay relationship to Items. A restock order needs an Item, it just doesn’t make sense without one. But an Item does not belong to a restock order. Thus, we do not have a belongsTo RestockOrder relationship on Item. If a restock order is deleted, we certainly do not want to delete the Item record.

The Problem

Somewhere in our RestockController, we have the following code. It creates an instance of a RestockOrder and Item. It assigns the item to the restock order.

		def createOrder = {
				.
				.
				.
				RestockOrder ord = new RestockOrder()
				ord.shelf = currentShelf
				ord.item =  new Item(name:name, description:desc, price:price)
				ord.save(failOnError:true)
			}
		}
	

When the RestockOrder is saved, we get the following error:

          not-null property references a null or transient value: com.ex.RestockOrder.item

and the only way to resolve it is to create AND save an instance of Item and then assign it to RestockOrder. This might be confusing because, you can create and assign an instance of Item to a Shelf, like the following:

			Shelf shelf = new Shelf(number:1)
			shelf.addToItems(new Item(name:name, description:desc, price:price))
			shelf.save(failOnError:true)
		

without any error.

The Solution

A transient object is an object that has been instantiated but is not yet associated to the Hibernate session. When you associate this object to a property of a object that is associated to the session and try to save, Hibernate will complain and give you the
“Not Null Property References A Null Or Transient Value error”

This problem has to do with how GROM (Hibernate) cascades saves. Saves are automatically cascaded from parent to child when there is a hasMany relationship. This is why you can add a new Item to Shelf and save the shelf without explicitly saving the Item first. The save cascades from Shelf to Item via the hasMany relationship.

With RestockOrder, there is no such relationship and Hibernate will not do the cascading automatically. You have two solutions:

1 ) Explicitly save the Item before assigning.

		Item itm = new Item(name:name, description:desc, price:price)
		itm.save(failOnError:true)
		
		RestockOrder ord = new RestockOrder()
		ord.shelf = currentShelf
		ord.item =  itm
		ord.save(failOnError:true)
	

2) Configure the cascading behavior for Item in RestockOrder.

Here we set a save-update cascading behaviour.

		package com.ex
		
		class RestockOrder {	
			Item item
			Shelf shelf
		}
		
		static mapping = {
			item cascade:'save-update'
		}
	

Conclusion

Though this error is relatively simple (especially for this example) problem, it is very problem and can be difficult to track down as your domain model grows. Thus it is extremely helpful to have a understanding when these errors occur so you know what to look for in your application.


Example 2


The Domain

In this example, we’ll use the same domain as in Example 1. The domain objects are slightly diffrent however.

The Code


We’ll use the same domain objects as Example 1, but with slight modifications:
The Shelf domain object:

		package com.ex
		
		class Shelf {
			int number
			static hasMany = [items:Item, restockOrders:RestockOrder]
		}
	

The Item domain object:

		package com.ex
		
		class Item {
			String name
			String description
			double price
		}
	

The RestockOrder domain object:

		package com.ex
		
		Item item
		static belongsTo =[shelf:Shelf]
		static mapping = {
			item cascade:'save-update'
		}	
	

The Problem


We need to allow the user to create a new shelf but during this process we also allow them to assign restock orders to the newly created shelf. To do this, we create a new RestockOrder domain object and assign it’s parent as the newly created shelf.

Somewhere in a controller we have the following code:

		RestockOrder ord = new RestockOrder()
		ord.shelf = newShelf
		ord.item =  new Item(name:name, description:desc, price:price)
		
		newShelf.save(failOnError:true)
	

The problem is, when you run it, you get the “org.hibernate.PropertyValueException” error. This is because at the time of saving the RestockOrder, the instance of Shelf is still transient.

You get a similar exception if you try and add to the list of RestockOrder’s within a Shelf by adding directly to the Shelf’s restockOrders list:

		def currentShelf = Shelf.findByNumber(1)
		RestockOrder ord = new RestockOrder()
		ord.item =  Item.findByName("a")
		currentShelf.restockOrders.add(ord)
		currentShelf.save(failOnError:true)
	

instead of using Shelf’s addToRestockOrder() method. By using native Collection methods to add to the list, Grails will not automatically set the parent on the child entity.

The Solution

To resolve this, instead of assigning the parent of a child, you can use the addTo* method on the parent to add the Item as a child of the parent.

When you add the hasMany relationship, Grails provides an addTo* method. In the example above, you will have a addToRestockOrder() method which accepts a RestockOrder instance. This relationship automatically cascades saves from the parent to child.

  • You do not need to call save on the child.
  • It also updates the child’s parent reference.

Conclusion

In this example, I showed how you can use the addTo* method to avoid some common Grails errors, but as with all other programming problems, there are always several ways to solve the issue.

So, hopefully after reading this article, you have a slightly better understanding why these errors appeared in your code by looking at how it was caused on ours. I will continue to update this article if I find any other notable or interesting causes to this particular error.

References