I have the following association set up:
class Applicant {
String appStepsComplete
String name
String eNumber
String email
String homePhone
String cellPhone
String address
Integer age
static hasMany = [scholarships:Scholarship]
static mapping = {
scholarships joinTable: [name:"APPLICANT_SCHOLARSHIPS"]
}
}
class Scholarship {
String fundCode
String seqNo
String name
static belongsTo = Applicant
}
When I am calling this it is allowing for duplicates to be added to the database:
applicant.scholarships << schol
applicant.save()
I need it to prevent duplicates in the database. I tried setting a unique constraint on scholarships in applicant by doing the following, but it didn't work:
static constraints = {
scholarships(unique:true)
}
Burt Beckwith's comment is right, you need to override hashCode and equals on Scholarship. Assuming the business key (the combination of fields that uniquely identify this entity, which you'd use as a composite natural key if the database wasn't using an artificial id) for this is a combination of fundCode and seqNo you could have something like:
int hashCode() {
(fundCode + seqNo).hashCode()
}
boolean equals(Object other) {
other?.getClass() == this.class
&& other.fundCode == fundCode
&& other.seqNo == seqNo
}
The hashCode implementation is probably not the best-performing thing ever, it is a lazy way to do it, leaning on String's hashCode. But it's enough to tell whether it fixes the dupe problem.
A DRYer solution is to use an AST transformation with this annotation
import groovy.transform.EqualsAndHashCode
@EqualsAndHashCode(includes=['fundCode', 'seqNo'])
class Scholarship {
String fundCode
String seqNo
String name
static belongsTo = Applicant
}
which will generate the equals and hashCode methods for you.
The Set implementation relies on these methods to decide whether two object instances represent the same information. Not overriding means the only check is for whether the references are the same (so in the case where you have different object instances that have the same information, they would be treated as two different objects). Using the business information instead of the id to check equality means it will work regardless of whether the domain objects have an id assigned.