Grails 2.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 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 test-app -integration”将导致:
|失败:testSomething(webapp.EmailTests)
| org.hibernate.AssertionFailure:webapp.Email条目中的null id(发生异常后不刷新会话) 在org.grails.datastore.gorm.GormStaticApi $ _methodMissing_closure2.doCall(GormStaticApi.groovy:105) 在webapp.Email $ __ clinit__closure1_closure2.doCall(Email.groovy:13) at 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']
}
答案 0 :(得分:4)
我想我明白了......一对多的关系破裂了。
User john
。Email
并将john设置为用户属性。一旦您尝试保存Email
实例,GORM就会抱怨。那是因为你将john分配给Email
,这是关系的反面。拥有方并不知道这一点,并且在那一点上什么也没有。简单的说。 在添加到用户之前,您无法保存并通过电子邮件发送电子邮件。
这是一种应该有效的测试方法。
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()
方法将电子邮件实例添加到集合中,并将用户设置在关系的反面。现在这种关系得到满足,保存约翰也应该保存所有电子邮件。
由于问题似乎是Email
验证器中对用户实例的引用,我可能还有另一条路线可供您使用。
class User {
static hasOne = [master: Email]
static hasMany = [emails: Email]
}
这将消除对有问题的验证器的需求,这使得Email
类取决于User
进行验证。您可以让用户对他拥有的电子邮件地址以及应该应用的规则负责。
您可以向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
}
}
}
我做了一些测试,他们以这种方式做得很好。