Hibernate 集成测试期间约束上的Grails null id错误

Hibernate 集成测试期间约束上的Grails null id错误,hibernate,grails,constraints,integration-testing,grails-2.2,Hibernate,Grails,Constraints,Integration Testing,Grails 2.2,Grails2.2.0 我试图创建一个自定义约束,强制用户只有一封主电子邮件。以下是导致错误的简化代码: 用户域类 class User { static hasMany = [emails: Email] static constraints = { } } class Email { static belongsTo = [user: User] String emailAddress Boolean isMaster sta

Grails2.2.0

我试图创建一个自定义约束,强制用户只有一封主电子邮件。以下是导致错误的简化代码:

用户域类

class User {

    static hasMany = [emails: Email]

    static constraints = {
    }
}
class Email {

    static belongsTo = [user: User]
    String emailAddress
    Boolean isMaster

    static constraints = {

        emailAddress unique: ['user']
        isMaster validator: { val, obj ->
            return !val || Email.findByUserAndIsMaster(obj.user, true) == null
        }

    }
}
电子邮件域类

class User {

    static hasMany = [emails: Email]

    static constraints = {
    }
}
class Email {

    static belongsTo = [user: User]
    String emailAddress
    Boolean isMaster

    static constraints = {

        emailAddress unique: ['user']
        isMaster validator: { val, obj ->
            return !val || Email.findByUserAndIsMaster(obj.user, true) == null
        }

    }
}
集成测试

class EmailTests {

    @Before
    void setUp() {

    }

    @After
    void tearDown() {
        // Tear down logic here
    }

    @Test
    void testSomething() {
        def john = (new User(login: 'johnDoe')).save(failOnError: true, flush: true)
        assert (new Email(emailAddress: 'john@gmail.com', user: john, isMaster: true)).save(failOnError: true)
    }
}
运行“grails测试应用程序-集成”将导致:

|失败:testSomething(webapp.EmailTests)
|org.hibernate.AssertionFailure:webapp.Email条目中的空id(发生异常后不要刷新会话) 位于org.grails.datastore.gorm.GormStaticApi$\u methodMissing\u closure2.doCall(GormStaticApi.groovy:105) 在webapp.Email$\uuuuClinit\uuuuClosure1\uClosure2.doCall(Email.groovy:13) 位于org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener.onApplicationEvent(AbstractPersistenceEventListener.java:46) 在webapp.EmailTests.testSomething(EmailTests.groovy:21)

如果将唯一约束更改为在自定义约束之后,则不会发生问题。这里发生了什么?我想了解,在这里,任何相关约束的顺序是怎样的

要清楚,这不会导致问题:

static constraints = {
        isMaster validator: { val, obj ->
            return !val || Email.findByUserAndIsMaster(obj.user, true) == null
        }
        emailAddress unique: ['user']
    }

我想我知道了。。。一对多的关系破裂了

让我解释一下
  • 测试中的第一行创建保存并刷新的
    用户john
  • 第二行创建一封
    电子邮件
    ,并将john设置为用户属性
一旦你试图保存
电子邮件
实例,GORM就会抱怨。这是因为您将john分配给
电子邮件
,这是关系的反面。拥有方没有意识到这一点,在这一点上什么都没有。简单地说在将实例添加到用户之前,无法保存和发送电子邮件。

这里有一个测试方法应该是有效的

void testSomething() {
   def john = new User(login: 'johnDoe')

   john.addToEmails(new Email(emailAddress: 'john@gmail.com', isMaster: true))
   john.save(flush:true)

   assert false == john.errors.hasErrors()
   assert 1 == john.emails.size()  
}
addToEmails()
方法将电子邮件实例添加到集合中,并将用户设置在关系的反面。这段关系现在很满意,保存john也应该保存所有电子邮件


另一条路线 由于问题似乎是
电子邮件中对用户实例的引用
验证器,我想可能还有另一种方法可以使用

class User {
    static hasOne = [master: Email]
    static hasMany = [emails: Email]
}
这将消除对有问题的验证器的需要,这使得您可以根据用户的身份对邮件进行验证。您可以让用户负责他拥有的电子邮件地址以及应该应用的规则。 您可以向
User
添加验证器,以验证您是否拥有电子邮件列表中不存在的主地址,并验证分配的所有地址是否唯一。 例如:

static constraints = {
    master validator: { master, user, errors ->
        if (master.emailAddress in user.emails*.emailAddress) {
            errors.rejectValue('master', 'error.master', 'Master already in e-mails')
            return false
        }
    }

    emails validator: { emails, user, errors ->
        def addresses = emails*.emailAddress
        if (!addresses.equals(emails*.emailAddress.unique())) {
            errors.rejectValue('emails', 'error.emails', 'Non unique e-mail')
            return false
        }
    }
}

我做了一些测试,他们通过这种方式做得很好。

测试中的关系不是倒退了吗?我想您应该使用
john.addToEmails()
。约翰是主队。在您的测试中,您使用
Email
就像它是拥有方一样:-)如果我这样做,则会出现相同的问题:
void testSomething(){def john=new User(login:'johnDoe')。save(failOnError:true)def Email=new Email(emailAddress:'john@gmail.com“,isMaster:true)john.addToEmails(email)assert john.save(failOnError:true)}
有意义。但我不明白为什么会出现“对象引用未保存的临时实例”错误,因为这是我尝试保存的第一对用户和电子邮件,应该通过自定义约束。我认为
email.findByUserAndIsMaster(obj.user,true)
是问题所在。它引用未保存的瞬态
用户
。我认为你可能需要找到另一种方法来实施你的约束。也许可以包装
User.addToEmails
来解决这个“鸡和蛋”问题?好的。我希望将所有的数据模型约束都实现为域约束。似乎我希望太多了。包装动态addTo方法到底是什么意思?在用户中覆盖方法并在其中执行签入?之后如何调用原始addTo方法?我也在想,这可以通过使用域事件(例如beforeensert)来实现吗?别介意我说的包装器。这不算:-)我用你可以走的另一条路线更新了帖子。