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

Posted

技术标签:

【中文标题】Grails - 在刷新 grails 错误之前保存瞬态实例?【英文标题】:Grails - save the transient instance before flushing grails Error? 【发布时间】:2012-10-12 07:29:55 【问题描述】:

我目前正在开发 Grails 应用程序,并且正在使用 Spring Security 和 UI 插件。我已经创建了用户类和我拥有的另一个区域类之间的关系,如下所示:

用户类别:

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 对象的方式,我应该在尝试保存用户之前保存父级(区域)。但是,在创建用户之前,区域总是存在的,所以我不需要再次创建区域,我该如何处理?我也尝试了“withTransaction”功能,但收效甚微

如果可能的话,我非常感谢您的帮助。

谢谢

编辑……

我已在 Controller 中将问题跟踪到这一行:

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

这告诉我这个函数正在为用户对象调用一个保存函数,但是在关系到位后,它需要先保存区域。有谁知道 SpringSecurityUi 可以帮助我解决这个问题?

谢谢

【问题讨论】:

把 springsecurityservice 放在控制器中,而不是域类中。 你能给我举个例子吗?因为 springsecurityservice 一直存在 错误是否与错误所说的“com.website.Area”类无关?谢谢 对不起,我没有阅读详细信息...当然它会在用户域类中。您可能导入了错误的区域,请检查您的导入和包名。 该区域被填充到一个下拉列表中,其中确实包含正确的内容。当我尝试保存数据时会出现问题,我相信它与 GORM 的工作方式有关。请问有什么想法吗?我真的可以使用帮助:-) 【参考方案1】:

你看到的错误信息

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

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

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

您只需要在 register 调用之前使用逻辑更新 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 以包含 Area 名称 您使用区域“名称”作为标识符安全吗?在您的类定义中,您没有约束来指示“名称”将是唯一的。也许从您的视图中传回 Area id 更安全 理想情况下,您应该使用自定义属性编辑器处理 Area 查找 - 这是一个示例:Grails command object data binding

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

【讨论】:

救命稻草 :-) 我已经在解决方案的一部分中解决了问题,但这确实帮助我解决了另一个问题:-D【参考方案2】:

尝试将双向依赖转向另一边

class Areas 

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

不要忘记从用户中删除 belongsTo

【讨论】:

【参考方案3】:
class Areas 
    String name
    static belongsTo = [User] 
    static hasMany = [users:User]

【讨论】:

以上是关于Grails - 在刷新 grails 错误之前保存瞬态实例?的主要内容,如果未能解决你的问题,请参考以下文章

我是不是需要在 grails 中显式刷新 GORM 保存调用?

Grails远程分页正在刷新整个页面而不是div

Grails:未刷新的会话和回滚的事务有啥区别?

是否可以刷新Grails中的属性?

如何减少 Grails 中 PermGen 空间的使用

类 [1] 的 Grails 错误属性 [0] 不能为空 - 在基本 grails 应用程序上