Grails,GORM,关系。可选 child 条记录
Grails, GORM, relationship. Optional child records
例如,我 parent class 咖啡馆:
class Cafee {
String name
static hasMany = [
admin: Person
]
}
和一个child class人:
class Person {
String name
static belongsTo = [cafee: Cafee]
}
我使用以下方式为 Cafee 做了一些记录:
def user = new Person(name: "Andrew")
def a = new Cafee(name: "Tarelka")
.addToAdmin(user)
.save()
将 child 添加到 parent 工作正常,但是当我尝试单独创建 Person-instance 时,例如:
def visitor = new Person(username: 'testerUser', password:'password', firstName:'Иван', lastName:'Иванов', email:'ivanov@gmail.com', isAdminCafee: false)
visitor.save(flush:true)
我得到一个错误:
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
如何解决?
这里有两件事并不十分明显。一种是belongsTo
这种形式
static belongsTo = [cafee: Cafee]
是双向的,与
相比
static belongsTo = [Cafee]
这是单向的;在这两种变体中,Cafee
都可以通过其 admin
集合访问其关联的 Person
实例,但是使用第二种语法,Person
无法直接知道哪个 [=18] =] 它与相关联。
像您一样声明一个 hasMany
会在 class 中创建一个 Set
个 Person
,它的名称是您在 [=23] 中使用的密钥=] 地图,在本例中为 admin
;就好像你添加了
Set<Person> admin
但你不应该这样做,因为它是多余的 - AST 转换将 属性 添加到字节码。
同样,声明时
static belongsTo = [cafee: Cafee]
类型为 Cafee
且名称为 cafee
的字段已添加到 class。就好像你添加了
Cafee cafee
但同样,请不要手动添加,请注意它们的存在。
所以问题是持久属性在默认情况下不是空的,除非你用 nullable: true
覆盖,所以如果你打印出那个 Person
实例的错误,你至少会看到一个抱怨不可为空的 cafee
属性 为空。这适用于 addToAdmin
,因为该方法有很多作用。如果将集合实例化为一个新的 Set
接口实现(可能只是一个 HashSet
)如果它为空(这应该只在域 class 实例是新的时发生;持久集永远不会为 null,只是空的或包含项目),然后将 Person
添加到集合中,最后如果关系是双向的,它将 Person
上的反向引用设置为拥有 Cafee
。
所以你所缺少的就是手动设置 Cafee
,或者作为地图构造函数的一部分
user = new Person(cafee: a, username: 'testerUser', ...)
或稍后在工作流程中(但在验证或保存之前)
user = new Person(username: 'testerUser', ...)
...
user.cafee = a
...
user.save()
例如,我 parent class 咖啡馆:
class Cafee {
String name
static hasMany = [
admin: Person
]
}
和一个child class人:
class Person {
String name
static belongsTo = [cafee: Cafee]
}
我使用以下方式为 Cafee 做了一些记录:
def user = new Person(name: "Andrew")
def a = new Cafee(name: "Tarelka")
.addToAdmin(user)
.save()
将 child 添加到 parent 工作正常,但是当我尝试单独创建 Person-instance 时,例如:
def visitor = new Person(username: 'testerUser', password:'password', firstName:'Иван', lastName:'Иванов', email:'ivanov@gmail.com', isAdminCafee: false)
visitor.save(flush:true)
我得到一个错误:
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
如何解决?
这里有两件事并不十分明显。一种是belongsTo
static belongsTo = [cafee: Cafee]
是双向的,与
相比static belongsTo = [Cafee]
这是单向的;在这两种变体中,Cafee
都可以通过其 admin
集合访问其关联的 Person
实例,但是使用第二种语法,Person
无法直接知道哪个 [=18] =] 它与相关联。
像您一样声明一个 hasMany
会在 class 中创建一个 Set
个 Person
,它的名称是您在 [=23] 中使用的密钥=] 地图,在本例中为 admin
;就好像你添加了
Set<Person> admin
但你不应该这样做,因为它是多余的 - AST 转换将 属性 添加到字节码。
同样,声明时
static belongsTo = [cafee: Cafee]
类型为 Cafee
且名称为 cafee
的字段已添加到 class。就好像你添加了
Cafee cafee
但同样,请不要手动添加,请注意它们的存在。
所以问题是持久属性在默认情况下不是空的,除非你用 nullable: true
覆盖,所以如果你打印出那个 Person
实例的错误,你至少会看到一个抱怨不可为空的 cafee
属性 为空。这适用于 addToAdmin
,因为该方法有很多作用。如果将集合实例化为一个新的 Set
接口实现(可能只是一个 HashSet
)如果它为空(这应该只在域 class 实例是新的时发生;持久集永远不会为 null,只是空的或包含项目),然后将 Person
添加到集合中,最后如果关系是双向的,它将 Person
上的反向引用设置为拥有 Cafee
。
所以你所缺少的就是手动设置 Cafee
,或者作为地图构造函数的一部分
user = new Person(cafee: a, username: 'testerUser', ...)
或稍后在工作流程中(但在验证或保存之前)
user = new Person(username: 'testerUser', ...)
...
user.cafee = a
...
user.save()