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...
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.
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.
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.
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.
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.
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;
}
}
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.
精彩评论