在持久化实体之前检查约束违规

时间:2010-01-30 11:32:23

标签: hibernate jpa seam richfaces

在创建之前防止约束违规检查的最佳机制是什么?修改实体?

假设'User'实体的'loginid'作为唯一约束,在创建或修改之前检查是否已存在具有此loginid名称的用户条目是明智的。

OR

您是否允许数据库抛出ConstraintViolationException并在UI层中正确处理此消息。应该在jboss seam框架中强制执行此类检查。

注意:目前没有对seam-gen代码强制执行此类检查。

我们目前使用Seam 2.2,Richfaces和Hibernate。

3 个答案:

答案 0 :(得分:7)

即使您在持久保存用户对象之前检查代码中的条件,也有可能有人在您检查的时间和持久保存新用户之间创建重复的loginid。

但是,如果进行显式检查,在UI中显示相应的错误消息会更容易。如果表上有多个约束,捕获ConstraintViolationException将不允许您轻松确定违反了哪个约束。

所以我会两个都做。假设您正在从Seam的EntityHome扩展:

  1. 在persist()方法中运行查询以确保loginid是唯一的。如果它没有向适当的控件添加错误消息并返回null。
  2. 将调用包装到super.persist()并捕获ConstraintViolationException,显示一般的重复错误消息
  3. 修改

    正如Shervin所说,创建一个JSF Validator是一个好主意(替换)#1,但你应该仍然期待最坏的并捕获ConstraintViolationException。

答案 1 :(得分:6)

我不同意处理ConstraintException。我写了一个验证器,在保存之前检查重复项,并且它运行良好。

以下是检查重复电子邮件的示例。

@Name("emailValidator")
@Validator
@BypassInterceptors
@Transactional
public class UniqueEmailValidator implements javax.faces.validator.Validator, Serializable {

private static final long serialVersionUID = 6086372792387091314L;

@SuppressWarnings("unchecked")
public void validate(FacesContext facesContext, UIComponent component, Object value) throws ValidatorException {
    EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
    String newEmail = (String) value;
    String oldEmail = String.valueOf(component.getAttributes().get("oldEmail"));
    if (oldEmail != null && !oldEmail.equalsIgnoreCase(newEmail)) {
        List<User> users = entityManager.createQuery(
                "SELECT DISTINCT u FROM " + User.class.getName() + " p where lower(p.fromEmail) = :email").setParameter("email",
                newEmail.toLowerCase()).getResultList();
        if (!users.isEmpty()) {
            Map<String, String> messages = Messages.instance();
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.get("admin.emailexists"), messages
                    .get("admin.emailexists")));
        }
    }
}
}

以你的形式(xhtml)写下:

<s:decorate template="/layout/definition.xhtml">
        <ui:define name="label">#{messages['processdata.email']}</ui:define>
        <h:inputText id="fromEmail" size="30" required="true" value="#  {userAdmin.existingUser.fromEmail}">
            <f:validator validatorId="emailValidator"/>
            <f:attribute name="oldEmail" value="#{userAdmin.existingUser.fromEmail}" />
            <s:validate />
        </h:inputText>
    </s:decorate>

这样,它将始终在保存之前验证字段。您甚至可以使用a:support标记来验证焦点何时更改。

答案 2 :(得分:2)

我的建议是,如果你可以检查一个条件然后检查它,即在你的情况下是一个UserExists方法调用。抛出异常是昂贵的,并且用于通常与您的控制之外的事物有关的特殊情况,例如:光盘访问等

在调用实体添加到数据库之前,通常会在业务逻辑中执行此检查。