Technologies: Spring, Hibernate, JSR-303, JQuery
Platform: Windows I am trying to implement @IdMustExist JSR-303 constraint. The purpose of the constraint is to check whether the entered id value exists in associated table. Please see this link for the IdMustExist.java and IdMustExistValidator.java code snippets.Scenario 1 - Valid Id value is entered: In this case, when hibernate's EntityManager.merge operation is executed, I see that the @IdMustExist constraint is executed. It successfully validates that the entered Id value exists in another associated/linked table. Hibernate completes the save operation successfully. I noticed that hibernate fires select SQL for the entity before calling the validator for @IdMustExist.
Scenario 2 - Invalid Id value is entered: In this case, when hibernate's EntityManager.merge operation is executed, it throws EntityNotFoundException (stack trace given below) as it could not find the entity for the entered invalid Id. I was hoping that the constraint @IdMustExist gets fired in the pre-update stage and we can gracefully show error message to the user. But it looks like something fails before hibernate can enter pre-update stage. I see that hibernate fires select SQLs for the entity and then throws the EntityNotFoundException. It does not get a chance to call validate @IdMustExist.
Does it mean, I cannot validate whether the entered Id exists in associated/linked table through JSR-303? Any alternatives?
Thanks for your help in advance. 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) r开发者_开发知识库be.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;
}
Your referenced link doesn't shows the code for the DAO, but I assume you are using the load()
method from the Session. See the javadoc:
Return the persistent instance of the given entity class with the given identifier, assuming that the instance exists.
Note the part that it says "assuming that the instance exists.". Means, you know that some entity exists, and you are asking Hibernate to load it. If it doesn't exists, this assumption is broken, and an exception is thrown.
What you may want is to use the get()
method instead. See what the javadoc says:
Return the persistent instance of the given entity class with the given identifier, or null if there is no such persistent instance.
So, the solution is to change from load()
to get()
.
Javadoc: http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/Session.html
Several things here. What Session is your CommonDao using. Really this should be a new session opened on the current connection. The whole issue around using a session (or entity manager within a constraint validator) is discussed on the Hibernate Validator wiki: http://community.jboss.org/wiki/AccessingtheHibernateSessionwithinaConstraintValidator
I wound not recommend this approach though. I would just call merge and deal with the Hibernate Core (or JPA) exceptions.
精彩评论