我有一个典型的示例,其中POST具有许多TAG,而TAG具有许多POST。
我没有使用典型的@ManyToMany
,而是在中间使用了一个称为TAGPOST的域对象,该对象也允许我在其中包含有用的数据,例如,使用特定标签对帖子进行标记等。每个POST和TAG响应,与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;
...
}
最佳答案
我为数据库唯一性编写了一个自定义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
实例在数据库中是唯一的。也许你应该做这样的事情。