3

私の問題は単純ですが、このためのGORM構文が見つかりませんでした。

次のクラスについて考えてみます。

class Article {
  String text

  static hasMany = [tags: String]

  static constraints= {
    tags(unique: true) //NOT WORKING
  }

}

制約で記事ごとに1つの一意のタグ名を定義したいのですが、上記の構文では作成できません。明らかに、DBスキーマには次のようなものが必要です。

create table article_tags (article_id bigint, tags_string varchar(255), unique (article_id , tags_string))

どうやってやるの?

PS:タグの最小サイズと最大サイズに制約を設定することにも悩まされています

4

5 に答える 5

5

参考までに、ドメインクラスでカスタムバリデーターを使用することもできます。

    static constraints = {
    tags(validator: { 
        def valid = tags == tags.unique()
        if (!valid) errors.rejectValue(
            "tags", "i18n.message.code", "default message")
        return valid
    })

データベースレベルでは、 grails-app / conf / hibernate / hibernate.cfg.xmlに次のコードを含めることで、DDL生成をカスタマイズできます。

<hibernate-mapping>
    <database-object>
        <create>
        ALTER TABLE article_tags
        ADD CONSTRAINT article_tags_unique_constraint 
        UNIQUE(article_id, tags_string);
    </create>
        <drop>
        ALTER TABLE article_tags 
        DROP CONSTRAINT article_tags_unique_constraint;
    </drop>
    </database-object>
</hibernate-mapping>
于 2010-10-28T18:47:40.253 に答える
2

最初にjoinTableマッピングを調べて、キーをサポートするかどうかを確認しましたが、サポートしuniqueません。

私が考えることができる最善の解決策は、次の組み合わせです。

  • SQLステートメントを手動で実行して、一意の制約を追加します。ある種のデータベース管理ツール(Liquibaseなど)がある場合は、それが理想的な場所です。

  • 関連付けをとして明示的に宣言しSetます。これにより、とにかく、Hibernateが一意の制約に遭遇することを回避できます。

    class Article {
        static hasMany = [tags: String]
        Set<String> tags = new HashSet<String>()
    }
    

別の解決策は、子ドメイン()を明示的に宣言Tagし、多対多の関係を設定し、uniqueを使用してそこの結合テーブルにキーを追加することconstraintsです。しかし、それも本当に素晴らしい解決策ではありません。これが原始的な例です:

class Article {
    static hasMany = [articleTags: ArticleTag]
}

class Tag {
    static hasMany = [articleTags: ArticleTag]
}

class ArticleTag {
    Article article
    Tag tag
    static constraints = {
        tag(unique: article)
    }
}

ただし、これを使用すると、コード内の多対多の関係を明示的に管理する必要があります。少し不便ですが、関係全体を完全に制御できます。あなたはここで核心の詳細を見つけることができます(Membershipリンクされた例のクラスは私のものに似ていArticleTagます)。

おそらく、GORMに精通している教祖の1人が、よりエレガントなソリューションを紹介しますが、ドキュメントには何も見つかりません。

于 2010-10-28T15:41:28.737 に答える
1

編集:このアプローチは制約を考慮していないことに注意してください。unique(article_id , tags_id)またArticle、同じタグを持つ2つの問題も発生します。- ごめん。

これは公式に文書化されていませんが(ここここのGrailsリファレンスドキュメントの関連部分を参照)、1対多の関連付けに対する制約はGORMによって単に無視されます。これにはuniquenullable制約、およびおそらく任意のものが含まれます。

dbCreate="create"これは、設定し、次にデータベーススキーマ定義を確認することで証明できます。ArticleサンプルとPostgreSQLデータベースの場合、これは次のようになります。

CREATE TABLE article_tags
(
  article_id bigint NOT NULL,
  tags_string character varying(255),
  CONSTRAINT fkd626473e45ef9ffb FOREIGN KEY (article_id)
      REFERENCES article (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT article0_tags_article0_id_key UNIQUE (article_id)
)
WITH (
  OIDS=FALSE
);

tags_string上記のように、列に制約はありません。

アソシエーションフィールドの制約とは対照的に、ドメインクラスの「通常の」インスタンスフィールドの制約は期待どおりに機能します。

したがって、ある種のTag、またはTagHolder、ドメインクラスが必要でありArticleクリーンなパブリックAPIを提供するパターンを見つける必要があります。

まず、TagHolderドメインクラスを紹介します。

class TagHolder {
    String tag

    static constraints = {
        tag(unique:true, nullable:false, 
            blank:false, size:2..255)
    }
}

Articleそしてそれをクラスに関連付けます:

class Article {
    String text

    static hasMany = [tagHolders: TagHolder]
}

クリーンなパブリックAPIを提供するために、メソッドString[] getTags()、を追加していますvoid setTags(String[]。そうすれば、のような名前付きパラメーターを使用してコンストラクターを呼び出すこともできますnew Article(text: "text", tags: ["foo", "bar"])addToTags(String)また、GORMの対応する「魔法の方法」を模倣したクロージャーも追加します。

class Article {
    String text

    static hasMany = [tagHolders: TagHolder]

    String[] getTags() { 
        tagHolders*.tag
    }

    void setTags(String[] tags) {
        tagHolders = tags.collect { new TagHolder(tag: it) }
    } 

    {
        this.metaClass.addToTags = { String tag ->
            tagHolders = tagHolders ?: []
            tagHolders << new TagHolder(tag: tag)
        }
    }
}

これは回避策ですが、コーディングはそれほど多くありません。
欠点は、追加のJOINテーブルを取得することです。それでも、このパターンでは、使用可能な制約を適用できます。

最後に、テストケースは次のようになります。

class ArticleTests extends GroovyTestCase {
    void testUniqueTags_ShouldFail() {
        shouldFail { 
            def tags = ["foo", "foo"] // tags not unique
            def article = new Article(text: "text", tags: tags)
            assert ! article.validate()
            article.save()
        }
    }

    void testUniqueTags() {     
        def tags = ["foo", "bar"]
        def article = new Article(text: "text", tags: tags)
        assert article.validate()
        article.save()
        assert article.tags.size() == 2
        assert TagHolder.list().size() == 2
    }

    void testTagSize_ShouldFail() {
        shouldFail { 
            def tags = ["f", "b"] // tags too small
            def article = new Article(text: "text", tags: tags)
            assert ! article.validate()
            article.save()
        }
    }

    void testTagSize() {        
        def tags = ["foo", "bar"]
        def article = new Article(text: "text", tags: tags)
        assert article.validate()
        article.save()
        assert article.tags.size() == 2
        assert TagHolder.list().size() == 2
    }

    void testAddTo() {
        def article = new Article(text: "text")
        article.addToTags("foo")
        article.addToTags("bar")
        assert article.validate()
        article.save()
        assert article.tags.size() == 2
        assert TagHolder.list().size() == 2
    }
}
于 2010-10-28T16:18:02.197 に答える
0

これを行うために私が見つけた唯一の方法は、カスタム制約を記述し、データベースで重複をチェックすることです。これを達成するためにGORM制約を使用する組み込みの方法はないと思います。

于 2010-10-28T18:35:55.973 に答える
0

これを試して:

http://johnrellis.blogspot.com/2009/09/grails-constraints-across-relationships.html

于 2010-10-28T17:39:23.100 に答える