Reputation: 4043
For example, I've parent class Cafee:
class Cafee {
String name
static hasMany = [
admin: Person
]
}
and a child class Person:
class Person {
String name
static belongsTo = [cafee: Cafee]
}
I've done some records to Cafee using:
def user = new Person(name: "Andrew")
def a = new Cafee(name: "Tarelka")
.addToAdmin(user)
.save()
Adding child to parent works fine, but when I trying to create Person-instance separately, for example:
def visitor = new Person(username: 'testerUser', password:'password', firstName:'Иван', lastName:'Иванов', email:'[email protected]', isAdminCafee: false)
visitor.save(flush:true)
I get an error:
ERROR context.GrailsContextLoaderListener - Error initializing the application: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person
Message: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person
How to fix it?
Upvotes: 2
Views: 726
Reputation: 75671
There are two things going on here that aren't entirely obvious. One is that this form of belongsTo
static belongsTo = [cafee: Cafee]
is bidirectional, as compared to
static belongsTo = [Cafee]
which is undirectional; in both variants a Cafee
can access its associated Person
instances via its admin
collection, but using the second syntax there's no direct way for a Person
to know which Cafee
it's associated with.
Declaring a hasMany
like you did creates a Set
of Person
s in the class, and its name is the key you used in the hasMany
map, in this case admin
; it's as if you had added
Set<Person> admin
but you shouldn't because it's redundant - an AST transform adds that property to the bytecode.
Similarly, when declaring
static belongsTo = [cafee: Cafee]
a field of type Cafee
with the name cafee
is added to the class. It's as if you added
Cafee cafee
but again, please don't manually add either, just be aware that they're there.
So the problem is that persistent properties are not-null by default unless you override with nullable: true
, so if you printed out the errors for that Person
instance you'd see at least one complaining that the non-nullable cafee
property is null. This works with addToAdmin
because that method does a lot. If instantiates the set to a new Set
interface implementation (probably just a HashSet
) if it's null (which should only ever occurr when the domain class instance is new; a persistent set will never be null, just empty or containing items), then adds the Person
to the collection, and finally if the relationship is bidirectional, it sets the backreference on the Person
to the owning Cafee
.
So all you're missing is setting the Cafee
manually, either as part of the map constructor
user = new Person(cafee: a, username: 'testerUser', ...)
or later in the workflow (but before validation or saving)
user = new Person(username: 'testerUser', ...)
...
user.cafee = a
...
user.save()
Upvotes: 2