كيفية وضع التفرد على مستوى ديسيبل لجمعية واحدة إلى طابع؟

StackOverflow https://stackoverflow.com/questions/4041939

سؤال

مشكلتي بسيطة ، لكنني لم أجد أي بناء جملة لهذا الغلور لهذا الغرض.

النظر في الفصل التالي:

class Article {
  String text

  static hasMany = [tags: String]

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

}

اريد ان احصل اسم علامة فريد لكل مقالة تم تعريفه في قيوداتي ولكن لا يمكنني صنعه مع بناء الجملة أعلاه. من الواضح أنني بحاجة إلى مخطط DB شيء مثل:

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

كيف أقوم بذلك؟

ملاحظة: أنا عالق أيضًا لإعداد القيود على الحد الأدنى والحجم القصوى

هل كانت مفيدة؟

المحلول

لمعلوماتك ، يمكنك أيضًا استخدام ملف مصلحة مخصصة في فصول المجال:

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

على مستوى قاعدة البيانات ، يمكنك تخصيص جيل DDL عن طريق وجود الرمز التالي في Grails-App/Conf/hibernate/hibernate.cfg.xml:

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

نصائح أخرى

في البداية نظرت إلى joinTable رسم الخرائط لمعرفة ما إذا كان سيؤيد unique المفتاح ، لكنه لن يفعل ذلك.

أفضل حل يمكنني التفكير فيه هو المزيج التالي:

  • قم بتشغيل بيان SQL يدويًا لإضافة القيد الفريد. إذا كان لديك نوع من أداة إدارة قاعدة البيانات (EG Liquibase) ، فسيكون ذلك المكان المثالي.

  • أعلن صراحة الرابطة على أنها أ Set. هذا يجب أن يتجنب السبات على الإطلاق في القيد الفريد ، على أي حال.

    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 بحل أكثر أناقة ، لكن لا يمكنني العثور على أي شيء في المستندات.

تحرير: لاحظ أن هذا النهج لا ليس النظر في unique(article_id , tags_id) قيد. كما يثير مشكلة مع اثنين ArticleS لها نفس العلامات. - آسف.

في حين أن هذا غير موثق رسميًا (انظر الأجزاء ذات الصلة من الوثائق المرجعية Grails هنا و هنا) يتم تجاهل القيود على الجمعيات الفردية ببساطة بواسطة Gorm. هذا يشمل unique و nullable القيود ، وربما أي.

يمكن إثبات ذلك عن طريق الإعداد 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]
}

من أجل توفير واجهة برمجة تطبيقات عامة نظيفة ، نضيف الأساليب String[] getTags(), void setTags(String[]. وبهذه الطريقة ، يمكننا أيضًا استدعاء المُنشئ مع المعلمات المسماة ، مثل ، new Article(text: "text", tags: ["foo", "bar"]). نحن نضيف أيضا addToTags(String) الإغلاق ، الذي يحاكي "الطريقة السحرية" المقابلة للجورم.

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

إنه حل بديل ، ولكن لا يوجد الكثير من الترميز.
عيب ، نحصل على جدول انضمام إضافي. ومع ذلك ، يسمح هذا النمط بتطبيق أي قيود متاحة.

أخيرًا ، يمكن أن تبدو حالة الاختبار مثل هذه:

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

الطريقة الوحيدة التي وجدتها للقيام بذلك هي كتابة قيود مخصصة والتحقق من قاعدة البيانات للتكرار. لا أعتقد أن هناك طريقة مدمجة لاستخدام قيود GORM لإنجاز هذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top