将一对 @ManyToOne 相关属性标记为唯一。可能的?
Posted
技术标签:
【中文标题】将一对 @ManyToOne 相关属性标记为唯一。可能的?【英文标题】:Marking a pair of @ManyToOne related properties as unique. Possible? 【发布时间】:2013-03-22 08:32:16 【问题描述】:我有一个典型的例子,一个 POST 有很多 TAG,而一个 TAG 有很多 POST。
我没有使用典型的@ManyToMany
,而是在中间使用了一个名为 TAGPOST 的域对象,它还允许我在其中拥有有用的数据,例如何时使用给定标签标记帖子等。每个 POST 和TAG resp,与 TAGPOST 处于@OneToMany
关系。
具体要求是一个帖子不能包含两次相同的标签,因此 TAGPOST.post 和 TAGPOST.tag 对必须始终是唯一的。通常,我会在表中创建一个复合主键对,负责存储 TAGPOST 对象。
AFAIK,没有办法表达这种独特的约束。我已经标记了jpa.ddl=update
,这意味着每次我将应用程序移动到新环境时,我都必须在数据库中手动修复它。这非常不方便,而且容易出错,尤其是在单元测试时,因为在每次迭代中都会或多或少地创建和删除数据库。
我什至想在 @PrePersist
上手动进行检查,或者甚至将检查移到业务层中,例如创建 PostService。
我该怎么办?我错过了 Play 默认拥有的东西吗?一些巧妙的注释来表达 TAGPOST 类的@ManyToOne
属性的唯一性?
仅供参考:我使用的是 Play 1.2.5
编辑:TAGPOST 类如下所示:
@Entity
public class TagPost extends Model
@ManyToOne
public Tag tag;
@ManyToOne
public Post post;
public Date dateAdded;
...
【问题讨论】:
我认为您可以为您的TagPost
对象设置一个复合 ID。可以分享TagPost
类的代码吗?
@dcernahoschi 刚刚发布了它
【参考方案1】:
我为数据库唯一性编写了一个自定义Check
。也许你应该自定义它。
DBUnique.java
package models.check;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import net.sf.oval.configuration.annotation.Constraint;
import play.db.jpa.GenericModel;
@Retention(RetentionPolicy.RUNTIME)
@Target( ElementType.FIELD, ElementType.PARAMETER )
@Constraint(checkWith = DbUniqueCheck.class)
public @interface DBUnique
String message() default DbUniqueCheck.mes;
Class<? extends GenericModel> modelClass();
String field() default ""; // field name will be used
DbUniqueCheck.java
package models.check;
import net.sf.oval.Validator;
import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
import net.sf.oval.context.FieldContext;
import net.sf.oval.context.OValContext;
import org.apache.commons.lang.StringUtils;
import play.db.jpa.GenericModel.JPAQuery;
@SuppressWarnings("serial")
public class DbUniqueCheck extends AbstractAnnotationCheck<DBUnique>
final static String mes = "validation.dbunique";
DBUnique dbUnique;
@Override
public void configure(DBUnique dBUnique)
this.dbUnique = dBUnique;
setMessage(dBUnique.message());
public boolean isSatisfied(Object validatedObject, Object value, OValContext context, Validator validator)
try
String field = dbUnique.field();
if (field == null || field.isEmpty())
field = ((FieldContext) context).getField().getName();
JPAQuery q = (JPAQuery) dbUnique.modelClass().getMethod("find", String.class, Object[].class)
.invoke(null, "by" + StringUtils.capitalize(field), new Object[] value );
return q.first() == null;
catch (Exception e)
e.printStackTrace();
return false;
用法:link to gist
它只是检查给定的field
,因为给定的class
实例在数据库中是唯一的。也许你应该做这样的东西..
【讨论】:
以前从未使用过,但是有一种方法可以在 @Table 注释中添加唯一约束。也许这会有所帮助? @Table(uniqueConstraints=@UniqueConstraint(...),)@UniqueConstraint
在数据库端创建一个唯一约束。因此,如果您想通过 JPA 持久化模态,JPA 将引发异常。您应该处理异常。另一方面(使用check
),如果您验证模态,您会得到一个validation.error。没有例外,没有尝试捕获,没有回滚和数据库级别检查..check this out以上是关于将一对 @ManyToOne 相关属性标记为唯一。可能的?的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate @ManyToOne @JoinColumn 始终为空
Doctrine ORM ManyToOne Inverse 不起作用
将一对多关系显示为 2 列 - 1 个唯一行(ID 和逗号分隔列表)