Antonio has written a nice article on how to use Bean Validation and Hibernate Validator 4.0 in JPA 1.0 and Hibernate 3.3 (by retrofitting what's happening automatically in JPA 2.0). He asked an interesting question at the end of his post.
While he understands the benefit of JPA 2.0 calling Bean Validation on entity creation or update, he is wondering about the use case driving the ability to trigger validation when an entity is removed.
The feature was not in initially but a user came with an interesting use case during the review process:
I want to be sure an order is paid before deleting it from the system. If it's not paid, I am not legally allowed to remove it.
Here is the solution:
@Entity class Order {
@Id @GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
@AssertTrue(groups=LegallyRemovable.class) public boolean isPaid() { return paid; }
public void setPaid(boolean paid) { this.paid = paid; }
private boolean paid;
[...]
}
<persistence-unit name="OrderManagement">
<properties>
<property name="javax.persistence.validation.group.pre-remove">com.acme.app.groups.LegallyRemovable</property>
</properties>
</persistence-unit>
The application will likely do something like that at the service layer:
if ( validator.validate(order, LegallyRemovable.class).size() > 0 ) {
throw BusinessException(...);
}
But if for some reason a developper forgets to do it or an order is deleted by cascade, the JPA 2 validation layer will be here to save you.
Note that by default, no group is validated when entities are removed which makes sense for 99% of the use cases out there.
Out of curiosity, is there a way to define what validations get run at what JPA events via annotations?
No. But if you've got a use case for it, feel free to elaborate.
Hi Emmanuel,
I wrote about that topic in my post , too:
http://musingsofaprogrammingaddict.blogspot.com/2010/01/jpa-2-and-bean-validation-in-action.html
The scenario there is similar, customers are only allowed to be deleted, if they have been archived on some kind of long-time storage.
Gunnar
Taking the use case you described above where an order can only be deleted if it is payed: During other events like insert or update the order doesn't have to be payed yet so this validation shouldn't be triggered.
The order must be validated but the fact that an order is paid or not does not make it valid or not. So constraints using the Default group are validated. That's why I use a dedicated group for the LegallyRemovable constraints.
You can change the group being validated at insert time and update time however using the persistence.xml construct
<persistence-unit name="OrderManagement"> <properties> <property name="javax.persistence.validation.group.pre-persist">com.acme.app.groups.MyGroup</property> <property name="javax.persistence.validation.group.pre-update">com.acme.app.groups.MyOtherGroup</property> </properties> </persistence-unit>Hi Emmanuel,
this type of validation is possible using a class-level annotation? @Target({ElementType.TYPE})
using the same context the refactored use case may be:
Imagining that Order has no bi-directional mapping with Item.
Thanks in advance.