-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 2:40 am 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Hi everyone,

I have an object which contains a collection of objects. Both are validated by Hibernate Validator.

Now, there is a form that presents everything all on the same page, so we need to do validation on the whole thing all at once. While I can definitely validate it properly, getting the error messages printed to the right fields is another thing entirely.

In Spring mvc, we use the following notation for collections:

Code:
myItems[0].property


So, for example, if we are editing all the province names and short forms on a single form, the paths might look like this:

Code:
country.provinces[0].name
country.provinces[0].code
country.provinces[1].name
country.provinces[2].code
...
etc.


Now, in order for spring's error binding to work, we need to pass "country.provinces[0].name". Unfortunately, "name" is the only thing that Hibernate validator gives us back, which is not good to solve this problem. Even worse, Spring tries to bind that path of "name" onto the parent object... and if for the sake of giving an example, let's say country did not have a property called "name", Spring would just crash outright because their reflection api would look for a property that doesn't exist.

Unlike component classes like "entity.component.name" where you say @Valid to get a nested path within Hibernate Validator, the @Valid does not work for collections.

So given this problem, what is the best way to solve it? In my case (it's not the province example), I cannot split up the form into separate forms.

Help?


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 5:50 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

Quote:
Now, in order for spring's error binding to work, we need to pass "country.provinces[0].name". Unfortunately, "name" is the only thing that Hibernate validator gives us back, which is not good to solve this problem.

I am not sure I understand. What or whom are you passing "country.provinces[0].name" and why is "name is the only thing Hibernate Validator returns? Are you using Validator.validate or Validator.validateProperty? Maybe you can post some example code.
Otherwise I don't have not much knowledge about what and how spring does validation. I think they are also working on doing a Bean Validation integration. It might be worth checking the Spring Forum for that.

Quote:
Unlike component classes like "entity.component.name" where you say @Valid to get a nested path within Hibernate Validator, the @Valid does not work for collections.

Why are you saying @Valid is not working for collections? Sure it is. Unless we are talking about different things. You for sure can place a @Valid on a collection and Hibernate Validator will validate each item in the collection.

--Hardy


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 6:43 am 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Hi Hardy!

Yep, @Valid is validating the collection just fine - this is a problem with the property path.

For example, if a component has @Valid and is contained within another object that is the parent, Hibernate Validator will create the proper path.

So let's say we have Customer and Address. If @Valid is on top of Address, Hibernate Validator will correctly say, "customer.address.street" <- has the error. The property path is correct.

Now, in order to satisfy spring mvc, I need to do the same thing with collections. However, Hibernate validator is not constructing the path.

Code:
class Country {

    @Valid
    List<Province> provinces = new ArrayList<Province>();

....
}


if there is a problem with a property on Province, even though we are starting from Country, Hibernate Validator will not make the property path:

Code:
country.provinces[3].name <- property path.


Instead, Hibernate Validator just says, "name" for the property path.

However, since Country is the parent object and there is no name property, this is incorrect.

In my actual case, this is the output form Hibernate Validator:
Code:
DEBUG ProcessForm:55 - Constraint Violated: {questionnaireAnswer.text.notEmpty}
DEBUG ProcessForm:84 - Adding error on path [text] with message [Text may not be empty]


And this is the error spring throws as a result:
Code:
    *   NotReadablePropertyException: Invalid property 'text' of bean class [myproject.domain.candidate.Candidate]: Bean property 'text' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?


Candidate (in my application) has no "text" property. In fact, this text property is a property of an object that is inside of a collection contained within Candidate. In a sense, Candidate is Country and the questionnaireAnswer is the province.

What would be helpful is Hibernate validator giving collection-aware paths ;) Or is there a way to do it? Am I doing it wrong?

Thanks Hardy.


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 6:57 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Hi,

