5

Grails 2.2.0

ユーザーがマスターメールを1つだけ持つように強制するカスタム制約を作成しようとしています。エラーの原因となる簡略化されたコードは次のとおりです。

ユーザードメインクラス

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)
    }
}

「grailstest-app-integration」を実行すると、次のようになります。

| 失敗:testSomething(webapp.EmailTests)
| org.hibernate.AssertionFailure:webapp.Emailのorg.grails.datastore.gorm.GormStaticApi $ _methodMissing_closure2.doCall(GormStaticApi.groovy:105)のEmailエントリ(例外が発生した後にセッションをフラッシュしないでください)のnull id $ __ clinit__closure1_closure2.doCall(Email.groovy:13)at org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener.onApplicationEvent(AbstractPersistenceEventListener.java:46)at webapp.EmailTests.testSomething(EmailTests.groovy:21)

一意の制約をカスタム制約の後に変更しても、問題は発生しません。ここで何が起きてるの?ここで、関連性のある制約の順序はどのようになっているのかを理解したいのですが。

明確にするために、これは問題を引き起こしません:

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

1 に答える 1

4

私はそれを理解したと思います... 1対多の関係が壊れています。

説明させてください

  • テストの最初の行は、User john保存およびフラッシュされる を作成します。
  • 2 行目は を作成Emailし、ユーザー プロパティとして john を設定します。

インスタンスを保存しようとすると、EmailGORM が文句を言います。Emailこれは、関係の逆側である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 を保存するとすべてのメールも保存されるはずです。


別のルート

問題はバリデーターのユーザーインスタンスへの参照にあるように思われるので、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
        }
    }
}

私はいくつかのテストを行いましたが、この方法で問題なく実行できました。

于 2013-03-19T06:53:06.490 に答える