I'm using Grails 2.4.2 and have a class Contract which has many InvoiceRecipient's. The InvoiceRecipients class has an attribute invoiceType which has 2 possible values, 'O' for the invoice-original and 'C' for an invoice-copy. As you could imagine, only one record with type 'O' is allowed for the InvoiceRecipients for one contract.
If I try to implement it as in the following snipplet, the VM runs into a StackOverflow.
Another approach I tried was a service method which iterates through the recipients array of the contract to count the records with invoiceType 'O' or I tried to do a select count through InvoiceRecipient.countByContractAndInvoiceType() to determine the number of 'O's in the contract->invoiceRecipients relation in the controller.
In both last cases, Hibernate generates an update statement for my current InvoiceRecipient record, which I try to validate. And even if the validation of the current InvoiceRecipient fails and I populate the errors-object of the instance, the record is already updated (without problems, because the constraint is not coded into the class and throws no error in "save".) And I have the logical-wrong records in the database.
class Contract implements Serializable {
...
static hasMany = [recipients: InvoiceRecipient]
...
}
class InvoiceRecipient implements Serializable {
static belongsTo = [contract: Contract]
...
String invoiceType
...
static constraints = {
invoiceType nullable: false, maxLength: 1, inList: ['O', 'C'], validator: { val, obj ->
/* => This generates a StackOverflow
if (InvoiceRecipient.countByContractAndInvoiceType(obj.contract, 'O') > 1)
return "invoiceRecipient.original.max.exceed"
*/
}
}
I'd probably use something like this:
validator: { val, obj ->
if (obj.recipients.findAll{invoiceType == 'O'}.size() > 1)
return "invoiceRecipient.original.max.exceed"
This way you should be able to prevent Hibernate from trying to flush dirty objects and in the process revalidate this object.