Bean验证:接收EntityNotFoundException但具有验证实体的约束

时间:2011-02-01 16:51:07

标签: hibernate hibernate-validator bean-validation

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

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

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

是否意味着,我无法通过JSR-303验证输入的Id是否存在于关联/链接表中?任何替代方案?

提前感谢您的帮助。 JP

javax.persistence.EntityNotFoundException: Unable to find com.mycompany.myapp.domain.package1.Class1 with id 100
at org.hibernate.ejb.Ejb3Configuration$Ejb3EntityNotFoundDelegate.handleEntityNotFound(Ejb3Configuration.java:133)
at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:233)
at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1080)
at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1028)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:623)
at org.hibernate.type.EntityType.resolve(EntityType.java:431)
at org.hibernate.type.EntityType.replace(EntityType.java:291)
at org.hibernate.type.TypeFactory.replace(TypeFactory.java:532)
at org.hibernate.event.def.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:495)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:423)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:234)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:859)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:843)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:847)
at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:682)
at com.mycompany.myapp.dao.hibernate.package2.HibernateClass2Dao.save(HibernateClass2Dao.java:101)
at com.mycompany.myapp.service.package2.Class1ServiceImpl.update(Class1ServiceImpl.java:56)
    ...
    ...
at java.lang.Thread.run(Thread.java:619)

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();         
    }
}

Class2ServiceImpl.java

    public void update(GenericRequest request, GenericResponse response){
    class2Dao.save(request, response);
}

Class2Controller.java

    @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;        
}   

2 个答案:

答案 0 :(得分:1)

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

  

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

注意它所说的部分“假设实例存在。”。意味着,你知道某些实体存在,并且你要求Hibernate加载它。如果它不存在,则该假设被破坏,并抛出异常。

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

  

使用给定的标识符返回给定实体类的持久化实例,如果没有这样的持久化实例,则返回null。

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

Javadoc:http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/Session.html

答案 1 :(得分:0)

这里有几件事。 Session 是您的 CommonDao 使用的。真的,这应该是在当前连接上打开的新会话。在Hibernate Validator wiki上讨论了使用会话(或约束验证器中的实体管理器)的整个问题:http://community.jboss.org/wiki/AccessingtheHibernateSessionwithinaConstraintValidator

我伤口不推荐这种方法。我只需要调用 merge 并处理Hibernate Core(或JPA)异常。