Grails - 在刷新grails之前保存瞬态实例错误?

时间:2012-10-22 13:14:49

标签: grails gorm save domain-model

我目前正在开发Grails应用程序,我正在使用Spring Security和UI插件。我创建了User Class和我所拥有的另一个Area Class之间的关系,如下所示:

用户类:

class User {

    transient springSecurityService

    String username
    String email
    String password
    boolean enabled
    boolean accountExpired
    boolean accountLocked
    boolean passwordExpired

    static belongsTo = [area:Areas]

        .......
}

区域类别:

class Areas {

    String name

    static hasMany = [users:User]

}

从类中可以看出,一个用户可以链接到一个区域,但一个区域可能有很多用户。这一切都很好,当我引导应用程序时,所有数据都正确添加。但是当我尝试使用表单来创建新用户时,我收到以下错误:

object references an unsaved transient instance - save the transient instance before flushing: com.website.Area

下面是我用来保存信息的控制器代码:

def save = {    
        def user = lookupUserClass().newInstance(params)

        if (params.password) {
            String salt = saltSource instanceof NullSaltSource ? null : params.username
            user.password = springSecurityUiService.encodePassword(params.password, salt)
        }

        if (!user.save(flush: true)) {
            render view: 'create', model: [user: user, authorityList: sortedRoles()]
            return
        }

        addRoles(user)
        flash.message = "${message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), user.id])}"
        redirect action: edit, id: user.id
    }

以下是GSP的样子:

<table>
        <tbody>

            <s2ui:textFieldRow name='username' labelCode='user.username.label' bean="${user}"
                            labelCodeDefault='Username' value="${user?.username}"/>

            <s2ui:passwordFieldRow name='password' labelCode='user.password.label' bean="${user}"
                                labelCodeDefault='Password' value="${user?.password}"/>

            <s2ui:textFieldRow name='email' labelCode='user.email.label' bean="${user}"
                                labelCodeDefault='E-Mail' value="${user?.email}"/>

            <s2ui:textFieldRow readonly='yes' name='area.name' labelCode='user.area.label' bean="${user}"
                                labelCodeDefault='Department' value="${area.name}"/>


            <s2ui:checkboxRow name='enabled' labelCode='user.enabled.label' bean="${user}"
                           labelCodeDefault='Enabled' value="${user?.enabled}"/>

            <s2ui:checkboxRow name='accountExpired' labelCode='user.accountExpired.label' bean="${user}"
                           labelCodeDefault='Account Expired' value="${user?.accountExpired}"/>

            <s2ui:checkboxRow name='accountLocked' labelCode='user.accountLocked.label' bean="${user}"
                           labelCodeDefault='Account Locked' value="${user?.accountLocked}"/>

            <s2ui:checkboxRow name='passwordExpired' labelCode='user.passwordExpired.label' bean="${user}"
                           labelCodeDefault='Password Expired' value="${user?.passwordExpired}"/>
        </tbody>
        </table>

我已经调查了这个,我相信这是我试图保存GORM对象的方式,我应该在尝试保存用户之前保存父(Area)。但是,在创建用户之前区域将始终存在,因此我不需要再次创建区域,我该如何处理?我尝试了“withTransaction”功能,但也没有成功

如果可能的话,我真的很感激一些帮助。

由于

编辑......

我已在Controller中跟踪此行的问题:

RegistrationCode registrationCode = springSecurityUiService.register(user, command.password, salt)

这告诉我这个函数正在为用户对象调用一个save函数,但是如果有关系,它需要首先保存Area。有谁知道SpringSecurityUi以帮助我吗?

由于

3 个答案:

答案 0 :(得分:8)

您看到的错误消息

object references an unsaved transient instance - save the transient instance before flushing: com.website.Area

...当您尝试从子(即用户)实体保存不存在的父(即Area)实体时发生。正如您所指出的那样, RegistrationController.register 方法可能会发生错误,如果您正在保存新用户的位置。

RegistrationCode registrationCode = springSecurityUiService.register(user, command.password, salt)

您只需要使用逻辑更新 RegistrationCode.register 方法,以便在注册调用之前将区域分配给用户(假设区域已存在) - 以下是一个简单的例子。

class RegistrationController ..
 ..
def area = Area.findByName(command.areaName) 
def user = lookupUserClass().newInstance(email: command.email, username: command.username,
                accountLocked: true, enabled: true, area: area)
RegistrationCode registrationCode = springSecurityUiService.register(user, command.password, salt)

几点说明:

  • 您从gsp视图传回“area.name”,因此您需要更新/覆盖RegisterCommand以包含区域名称
  • 您使用区域“名称”作为标识符安全吗?在类定义中,您没有约束来指示“name”将是唯一的。也许从您的视图中传回区域 ID更安全
  • 理想情况下,您应该使用自定义属性编辑器处理区域查找 - 以下是一个示例:Grails command object data binding

无论如何,希望有所帮助。

答案 1 :(得分:1)

尝试将双向依赖关系转向另一方

class Areas {

    String name
    static belongsTo = [User] 
    static hasMany = [users:User]
}

不要忘记从用户

中删除belongsTo

答案 2 :(得分:1)

class Areas {
    String name
    static belongsTo = [User] 
    static hasMany = [users:User]
}