Hibernate Bean验证:接收EntityNotFoundException,但有一个验证实体的约束

Hibernate Bean验证:接收EntityNotFoundException,但有一个验证实体的约束,hibernate,hibernate-validator,bean-validation,Hibernate,Hibernate Validator,Bean Validation,技术:Spring、Hibernate、JSR-303、JQuery 平台:Windows 我正在尝试实现@IdMustExist JSR-303约束。约束的目的是检查关联表中是否存在输入的id值。有关IdMustExist.java和IdMustExistValidator.java代码段,请参见本文 场景1-输入有效的Id值:在这种情况下,当执行hibernate的EntityManager.merge操作时,我看到@IdMustExist约束被执行。它成功验证输入的Id值是否存在于另一个关

技术:Spring、Hibernate、JSR-303、JQuery
平台:Windows
我正在尝试实现@IdMustExist JSR-303约束。约束的目的是检查关联表中是否存在输入的id值。有关IdMustExist.java和IdMustExistValidator.java代码段,请参见本文

场景1-输入有效的Id值:在这种情况下,当执行hibernate的EntityManager.merge操作时,我看到@IdMustExist约束被执行。它成功验证输入的Id值是否存在于另一个关联/链接表中。Hibernate成功完成保存操作。我注意到hibernate在调用@IdMustExist的验证器之前触发了实体的SELECTSQL

场景2-输入的Id值无效:在这种情况下,当执行hibernate的EntityManager.merge操作时,它抛出EntityNotFoundException(下面给出了堆栈跟踪)因为它找不到输入的无效Id的实体。我希望@IdMustExist约束在预更新阶段被触发,我们可以优雅地向用户显示错误消息。但在hibernate进入预更新阶段之前,似乎有些东西失败了。我看到hibernate为实体激发select SQL,然后抛出EntityNotFoundException。它没有机会调用validate@IdMustExist

这是否意味着我无法通过JSR-303验证输入的Id是否存在于关联/链接表中?还有别的选择吗

提前谢谢你的帮助。 太平绅士

HibernateClass2Dao.java

    public void save(GenericRequest request, GenericResponse response){
    Class2 class2Request = (Class2) request.getObject(Class2.class.getName());
    EntityManager em = PersistenceUtil.getEntityManagerFactory().createEntityManager();
    Class2 class2Persisted = em.find(Class2.class, class2Request.getId());
    EntityTransaction tx = em.getTransaction();
    try {
        tx.begin();
        class2Persisted.setClass1(class2Request.getClass1());
        em.merge(class2Request);
        tx.commit();            
    } catch (RollbackException rbe) {
        response.setSuccess(false);
        rbe.getCause().printStackTrace();
        ConstraintViolationException cve = (ConstraintViolationException) rbe.getCause();
        Set<ConstraintViolation<?>> constraintViolations = cve.getConstraintViolations();
        if (constraintViolations.size() > 0){
            for (ConstraintViolation<?> violation : constraintViolations){
                Iterator<Node> itr = violation.getPropertyPath().iterator();
                String propertyPath = itr.next().getName();
                Class<? extends Payload> payload = violation.getConstraintDescriptor().getPayload().iterator().next();
                String payloadName = payload.getCanonicalName();                
                response.addMessage(violation.getMessage(), violation.getLeafBean().getClass().getName(),
                        propertyPath , payloadName);            
            }
        }
    } finally {
        em.close();         
    }
}
    public void update(GenericRequest request, GenericResponse response){
    class2Dao.save(request, response);
}
    @RequestMapping(value="/update")
public @ResponseBody GenericResponse update(String jqGridId, String oper
            , Class2 class2
            , BindingResult bindingResult) throws Exception {
    GenericResponse response = new GenericResponse(true);
    response.setMessageSource(messageSource);
    Locale locale = new Locale(CommonConstants.DEFAULT_LOCALE);
    if (bindingResult.hasErrors()){
        response.setSuccess(false);
        addBindingMessages(response, bindingResult, locale);
        return response;
    }
    class2.setId(Long.parseLong(jqGridId));
    class2.setCode(jqGridId);
    GenericRequest request = new GenericRequest(UserUtil.getUser(), class2);
    this.class2Service.update(request, response);
    return response;        
}   
Class2Controller.java

    public void save(GenericRequest request, GenericResponse response){
    Class2 class2Request = (Class2) request.getObject(Class2.class.getName());
    EntityManager em = PersistenceUtil.getEntityManagerFactory().createEntityManager();
    Class2 class2Persisted = em.find(Class2.class, class2Request.getId());
    EntityTransaction tx = em.getTransaction();
    try {
        tx.begin();
        class2Persisted.setClass1(class2Request.getClass1());
        em.merge(class2Request);
        tx.commit();            
    } catch (RollbackException rbe) {
        response.setSuccess(false);
        rbe.getCause().printStackTrace();
        ConstraintViolationException cve = (ConstraintViolationException) rbe.getCause();
        Set<ConstraintViolation<?>> constraintViolations = cve.getConstraintViolations();
        if (constraintViolations.size() > 0){
            for (ConstraintViolation<?> violation : constraintViolations){
                Iterator<Node> itr = violation.getPropertyPath().iterator();
                String propertyPath = itr.next().getName();
                Class<? extends Payload> payload = violation.getConstraintDescriptor().getPayload().iterator().next();
                String payloadName = payload.getCanonicalName();                
                response.addMessage(violation.getMessage(), violation.getLeafBean().getClass().getName(),
                        propertyPath , payloadName);            
            }
        }
    } finally {
        em.close();         
    }
}
    public void update(GenericRequest request, GenericResponse response){
    class2Dao.save(request, response);
}
    @RequestMapping(value="/update")
