Reputation: 1105
Say I have the following (heavily simplified) GORM domain classes:
class PhoneCall extends Interaction {
Survey survey
}
class Survey {
String campaignCode
Integer clientId
Boolean isDynamic
List interactions
static constraints = {
campaignCode unique: true, nullable: false
clientId nullable: true
isDynamic nullable: true
}
static hasMany = [interactions: Interaction]
}
class Interaction {
String clazz
Instant dateCreated
static constraints = {
}
static mapping = {
tablePerHierarchy false
autoTimestamp false
}
def beforeInsert() {
dateCreated = Instant.now()
}
}
I have the following simple code to set up these classes for a test:
def survey = new Survey(campaignCode: "TEST", isDynamic: true).save(failOnError: true, flush: true)
def phoneCall = new PhoneCall(survey: survey, clazz: PhoneCall.name).save(failOnError: true)
This fails with the following stack trace:
org.springframework.dao.DataIntegrityViolationException: could not insert: [uk.co.nttfundraising.onitfhi.domain.PhoneCall]; SQL [insert into phone_call (id) values (?)]; constraint [survey_id]; nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [uk.co.nttfundraising.onitfhi.domain.PhoneCall]
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:643)
at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:412)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:339)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod.performSave(SavePersistentMethod.java:56)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractSavePersistentMethod.doInvokeInternal(AbstractSavePersistentMethod.java:215)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:63)
at org.codehaus.groovy.grails.orm.hibernate.HibernateGormInstanceApi.save(HibernateGormInstanceApi.groovy:196)
However, if I remove the line List interactions
from Survey
(making interactions
into a Set
), everything works fine. There are also no problems if I use SortedSet interactions
, though the generated database schema doesn't seem to have any notion of order so I'm unsure about that solution. Google mostly suggests not saving the Survey
(e.g. this blog post) but I've tried this to no avail.
It's only the List
that fails, and it causes the insert into PhoneCall
to completely ignore my Survey
! What's going on?
Upvotes: 0
Views: 585
Reputation: 9885
A caveat to using a List
is that the item you add to it cannot be save()
d prior to adding to the List
. But more importantly, the proper way to add items to a collection when using a one-to-many association is to use survey.addToInteractions()
, See addTo*(). But first, you need a proper association...
class PhoneCall extends Interaction {
static belongsTo = [survey: Survey]
}
By replacing the Survey
property with belongsTo
, you get a proper bi-directional one-to-many association. Then, you can use/test it like this:
def survey = new Survey(campaignCode: "TEST", isDynamic: true)
survey.addToInteractions(new PhoneCall(survey: survey, clazz: PhoneCall.name))
survey.save(failOnError: true, flush: true)
Notice that the PhoneCall
is never explicitly saved, and PhoneCall.survey
is not explicitly assigned. All of this gets taken care of when survey.save()
is called.
Once saved, someSurvey.interactions[index].survey
will reference the someSurvey
.
Upvotes: 1