如何在数据库级别为一对多关联设置唯一性?
我的问题很简单,但我找不到任何 GORM 语法。
考虑以下类:
class Article {
String text
static hasMany = [tags: String]
static constraints= {
tags(unique: true) //NOT WORKING
}
}
我想在我的约束中定义每篇文章一个唯一的标签名称,但我无法使用上述语法来实现它。 显然,我在数据库模式中需要类似的东西:
create table article_tags (article_id bigint, tags_string varchar(255), unique (article_id , tags_string))
我该怎么做?
PS:我也陷入了对标签最小和最大尺寸设置限制的困境
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
仅供参考,您还可以在域类中使用自定义验证器:
在数据库级别,您可以自定义 DDL 生成以下代码位于 grails-app/conf/hibernate/hibernate.cfg.xml 中:
FYI, you can also use a custom validator in domain classes:
At the database level, you can customize DDL generation by having the following code in grails-app/conf/hibernate/hibernate.cfg.xml:
最初我查看了
joinTable
映射以查看它是否支持unique
键,但事实并非如此。我能想到的最好的解决方案是以下组合:
手动运行SQL语句添加唯一约束。如果您有某种数据库管理工具(例如 Liquibase),那将是理想的地方。
将关联显式声明为
Set
。无论如何,这应该可以避免 Hibernate 遇到唯一约束。另一种解决方案是显式声明您的子域 (
Tag
) 并设置多对多关系,使用unique
键将unique
键添加到连接表中代码>约束。但这也不是一个很好的解决方案。这是一个原始示例:但是,有了这个,您必须显式管理代码中的多对多关系。这有点不方便,但它让你可以完全控制整个关系。您可以在此处找到详细信息(链接示例中的
Membership
类类似于我的ArticleTag
)。也许一位更熟悉 GORM 的大师会提出一个更优雅的解决方案,但我在文档中找不到任何内容。
Initially I looked at the
joinTable
mapping to see if it would support aunique
key, but it won't.The best solution I can think of is the following combination:
Manually run the SQL statement to add the unique constraint. If you have some sort of database management tool (e.g. Liquibase), that would be the ideal place.
Explicitly declare the association as a
Set
. This should avoid Hibernate ever running into the unique constraint, anyway.An alternate solution would be to explicitly declare your child domain (
Tag
) and set up a many-to-many relationship, adding theunique
key to the join table there usingconstraints
. But that's not really a great solution, either. Here's a primitive example:With this, though, you have to explicitly manage the many-to-many relationship in your code. It's a bit inconvenient, but it gives you full control over the relationship as a whole. You can find out the nitty gritty details here (the
Membership
class in the linked example is akin to theArticleTag
in mine).Perhaps one of the gurus more familiar with GORM will chime in with a more elegant solution, but I can't find anything in the docs.
编辑:请注意,此方法不考虑
unique(article_id,tags_id)
约束。它还引发了两个具有相同标签的Article
问题。 - 抱歉。虽然这没有正式记录(请参阅 Grails 参考文档的相关部分此处 和 这里)一对多关联的约束被 GORM 简单地忽略。这包括
唯一
和可为空
约束,以及可能的任何约束。这可以通过设置 dbCreate="create" 以及接下来查看数据库模式定义来证明。对于您的
Article
示例和 PostgreSQL 数据库,这将是:如上所示,
tags_string
列没有任何限制。与关联字段的约束相反,域类的“正常”实例字段的约束确实按预期工作。
因此,我们需要某种
Tag
或TagHolder
域类,并且我们需要找到一种仍然提供Article 的模式
具有干净的公共 API。首先,我们引入
TagHolder
域类:并将其与
Article
类关联:为了提供干净的公共 API,我们添加方法
String[] getTags()
,void setTags(String[]
。这样,我们还可以使用命名参数调用构造函数,例如,new Article(text: " text", Tags: ["foo", "bar"])
。我们还添加了addToTags(String)
闭包,它模仿了 GORM 相应的“魔术方法”。这是一个解决方法,但不涉及太多编码。
一个缺点是,我们得到了一个额外的 JOIN 表。然而,这种模式允许应用任何可用的约束。
最后,测试用例可能如下所示:
EDIT: Note that this approach does not consider a
unique(article_id , tags_id)
constraint. It also raises an issue with twoArticle
s having the same tags. - Sorry.While this is not officially documented (see the relevant parts of the Grails Reference Documentation here and here) constraints on one-to-many associations are simply ignored by GORM. This includes
unique
andnullable
constraints, and probably any.This can be proved by setting
dbCreate="create"
and next, by looking at the database schema definition. For yourArticle
sample and the PostgreSQL database, this would be:As can be seen above, there are no constraints for the
tags_string
column.In contrast to constraints on association fields, constraints on "normal" instance fields of domain classes do work as expected.
Thus, we'll want to have some kind of
Tag
, orTagHolder
, domain class, and we'd need to find a pattern that still provides theArticle
with a clean public API.First, we're introducing the
TagHolder
domain class:and associate it with the
Article
class:In order to provide a clean public API, we're adding the methods
String[] getTags()
,void setTags(String[]
. That way, we can also call the constructor with named parameters, like,new Article(text: "text", tags: ["foo", "bar"])
. We're also adding theaddToTags(String)
closure, which mimicks GORM's corresponding "magic method".It's a workaround, but there's not too much coding involved.
A drawback, we're getting an additional JOIN table. Nevertheless, this pattern allows for applying any available constraints.
Finally, a test case could look like this one:
试试这个:
http://johnrellis.blogspot.com/2009/ 09/grails-constraints-across-relationships.html
Try this:
http://johnrellis.blogspot.com/2009/09/grails-constraints-across-relationships.html
我发现执行此操作的唯一方法是编写自定义约束并对数据库进行重复检查。我认为没有内置的方法可以使用 GORM 约束来完成此任务。
The only way I've found to do this is to write a custom constraint and do a database check for the duplication. I don't think there is a built-in way to use a GORM constraint to accomplish this.