views:

253

answers:

7

AFAIK JSR-303 is the standard bean validation system.

I don't know whether it could do validations like this (I guess no):

  • if an object has a deleted flag set, you cannot modify the object
  • you cannot change the start date property, after the date is passed
  • you cannot decrease some integer properties in the bean

So how can I handle validations, which depend on the previous state of an object?

I would like to solve problems like that in hibernate3.5 - spring3 - JPA2 environment.

Thanks


My solution was to mess with hibernate, reload the object to see the old state (after evicting the new object). This time I need some smarter solution...

+1  A: 

I don't think this can be done using JSR 303 validation (or any other validation framework I've used). Validation is usually stateless - you pass it an instance of an object, and your validation framework tests things to make sure the current values of your object are valid. There's no real knowledge of previous states of the object.

You can do this - just not with validation. You could use a constrained property, or you could make this work using the proxy pattern or AOP.

Nate
Thanks, constrained property will only fit, if the constraint affects only one property (as in all of my examples BTW, but it is a limitation...)
pihentagy
A: 

how can I handle validations, which depend on the previous state of an object?

I'm not 100% sure it's doable but the only way I can think of would be to create an object graph made of the "new state" and the "old-state" (transient) and to validate the object graph as a whole using custom constraints. That's at least what I would try.

Pascal Thivent
A: 

I would probably create a transient field that says previous version which points to a copy of the data that represents its previous state. This object is created on construction but since it is marked as transient it is not serialized. Then do the validations against it.

Simplest implementation would be to add a method called makeACopy() which makes a copy of the object and put it into the field.

You can add complexity by implementing Clonable or creating a utility class that would do reflection, but that's up to you. I suggest makeACopy() and refactor later since it is easier to think about.

Archimedes Trajano
A: 

It sounds like the fields which you want to validate (with regards to previous state) are all metadata about the records as opposed to real data. All of these fields (idDeleted, createdDate, etc.) are better left out of your domain layer and therefor do not require validation. I would put the logic for determining & setting these values in you data-access layer so that the systems using your repository interfaces do not need to know or care about getting them right.

If my assumption about these fields being meta-data is not correct and you have user-entered data which validation depends on previous state, then I do not think that an extra lookup for the previous values is absurd and should not be out of the question. It makes sense in your case. Hibernate itself does a lookup under then hood to determine whether to INSERT or UPDATE when using it's save function.

Hope you find a reasonable solution.

Gweebz
A: 

I don't know any ready-to-use solution either. As you suspect JSR-303 won't do the job, because it's validation is 'static'.

But...

An idea would be to use some AOP techniques to do that. So...

if an object has a deleted flag set, you cannot modify the object

This one I would implement as a proxy method registered around every setter. The proxy method would check the 'deleted' flag. If it was set to true, an exception would be thrown, otherwise the original method would be executed.

you cannot change the start date property, after the date is passed

This one is similar. This time you wouldn't access any other property in the intercepted setter, but the original (not changed yet) value of the field and setter argument.

you cannot decrease some integer properties in the bean

That one is the same as with the dates, the only difference is the date type (date vs integer).

One can argue if AOP is a good choice for this task, but still a solution. I am doubtful too.

One more concern is that I guess you would want to enforce these contraints on JPA entities. So using Spring AOP wouldn't be that easy, since the entities wouldn't be Spring managed.

Grzegorz Oledzki
A: 

A completely different approach is to put the validation checks into the setters of properties. The downside is that you would lose declarativeness.

Example:

public void setCounter(int newCounter) {
    if (newCounter < this.counter) {
       throw new IllegalOperationException("Cannot decrease the counter");
    } else {
       this.counter = newCounter;
    }
}
Grzegorz Oledzki
and you should set your properties in correct order (think about rules, that validate more properties), or your logic will blow up...
pihentagy
A: 

You might want to look at OVal instead. We do this kind of validation all the time. Normally, it's done using the SimpleCheck where you get the object and the value and can do all kinds of cross-checking.