Hibernate 在持久化实体之前检查约束冲突

Hibernate 在持久化实体之前检查约束冲突,hibernate,jpa,seam,richfaces,Hibernate,Jpa,Seam,Richfaces,在创建|修改实体之前,防止约束冲突检查的最佳机制是什么 假设“用户”实体将“loginid”作为唯一约束,那么在创建或修改之前检查是否已经存在具有此loginid名称的用户条目是明智的 或 是否允许数据库抛出ConstraintViolationException,并在UI层中适当地处理此消息。在JBossSeam框架中,这些检查应该在哪里强制执行 注意:目前没有对seam gen代码执行此类检查 我们目前使用Seam 2.2,Richfaces和Hibernate。我的建议是,如果您可以检查一

在创建|修改实体之前,防止约束冲突检查的最佳机制是什么

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

是否允许数据库抛出ConstraintViolationException,并在UI层中适当地处理此消息。在JBossSeam框架中,这些检查应该在哪里强制执行

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


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

我的建议是,如果您可以检查一个条件,那么请检查它,即在您的情况下,一个UserExists方法调用。抛出异常非常昂贵,并且适用于通常与您无法控制的事情有关的异常情况,例如光盘访问等


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

即使在持久化用户对象之前检查代码中的条件,在您进行检查和持久化新用户之间,始终有可能会有人创建重复的loginid

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

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

在persist方法中,运行查询以确保loginid是唯一的。如果不正确,请将错误消息添加到相应的控件并返回null。 包装对super.persist的调用并捕获ConstraintViolationException,显示一条通用的重复错误消息 编辑


正如Shervin提到的,创建一个JSF验证器是一个很好的想法,它可以取代上面的1,但是您仍然应该期待最坏的结果,并捕获ConstraintViolationException。

我不同意处理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标记,以便在焦点更改时进行验证。

是的,我唯一关心的是这种方法几乎不需要为记录的任何创建或修改触发查询。我只是在寻找用户喜欢的最佳方法。感谢问题是:在DB的每次写入操作上进行测试,还是只有在出现问题时才处理异常,这是什么更昂贵?如果独立客户端直接调用JPA,如何防止这些问题。我们如何使代码在这些场景中可重用?独立客户端总是直接调用JPA,还是可以强制它通过某种DAO?我们正在使用Seam组件EntityHome、EntityQuery接口,它们目前充当用户界面和JPA持久层之间的粘合剂。我不清楚是否可以强制独立客户端使用Seam抽象层而不是直接使用JPA层来实现持久性。如果必须存在Seam层,那么Seam库需要作为客户端工具包的一部分提供。不确定这里的最佳方法是什么,它可能会导致大量额外的查询,这将严重影响性能。如果您的数据库处理unique,那么它将抛出异常,并且您的常规异常处理程序将修复它。在流程验证和更新模型JSF阶段之间,仍然有可能使用重复的电子邮件更新您的数据库,并引发ConstraintException。如果您使用乐观锁定,即@Version,则不可能发生这种情况