将一对 @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 始终为空

JPA双向一对多多一对一

Doctrine ORM ManyToOne Inverse 不起作用

将一对多关系显示为 2 列 - 1 个唯一行(ID 和逗号分隔列表)

Doctrine2 Mapping:2 个字段映射到一个字段(ManyToOne)

Hibernate JPA - 没有填充ManyToOne关系