2013-03-22 32 views
1

我有一个典型的例子,一个POST有很多标签,而一个TAG有很多POST。 而不是使用一个典型@ManyToMany,我使用中间域对象,称为TAGPOST,这也让我有在里面有用的数据,当一个岗位被标记与给定的标签,等每个岗位,并TAG如resp,与TAGPOST的关系是@OneToMany将一对@ManyToOne相关属性标记为唯一。可能?

的具体要求是,后不能有包含了两次,因此TAGPOST.post和TAGPOST.tag对必须始终唯一相同的标签。通常情况下,我会通过在表中创建一个组合主键对来实现这一点,负责存储TAGPOST对象。

据我所知,没有任何方式来表达这种独特的约束。我标记为jpa.ddl=update,这意味着每次将应用程序移至新环境时,我都必须手动修复此数据库。这非常不方便,而且容易出错,特别是在单元测试时,因为那样在每次迭代中创建和删除数据库的次数都会增加或减少。

我甚至在想手工做@PrePersist检查,甚至在业务层移动的检查,比如,创建一个PostService。

我该怎么办?我是否缺少Play默认的内容?一些巧妙的注释来表达TAGPOST类的@ManyToOne属性的唯一性?

FYI:我使用的播放1.2.5

编辑:TAGPOST类看起来是这样的:

@Entity 
public class TagPost extends Model { 

    @ManyToOne 
    public Tag tag; 

    @ManyToOne 
    public Post post; 

    public Date dateAdded; 

    ... 
} 
+0

我认为你可以有你'TagPost'对象的复合ID。你能分享'TagPost'类的代码吗? – dcernahoschi 2013-03-22 09:34:47

+0

@dcernahoschi刚刚发布了它 – preslavrachev 2013-03-22 10:21:47

回答

0

我写了一个定制Check对于DB唯一性。也许你应该定制它。

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

它只是检查给class实例给出field以dB独一无二的。也许你应该像这些..

+0

从来没有使用过它,但有一个办法在@Table注释添加唯一约束。也许这会有所帮助? @Table(uniqueConstraints = @ UniqueConstraint(...),) – 2013-03-23 08:49:37

+0

'@UniqueConstraint'在db端创建一个唯一约束。所以如果你想通过JPA持久化模式,JPA会引发异常。你应该处理这个异常。另一方面(用“check”),如果你验证了模态,你可以得到一个validation.error。没有例外,没有trycatches,没有回滚和数据库级别检查.. [检查了这一点](http://www.playframework.com/documentation/1.2.5/validation) – hgoz 2013-03-23 09:22:09