Reputation: 449
I have just implemented Bean Validation with Hibernate.
If I call the validator explicitly it works as expected and my @Autowired DAO bean that connects to the DB is injected as expected.
I had previously discovered that I needed to add the statement below before the above would work. I had made extensive use of @Autowired beans before but the statement below was necessary for the validator to be managed by Spring and the bean injected into the ConstraintValidator.
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
However when the validator is called automatically during a SessionFactory.getCurrentSession.merge the bean is null.
The fact that it works if I invoke the validator directly with a call to javax.Validation.validate makes me think that I have set up the Spring configuration correctly.
I have read a number for posts where people have been unable to get the DAO bean @Autowired but in my case it is except when called during the merge.
The log output below shows the validator being called directly first and then being called as as a result of a merge operation.
07.12.2011 01:58:13 INFO [http-8080-1] (FileTypeAndClassValidator:isValid) - Validating ...
07.12.2011 01:58:13 INFO [http-8080-1] (ConstraintValidatorHelper:getPropertyValue) - propertyName=className, returnValue=com.twoh.dto.PurchaseOrder
07.12.2011 01:58:13 INFO [http-8080-1] (ConstraintValidatorHelper:getPropertyValue) - propertyName=fileTypeId, returnValue=4
07.12.2011 01:58:13 INFO [http-8080-1] (QueryUtil:createHQLQuery) - select ft.id from FileType ft where ft.id = :fileTypeId and ft.fileClassName = :fileClassName
07.12.2011 01:58:13 INFO [http-8080-1] (BaseDAO:merge) - Entity: com.twoh.dto.PurchaseOrder: 1036.
07.12.2011 01:58:13 INFO [http-8080-1] (FileTypeAndClassValidator:isValid) - Validating ...
07.12.2011 01:58:13 INFO [http-8080-1] (ConstraintValidatorHelper:getPropertyValue) - propertyName=className, returnValue=com.twoh.dto.PurchaseOrder
07.12.2011 01:58:13 INFO [http-8080-1] (ConstraintValidatorHelper:getPropertyValue) - propertyName=fileTypeId, returnValue=4
07.12.2011 01:58:13 INFO [http-8080-1] (FileTypeAndClassValidator:isValid) - java.lang.NullPointerException
Below is the code for the ConstraintValidator:
package com.twoh.dto.ConstraintValidation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.twoh.dao.IQueryUtil;
@Component
public class FileTypeAndClassValidator implements ConstraintValidator<FileTypeAndClass, Object> {
private Log logger = LogFactory.getLog(this.getClass());
private String fileClassProperty;
private String fileTypeProperty;
@Autowired
private IQueryUtil queryUtil;
public void initialize(FileTypeAndClass constraintAnnotation) {
this.fileClassProperty = constraintAnnotation.fileClassProperty();
this.fileTypeProperty = constraintAnnotation.fileTypeProperty();
}
public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
boolean result = true;
logger.info("Validating ...");
if (object == null) {
result = false;
} else {
try {
String fileClassName = ConstraintValidatorHelper.getPropertyValue(String.class, fileClassProperty, object);
Integer fileTypeId = ConstraintValidatorHelper.getPropertyValue(Integer.class, fileTypeProperty, object);
result = queryUtil.createHQLQuery((
"select ft.id" +
" from FileType ft" +
" where ft.id = :fileTypeId" +
" and ft.fileClassName = :fileClassName"
))
.setParameter("fileTypeId", fileTypeId)
.setParameter("fileClassName", fileClassName)
.iterate().hasNext();
} catch (Exception e) {
logger.info(e);
}
}
return result;
}
}
Upvotes: 2
Views: 5532
Reputation: 2154
You can use the following method provided by Spring framework since 2.5.1
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
it's much cleaner since you don't have to set any listener/property across your application.
You code will look like this:
public class ValidUniqueUserEmailValidator implements ConstraintValidator<ValidUniqueUserEmail, Object>, Serializable {
private static final long serialVersionUID = 1L;
@Autowired
private UserDAO userDAO;
@Override
public void initialize(ValidUniqueUserEmail constraintAnnotation) {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
boolean isValid = true;
if (value instanceof String) {
String email = value.toString();
if (email == null || email.equals("")) {
isValid = false;
}else{
User user = new User();
user.setEmail(email);
isValid = (userDAO.countByEmail(user) > 0);
}
}
return isValid;
}
}
Hope it helps you guys out
Upvotes: 7
Reputation: 449
Ok after about 18hrs of googling I finally came across a site that both described the problem it has a solution. Recipe: Using Hibernate event-based validation with custom JSR-303 validators and Spring autowired injection
In my current project, we wanted to build a custom validator that would check if an e-mail address already existed in the database before saving any instance of our Contact entity using Hibernate. This validator needed a DAO to be injected in order to check for the existence of the e-mail address in the database. To our surprise, what we thought would be a breeze was more of a gale. Spring’s injection did not work at all when our bean was validated in the context of Hibernate’s event-based validation (in our case, the pre-insert event).
In the end my spring configuration ended up looking like this:
...
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="beanValidationEventListener" class="org.hibernate.cfg.beanvalidation.BeanValidationEventListener">
<constructor-arg ref="validator"/>
<constructor-arg ref="hibernateProperties"/>
</bean>
...
<util:properties id="hibernateProperties" location="classpath:hibernate.properties"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="com.twoh" />
<property name="hibernateProperties" ref="hibernateProperties"/>
<property name="eventListeners">
<map>
<entry key="pre-insert" value-ref="beanValidationEventListener" />
<entry key="pre-update" value-ref="beanValidationEventListener" />
</map>
</property>
</bean>
Upvotes: 6