views:

387

answers:

1

Let's say I have the following command bean for creating a user:

public class CreateUserCommand {

   private String userName;
   private String email;

   private Integer occupationId;
   pirvate Integer countryId;

}

occupationId and countryId are drop down selected values on the form. They map to an entity in the database (Occupation, Country).

This command object is going to be fed to a service facade like so:

userServiceFacade.createUser(CreateUserCommand command);

This facade will construct a user entity to be sent to the actual service. So I suppose that in the facade layer I will have to make several dao calls to map all the lookup properties of the User entity.

Based on this what is the best strategy to validate that occupationId and countryId map to real entities? Where is the best place to perform this validation? There is the spring validator but I am not sure this is the best place for this, for one I am wary of this method as validation is tied to the web tier, but also that means I would need to make the dao calls in the validator for validation but I would need to call the dao's in the facade layer again when the command -> entity translation occurs.

Is there anything I can do better?

Thanks.

+1  A: 

Are you sure you need a DTO to retrieve a Entity ?

I suppose your User is as follows

public class User implements Serializable {

    private String userName;
    private String email;

    private Occupation occupation;
    private Country country;

}

Usually, a reference to another Entity when displaying your form is by using some <select HTML element, which is similar to

<select name="occupation">
    <option name="0">A occupaation</option>
    <option name="1">Other occupaation</option>
    <option name="2">Another occupaation</option>
</select>

Spring uses data-dinding to bind your select HTML element to your property. The method used is called initBind

public class UserController extends BaseCommandController {

    private OccupationRepository occupationRepository;
    private CountryRepository countryRepository;

    // getter's and setter's (retrieved by Dependency Injection supported by Spring)

    public UserController() {
        setCommandClass(User.class);
        setValidator(new UserValidator());
    }

    public void initBind(HttpServletRequest request, ServletRequestDataBinder binder) {
        binder.registerCustomPropertyEditor(Occupation.class, new PropertyEditorSupport() {

            public void setAsText(String occupationId) {
                // StringUtils belongs to jakarta-commons lang 
                if(StringUtils.isBlank(occupationId)) {
                    setValue(null);

                    return;
                }

                setValue(occupationRepository.getById(Integer.valueOf(occupationId)));
            }

            public String getAsText() {
                if(getValue() == null)
                    return;

                return String.valueOf(((Occupation) getValue()).getId());
            }
        });

        // Same approach when binding Country
    }

}

Notice you can replace initBind method by assigning a WebBindingInitializer object

public class UserBindingInitializer implements WebBindingInitializer {

    private OccupationRepository occupationRepository;
    private CountryRepository countryRepository;

    // getter's and setter's (retrieved by Dependency Injection supported by Spring)

    public void initBinder(WebDataBinder binder, WebRequest request) {
        binder.registerCustomPropertyEditor(Occupation.class, new PropertyEditorSupport() {

            public void setAsText(String occupationId) {
                // StringUtils belongs to jakarta-commons lang 
                if(StringUtils.isBlank(occupationId)) {
                    setValue(null);

                    return;
                }

                setValue(occupationRepository.getById(Integer.valueOf(occupationId)));
            }

            public String getAsText() {
                if(getValue() == null)
                    return;

                return String.valueOf(((Occupation) getValue()).getId());
            }
        });
    }

}

...

public class UserController extends BaseCommandController {

    private OccupationRepository occupationRepository;
    private CountryRepository countryRepository;

    // getter's and setter's (retrieved by Dependency Injection supported by Spring)

    public void setUserBindingInitializer(UserBindingInitializer bindingInitializer) {
        setWebBindingInitializer(bindingInitializer);
    }

And your UserValidator (Notice your Validator does not know anything else about any repository)

public class UserValidator implements Validator {

    public boolean supports(Class clazz) {
        return clazz.isAssignableFrom(User.class);
    }

    public void validate(Object command, Errors errors) {
        User user = (User) command;

        if(user.getOccupation() == null)
            errors.rejectValue("occupation", "errors.required", null);

        if(user.getCountry() == null)
            errors.rejectValue("country", "errors.required", null);
    }

}
Arthur Ronald F D Garcia
Thanks for the reply. Well the reason for the DTO is that the domain (hibernate) object is pretty complex with a lot of relationships, methods, etc. The user example here is for illustration only. That is why I would like to shield the presentation layer from the domain. In this case I would need to perform the validation described above because I dont want to expose the entities.I had planned on using Validator although that would place the validation logic on the web tier which seems dubious to me. Unless validator should not only make simple field checks?
arrages
@arrages Validator has been designed to perform simple validation. Something like isInteger, isNotBlank, isNotNull, nothing else. When using complex validation, You had better to place it inside your business logic. Notice i also use, as said by yourself, pretty complex with a lot of relationships, methods, without no problem.
Arthur Ronald F D Garcia