I have two models : User,Project
public class Project{
private int id;
@NotEmpty(message="Project Name can not be empty")
private String name;
private User manager;
private User operator;
//getter/setter omitted
}
public class User{
private int id;
private String name;
//omit other properties and getter/setter
}
Now, when I create a new Project, I will submit the following parameters to ProjectController:
projects?name=jhon&manager.id=1&operator.id=2...
Then I will create a new Project object and insert it to db.
However I have to validate the id of the manager and operator is valid,that's to say I will validate that if there is matched id in the user table.
So I want to know how to implement this kind of validation?
update1:using validator
This is the form for create new project:
<sf:form method="${project.id==0?'post':'put'}" commandName="project" action="${context}${action}">
Manager:<sf:input path="manager.id" /> <sf:errors path="manager.id" /> <br />
Operator:<sf:input path="operator.id" /> <sf:errors path="operator.id" /> <br />
Name:<sf:input path="name" /> <sf:errors path="name" /> <br />
<input type="submit" value="Submit" />
</sf:form>
@Override
public void validate(Object obj, Errors errors) {
User user = (User) obj;
int id=user.getId();
User u=userDao.query(id);
if(u==null){
errors.rejectValue("id", "user does not exist!");
}
}
It seems that this validator works.
However,the error message can not be displayed in the form.
Then though debug I inspect the result object and I found this:
org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'project' on field 'id': rejected value [0]; codes [user does not exist!.project.id,user does not exist!.id,user does not exist!.int,user does not exist!]; arguments []; default message [null]
Field error in object 'project' on field 'id': rejected value [0]; codes [user does not exist!.project.id,user does not exist!.id,user does not exist!.int,user does not exist!]; arguments []; default message [null]
It seems that the result does have errors,but it's path is project.id
while in my form it is project.manager.id
How to fix?
Here's one possible solution.
Create the class below :
...
import org.springframework.validation.Validator;
...
@Component
public class ProjectValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Project.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Project project = (Project) target;
/* Do your checks here */
...
if (managerIdDoesNotMatch) {
errors.rejectValue("manager.id", "your_error_code");
}
...
if (operatorIdDoesNotMatch) {
errors.rejectValue("operator.id", "your_error_code");
}
...
}
}
And in your controller do something like :
...
public class ProjectController {
@Autowired
ProjectValidator projectValidator;
...
@RequestMapping(...)
public String yourCreateMethod(..., @ModelAttribute @Valid Project project, BindingResult result) {
projectValidator.validate(project, result);
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
}
This should get you started. You could instantiate/set the validator differently, have a a user sub-validator, but you get the general idea.
References :
- Latest Spring validation documentation
- This Stack Overflow post about how to perform validation
Actually what you need to do is add @Valid
on
private User manager;
private User operator;
like this
@Valid
private User manager;
@Valid
private User operator;
In your Controller you can add a custom validator:
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new ProjectValidator());
}
In this validator, you could check the User
objects or delegate to a UserValidator
, as done here in the last paragraph before section 6.3
I did what Jerome Dalbert suggested and in addition added a custom BeanValidator for delegating the actual work of validating to a JSR 303 implementation.
The prefix is used to denote the path of the property in the form.
@Component
public class BeanValidator implements org.springframework.validation.Validator, InitializingBean {
private Validator validator;
public void afterPropertiesSet() throws Exception {
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.usingContext().getValidator();
}
public boolean supports(Class clazz) {
return true;
}
public void validate(Object target, Errors errors, String prefix) {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(target);
for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
String propertyPath = constraintViolation.getPropertyPath().toString();
String message = constraintViolation.getMessage();
errors.rejectValue(prefix + "." + propertyPath, "", message);
}
}
public void validate(Object target, Errors errors) {
validate(target, errors, "");
}
}
and here how I used it in the UserValidator:
@Component
public class UserValidator implements Validator {
@Autowired
BeanValidator beanValidator;
@Override
public boolean supports(Class<?> clazz) {
return User.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
beanValidator.validate(user.getAddress(), errors, "address");
}
}
References:
- http://blog.trifork.com/2009/08/04/bean-validation-integrating-jsr-303-with-spring/