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 中创建一个 SetPerson,它的名称是您在 [=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()