仅在瞬态发生变化时才调用beforeUpdate

时间:2011-12-01 12:04:11

标签: grails gorm

我使用Grails 2.0.0.RC2,我有一个像这样的User类:

class User {
    String username
    String password

    // Idea from http://grailsrecipes.wordpress.com/2009/04/19/grails-user-registration-and-login/ .
    String formPassword
    String formPasswordConfirm

    // Constraints and validation ommited

    static transients = ['formPassword', 'formPasswordConfirm']

    def beforeUpdate() {
        println("Inside beforeUpdate")
        if (formPassword != null)
            encodePassword()
    }

    protected void encodePassword() {
        password = formPassword // Just for this case
    }
}

当用户要求重置密码时,我会向他发送一封电子邮件,其中包含重置密码页面的链接。重置密码表单很简单 - 它只包含两个字段:formPasswordformPasswordConfirm。我在控制器中执行了一个简单的操作:user.formPassword = params["formPassword"]user.formPasswordConfirm = params["formPasswordConfirm"]。然后我做(user.save()) - 问题就开始了。

我的问题是没有调用beforeUpdate()。我认为这是一个验证问题(这里是omiited),但事实并非如此。事实证明user.save()没有将用户持久化到数据库!为什么?我希望它被持久化,密码应该改变。但是user.isDirty()之前user.save()是假的。那是因为没有任何持久性属性被改变。确实如此。由于未调用user.save(),因此未调用beforeUpdate()

这是理想的行为吗?也许这是一个Grails错误,在更新之前应该始终调用beforeUpdate(),然后应该检查isDirty()?你觉得怎么样?

2 个答案:

答案 0 :(得分:3)

我不知道在更新瞬态字段时是否应该调用beforeUpdate,但是可以选择

  1. 这是一个Grails / Hibernate错误
  2. 这是预期的行为,因为修改瞬态字段时不会发生数据库更新
  3. 我会在2上投入大量资金,在1上投注很少。

    所以把它放在一边,我认为你已经使你的域类变得更复杂了。具体来说,您应该能够通过2个密码字段而不是3来实现目标。

    class User {
    
        static transients = ['passwordConfirm']
        def springSecurityService
    
        String password
        String passwordConfirm
    
        static constraints = {
            password blank: false, validator: {password, self ->
    
                // We only need to check the password confirmation when it is not empty, i.e.
                // when a user registers or resets their password
                if (self.passwordConfirm) {
                    password == self.passwordConfirm
                }
            }
        }
    
        def beforeInsert() {
            encodePassword()
        }
    
        def beforeUpdate() {
            if (isDirty('password')) {
                encodePassword()
            }
        }
    
        private void encodePassword() {
            password = springSecurityService.encodePassword(password)
            passwordConfirm = springSecurityService.encodePassword(passwordConfirm)
        }
    
    }
    

    您的重置密码控制器操作应如下所示:

    def resetPassword = {
    
        User user = User.findByUsername(params.username)
        user.password = params.formPassword
        user.passwordConfirm = params.formPasswordConfirm
    
        if (user.save()) {
            // It worked, send them to the login page or whatever...
        } else {
            // Validation failed, send them back to the reset password page    
        }
    }
    

答案 1 :(得分:1)

这是理想的行为。

在我看来,语义非常清晰,你没有改变任何持久字段,因此没有什么可以保存的。 .save()无法猜测在这种情况下,某些瞬态属性应该让你运行save()(然后是beforeUpdate())。

只需添加一个更新密码的方法就可以了。