grails-data-mapping
grails-data-mapping copied to clipboard
Unique constraint defined in child classes for inherited field is applied globally
The detailed functionality (especially in edge cases dealing with inheritance) of unique constraints has changed back and forth over the last couple of years. As of 6.1.11, this behavior has changed again.
Task List
- [x] Steps to reproduce provided
- [x] Stacktrace (if present) provided
- [x] Example that reproduces the problem uploaded to Github
- [x] Full description of the issue provided (see below)
Steps to Reproduce
- Create a parent domain class with at least one field (e.g.,
name
) - Create 2 child domain classes that each define a unique constraint for the inherited name field
- Create an instance of each child class and set their name fields to the same value
- Persist both instances
Here's an example of what the domains might look like:
class Parent {
String name
}
class Child1 extends Parent {
static constraints = {
name unique: true
}
}
class Child2 extends Parent {
static constraints = {
name unique: true
}
}
And the unexpected behavior:
def child1 = new Child1(name: 'test').save()
def child2 = new Child2(name: 'test').save() // validation fails
Expected Behaviour
Although the name
field is declared in the parent class, the unique constraints are defined in the child classes, so one would expect that only duplicate values within the same child class would cause validation to fail. When searching for duplicate instances, it shouldn't matter where the field is declared, only where the constraint is defined. This is how things have worked as of late up until 6.1.11.
Actual Behaviour
In reality, the unique constraint finds the class that declares the constrained property and assumes that that class is also the one that defines the constraint. After persisting an instance of one child class, an instance of the other child class with the same name
value fails validation. This would be the expected behavior if the constraint was also defined in the parent class, but that's not the case.
Unfortunately, the fix for a recent issue caused this issue to arise. I can see how this is a tricky problem. Constraints should have the ability to be inherited, but i don't believe that we should assume that the class that declares the field also defines the constraint. Currently, a constraint only has a reference to the constraint owning class, but not to the constraint defining class. If there was a reference to the constraint defining class, we could check if the constraint defining class and the class that declares the field are the same before automatically using the class that declares the field in the unique constraint criteria.
I'd be happy to work on a pull request but wanted to get some feedback first.
Environment Information
- Operating System: MacOS
- GORM Version: 6.1.11
- Grails Version (if using Grails): 3.3.8
- JDK Version: 1.8
Example Application
https://github.com/dpcasady/unique-constraint-inheritance
Any thoughts on this? Like I said, I'd be happy to attempt a fix if you think it's worthwhile.