0
votes

I have a Grails application with a bunch of domain classes, some with many fields, some of which have a hasMany relationship with the domain class in question. For this particular application I only have one "constraint", that is that every instance of every domain class must be unique. I don't care if an arbitrary field has the same value across multiple instances of the same domain class, so long as each instance is made unique by the value of some other field in that domain class. So basically I want validation to take place at a domain class instance level instead of the domain class field level. Right now I am doing that by using the very groovy @EqualsAndHashCode annotation to generate my equals and hashCode methods, then calling equals in a custom validator on some arbitrary field of a domain class.

Two questions:

  1. Is there a more efficient way of validating a domain class is unique?
  2. If no, then is there a way I can call my custom validator code on the domain class instance itself instead of going through one of the fields of the domain class instance?
@groovy.transform.EqualsAndHashCode
class MyDomainClass {
    String some
    int arbitrary
    boolean field
    static constraints = {
        // The field I chose to validate on is irrelivant, I just need to run the validation code **somewhere**
        arbitrary validator: { val, obj ->
            return !MyDomainClass.getAll().contains(obj)
         }
    }
}

I should also add I'm looking for a generic (hopefully efficient) way to do this. I realize calling getAll() is very inefficient and instead calling some variant of find or performing an HQL query on the exact fields of each domain class would be much more efficient... it just takes a lot longer to write!

Examples

    assert MyDomainClass.getAll().isEmpty() // true

    def myDomainClass1 = new MyDomainClass( some: "foo", arbitrary: 1, field: true)
    assert MyDomainClass.getAll().contains(myDomainClass1); // false
    myDomainClass1.save(flush:true)

    def myDomainClass2 = new MyDomainClass( some: "bar", arbitrary: 1, field: true)
    assert MyDomainClass.getAll().contains(myDomainClass2); // false.  Even though this has the same `arbitrary` value as myDomianClass1, it has a different `some` value which makes it unique.
    myDomainClass2.save(flush:true)

    def myDomainClass3 = new MyDomainClass( some: "foo", arbitrary: 1, field: false)
    assert MyDomainClass.getAll().contains(myDomainClass3); // false.  Even though this has the same `some` value as myDomainClass1 and the same `arbitrary` value as myDomainClass1 and myDomainClass2, it has a different `field` value which makes it unique.
    myDomainClass3.save(flush:true)
1
It's difficult to understand what you are asking. You want to know if a domain class is unique, but not at the field level. How else would you determine if it is unique other than the fields of the domain? Can you not say arbitrary unique: true ??? In this case it would help to provide the actual example you are working with.James Kleeh
@JamesKleeh If I said arbitrary unique: true it would mean that no other arbitrary field in any other instance of that MyDomainClass could have the same value. That isn't what I want. I want other instance of MyDomainClass to be able to have the exact same arbitrary value, so long as the values of other fields make the instance of the domain class unique. I will add a better example to illustrate that.ubiquibacon
So then you need to add a unique constraint that spans all of the fields. some unique: ['arbitrary', 'field']James Kleeh
@JamesKleeh If I used a multi column unique (I think that is what you are suggesting) I would have to do that for each field in MyDomainClass. Example: some(unique:['arbitrary', 'field'] then arbitrary(unique: ['some', 'field']) then field(unique:['some', 'arbitrary']). I'm pretty sure this would get me what I want (and it is definitely more efficient than how I'm doing it now), but I would like a more generic solution if possible.ubiquibacon
You would only have to put the constraint on one field. The example I showed above would ensure some, arbitrary, and field will be the unique key. All three of your examples do the exact same thing, what order and what field you put it on is....arbitrary :)James Kleeh

1 Answers

1
votes

This will ensure the combination of the 3 fields in the domain are unique. This also ensures the constraint is on the database level, instead of just application level.

class MyDomainClass {
    String some
    int arbitrary
    boolean field
    static constraints = {
        some(unique: ['arbitrary', 'field'])
    }
}