我的问题很简单,但我找不到任何GORM语法。
考虑以下课程:
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))
我该怎么做?
PS:我也因为设置标签最小和最大尺寸的限制而陷入困境
答案 0 :(得分:5)
仅供参考,您还可以在域类中使用custom validator:
static constraints = {
tags(validator: {
def valid = tags == tags.unique()
if (!valid) errors.rejectValue(
"tags", "i18n.message.code", "default message")
return valid
})
在数据库级别,您可以{em> grails-app / conf / hibernate / hibernate.cfg.xml 中的以下代码customize DDL generation:
<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>
答案 1 :(得分:2)
最初,我查看了joinTable
映射,看它是否支持unique
密钥,但它不支持。
我能想到的最佳解决方案是以下组合:
手动运行SQL语句以添加唯一约束。如果你有某种数据库管理工具(例如Liquibase),那将是理想的地方。
将关联明确声明为Set
。无论如何,这应该避免Hibernate遇到唯一约束。
class Article {
static hasMany = [tags: String]
Set<String> tags = new HashSet<String>()
}
另一种解决方案是显式声明您的子域(Tag
)并建立多对多关系,使用{{1}将unique
键添加到连接表中}。但这也不是一个很好的解决方案。这是一个原始的例子:
constraints
但是,有了这个,您必须明确管理代码中的多对多关系。这有点不方便,但它可以让你完全控制整个关系。您可以找到细节details here(链接示例中的class Article {
static hasMany = [articleTags: ArticleTag]
}
class Tag {
static hasMany = [articleTags: ArticleTag]
}
class ArticleTag {
Article article
Tag tag
static constraints = {
tag(unique: article)
}
}
类类似于我的Membership
。
也许有一位更熟悉GORM的大师会使用更优雅的解决方案,但我在文档中找不到任何内容。
答案 2 :(得分:1)
编辑:请注意,此方法不考虑unique(article_id , tags_id)
约束。它还引发了两个Article
具有相同标签的问题。 - 抱歉。
虽然这没有正式记录(参见Grails参考文档here和here的相关部分),GORM会忽略对一对多关联的约束。这包括unique
和nullable
约束,可能包含约束。
这可以通过查看数据库模式定义来设置dbCreate="create"
和next来证明。对于您的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
}
}
答案 3 :(得分:0)
答案 4 :(得分:0)
我发现这样做的唯一方法是编写自定义约束并对复制执行数据库检查。我认为没有一种内置方法可以使用GORM约束来实现这一目标。