public @ResponseBody GenericResponse update(String jqGridId, String oper
            , Class2 class2
            , BindingResult bindingResult) throws Exception {
    GenericResponse response = new GenericResponse(true);
    response.setMessageSource(messageSource);
    Locale locale = new Locale(CommonConstants.DEFAULT_LOCALE);
    if (bindingResult.hasErrors()){
        response.setSuccess(false);
        addBindingMessages(response, bindingResult, locale);
        return response;
    }
    class2.setId(Long.parseLong(jqGridId));
    class2.setCode(jqGridId);
    GenericRequest request = new GenericRequest(UserUtil.getUser(), class2);
    this.class2Service.update(request, response);
    return response;        
}   

您引用的链接没有显示DAO的代码,但我假设您正在使用会话中的
load()
方法。请参阅javadoc:

假设实例存在,返回具有给定标识符的给定实体类的持久实例

请注意它所说的“假设实例存在”部分。也就是说,您知道某个实体存在,并要求Hibernate加载它。如果它不存在,则该假设被打破,并引发异常

您可能需要使用
get()
方法。看看javadoc是怎么说的:

返回具有给定标识符的给定实体类的持久实例,如果没有此类持久实例,则返回null

因此,解决方案是从
load()
更改为
get()

Javadoc:

这里有几件事。您的CommonDao正在使用哪个会话。实际上,这应该是在当前连接上打开的新会话。Hibernate validator wiki上讨论了使用会话(或约束验证器中的实体管理器)的整个问题:


不过,我不推荐这种方法。我只需要调用merge并处理Hibernate核心(或JPA)异常。

@IdMustExist右键执行select查询。当id不可用时,不会引发entity not found异常。我看到的唯一优雅的方法是在select查询周围有一个try-catch块(用于NoEntityFoundException)。它不是已检查的异常,但您可以优雅地处理退出条件。该异常不是由@IdMustExist引发的。我在@IdMustExist.isValid方法中有调试语句,但它们不显示。在@IdMustExist有机会验证之前,Hibernate会抛出异常。谢谢!请回答。我已经更新了上面主要问题中HibernateClass2DAO、Class2ServiceImpl和Class2Controller类的代码片段。请复习。我没有调用load方法。Class2实例是spring创建的一个新对象,用于绑定请求参数。我只调用EntityManager.merge()方法。我猜hibernate反过来调用load方法。请查看EntityNotFound异常堆栈跟踪,该跟踪调用了entityIsDetached()方法、fireLoad()方法等。是否有办法抑制hibernate以不加载关联对象?还有其他的设计吗?我想你在规格上遇到了一个灰色地带。我必须阅读JSR,看看它是否涵盖了这一部分,但似乎没有,特别是考虑到您正在为对象设置ID。通常,我会说,如果您有一个标记为“@Id”的属性,并且该字段已填充,“merge”将假定该实体存在。事实并非如此,这是其假设的一个例外。请您在不设置@Id的情况下复制此内容,好吗?根据我对规范的理解,我认为它不应该抛出任何异常,而是应该将这个新实体持久化到数据库中。但无论如何,我的理解是Hibernate做的是正确的。因此,我将执行两个操作:1)对具有给定ID的实体发出“get”(或查询)。2)执行合并操作(如果存在)。谢谢!帕蒂农。我会尝试你的建议并确认。我认为应该行得通。我知道Class2实体存在,因此设置的Id是正确的。但Class2中提到的Class1并不存在。抛出的异常是针对class1的。你是对的!请参阅上面修改的HibernateClass2Dao.java源代码。我首先检索Class2并手动设置Class2.setClass1。然后我调用em.merge()。我不再获得EntityNotFound异常,并且按照预期执行@IdMustExist约束,引发ConstraintViolation异常。谢谢再一次。我有另一个相关的问题,我正在创建一个新的职位。