something must be wrong here. In your country example the path you should get back when validating a country instance is indeed something like country.provinces[3].name. That is if you rely on the toString implementation of Hibernate's PathImpl. If you check the API of ConstraintViolation you will notice that ConstraintViolation.getPropertyPath() actually returns an instance of javax.validation.Path. Using this Path object you can get an iterator over Node instances. So in the spec there is no EL property path defined. If you want to code against the specification you have to use the Path API to validate the property path.

Hibernate Validator's PathImpl also implements toString and returns in most cases what you expect in sense of EL. However, since there is no official standard for the property string it you probably should not depend on the toString implementation (it is not portable!)

Hope this makes sense.

--Hardy


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 2:07 pm 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Oh, I think I know the problem!!!!

The collection is cascade="all-delete-orphan". Could that be why I'm only getting the single element? Man, that's totally it! It has to be.

Umm... does that mean I should turn off cascade and change all the code to save these instances manually?


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 2:49 pm 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
I am using this as the property path:

Code:
path.append( constraintViolation.getPropertyPath() );

So I guess I am using the toString method. I tried calling iterator on that Path object and I get a node with text. I don't think it has more to it, or does it? I should construct the path on my own basically?
-----
I'm trying different inverse/cascade options to get the effect I want, but I don't think that's it. realized that the way I had it should be fine.

Here is the test that asserts hibernate validator should be giving me the correct path when there is a validation error:

Code:
   @Test
   public void submitQuestionnaireFormShouldThrowCorrectErrorPath() {
      Candidate candidate =
         jobApplicationService.createJobQuestionnaireAnswers( 1 );
      assertEquals( 1, candidate.getAnswers().size() );

      // this causes the validation error.
      candidate.getAnswers().get( 0 ).setText( "" );

      result = new BeanPropertyBindingResult( candidate, "candidate" );

      try {
         controller.submitQuestionnaireForm( candidate, result, status );
         flush();
      } catch( ConstraintViolationException e ) {
         assertEquals( "candidate.answers[0].text",
            e.getConstraintViolations().iterator().next().
               getPropertyPath() );
      }
   }


controller.submitQuestionnaireForm( ... ) basically just calls into a service that saves the root Candidate object. Hibernate then cascades and saves the collection "answers"... and then the bean validation event listener kicks in and throws the ConstraintViolationException.

Once the exception is thrown, the test fails as it returns "text" instead of what I'm after.

Does this give you more indication as to what is going on?


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 3:12 pm 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Okay, I played with the Path object a bit and it's not giving me what I need to construct the correct path. According to it, the root object is the answer - not the candidate.

There's only a few things that come to my mind here.

1. This is kind of a similar problem to components validating on cascade, but not giving the right path, such as "customer.address.postalCode". @Valid was used to fix this. Hibernate Validator uses @Valid to construct the full path.

@Valid doesn't seem to do anything with collections. Removing it or including it has no effect on the outcome at all. If there is a problem here, perhaps @Valid could instruct the constraint builder code to make the collection part of the whole root object and not a separate object.

2. I'm not calling into the right method - i.e. getPropertyPath() is incorrect in these cases and I need to do something else.

3. Hibernate Validator/BeanValidationEventListener does not support the desired functionality.

Right now, I'm thinking using a custom constraint that validates on top of the collection... and then validating all the elements in turn and adding the constraints manually through the context that is passed in isValid (we've discussed that before). That to me is the line of attack for this problem unless there is something already present in the framework that can do what I want with little hassle ;)


Top
 Profile  
 
 Post subject: Re: Collection validation path not interoperable with spring mvc
PostPosted: Tue Oct 27, 2009 4:29 pm 
Regular
Regular

Joined: Tue May 12, 2009 6:08 am
Posts: 92
Okay, I figured out a solution. If I prevalidate the object before I send it to the database, Hibernate validator gives me what I need. I guess that makes sense... I was just hoping the hibernate event listener was smart enough to do this for me. I don't know why I didn't think of this last night. I was probably tired :/


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.