Bean validation in Java EE

20 March 2008

Stéphane Épardaud

by Stéphane Épardaud

This article talks about the process of validation in Java EE, more specifically about Hibernate Validation and Bean Validation. We start by describing why we need validation, what solutions are available, how we use them and why they are great. We then proceed to describe their limitations, and offer proposals for resolving those limitations in the hope that the future Bean Validation standard will incorporate our (or similar) solutions.

What is validation and what is wrong with Java EE validation.

If you are writing your database persistence interface in Java, chances are you will be using the Java persistence API. It abstracts database tables as Java classes (entities in the local jargon), database table columns as object properties (in the Java Bean sense), and database table rows as instances of those classes. The mapping between Java classes and the database model is done using annotations.

This API (javax.persistence) defines a few ways to put constraints on your properties, for instance column or multi-column unicity, or nullability of columns.

If you try to save (persist or merge in the local jargon) entities in which those constraints have been violated, you will get an ugly exception, and the transaction will probably be rolled back (if that is what you asked for).

When I say ugly exception I mean an exception which has no clear semantics as to what the exception class is, how to get to it (they nest exceptions in non-standard way), how to get to the name of the violated constraint(s) (which incidentally cannot be named by hand in the annotation), and does not allow an application to specify a message to display to signal errors of a particular constraint.

Additionally, it is not possible with Java persistence to specify custom validation rules for entities (the whole entity) or properties.

What is Hibernate validation and what is it good for?

Hibernate provides ways to do custom validation for entities and properties. Work is under way to standardise Hibernate Validation in JSR-303 which is discussed openly and actively in this forum. We will discuss this JSR further in this article.

We should start with a simple example of how to define and edit an entity with JPA, Hibernate and our framework of choice: Seam.

Our validated entity

Here we start simple by having only one interesting property: emailAddress which should not be null, and should be a valid email address. Hibernate Validation declares validation constraints using Java annotations, which can be put on entity bean property fields or getter methods, much in the same way that you define your database mappings in JPA.

Hibernate Validation comes with many predefined validation constraint annotations, and provides a way to define your own if you need anything else. To solve our problem we are going to use the @NotNull and @EMail annotation constraints on our emailAddress property which will declare that this property must not be null and must be a valid email address:

Person.java

@Entity
public class Person {

 @GeneratedValue
 @Id
 private long id;

 @NotNull
 @EMailValidator
 private String emailAddress;

 /* Follows getter/setter methods */
}

If we try to persist a Person with a null or invalid email address, the JPA layer will throw an InvalidStateException (because Hibernate Validation enforces validation constraints automatically when saving an entity). This exception will possibly be wrapped by a transaction exception. But these validation constraints are more useful for other reasons:

  • They translate to DB-level constraints (well, @NotNull does)

  • They validate DB-level generated entities as well (if you import them by hand in SQL say)

  • They are automatically hooked in JSF views by Seam, for JSF validation

Our view

Here in the view where we edit a Person we instruct Seam to validate each field (the <s:validateAll> tag), and place the validation messages after each invalid field (the afterInvalidField facet). This will use the validation constraints we defined in the Entity to validate the values during the Process Validations JSF phase.

edit.xhtml

...
<h:form>
 <s:validateAll>
  <f:facet name="afterInvalidField">
   <s:message/>
  </f:facet>

  <p>
   <label for="emailAddress">E-mail address</label>
   <br/>
   <s:decorate>
    <h:inputText id="emailAddress" value="#{editedPerson.mailAddress}"/>
   </s:decorate>
  </p>
  <p>
   <h:commandButton action="#{personAction.save}" value="Save"/>
   <h:outputText value=" or "/>
   <h:commandLink action="#{personAction.cancel}">Cancel</h:commandLink>
  </p>
 </s:validateAll>

</h:form>
...

As you can see it is quite straightforward.

##Our action bean

This is the code which will be invoked on the server when the view described above will be invoked. It uses the personID request parameter (injected by Seam using the @RequestParameter annotation) to load a Person object from the database and outject it in the editedPerson variable using a factory method (initEditedPerson) which will be invoked when editedPerson is null.

Once the editedPerson has been sent to the view for editing, there are two ways out of the view: canceling (calling the cancel() method) or saving (calling save()). Canceling simply does nothing, and saving is as straightforward as instructing the persistence layer (PersonDAO) to save our entity in the database, since it has already been automatically validated.

PersonActionBean.java

@Stateful
@Name("personAction")

public PersonActionBean implements PersonAction {

  @RequestParameter
  private Long personID;

  @In(required = false)
  @Out
  private Person editedPerson;

  @EJB
  private PersonDAO personDAO;

  @Begin
  @Factory("editedPerson")
  private void initEditedPerson(){
    editedPerson = personDAO.findPersonById(personID);
  }

  @End
  public void cancel(){}

  @End
  public void save(){
    personDAO.save(editedPerson);
  }
}

More elaborate validation using custom validators

Now that the basics about Hibernate Validation have been explained, we still have to explain two important features: custom validation constraints, and custom messages.

We have noticed that users using our application were able to save local email addresses (email addresses which do not contain an @ or have a host name with no domain after it). These local email addresses are widely used in local or private networks, and are perfectly valid email addresses, but they cannot be used outside of those networks, so they cannot be reached globally, which means we cannot contact those people.

The @EMail constraint validation will accept both local and global email addresses, because they are both valid, which is why these users have been able to submit those local email addresses. So we have to define our own validation constraint which will refuse local email addresses.

This is very easy to do in Hibernate Validation: we have to define a new annotation (@NonLocalEmail) which will be used on our property, and point to a class responsible for the validation (NonLocalEmailValidator):

NonLocalEmail.java

@Documented
@ValidatorClass(NonLocalEmailValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NonLocalEmail {
  String message() default "{validator.email}";
}

There are several points of interest in the previous annotation:

  • @ValidatorClass is the annotation which points to the validation class responsible for the validation logic (this annotation is just a marker: annotations do not contain code).

  • @Target specifies that this validation constraint can be applied on fields and methods.

  • the message property is a standard Hibernate Validation validator annotation property which will be used by the JSF views in order to provide a meaningful error message when the validation fails. It can be overridden by the annotation user, and holds a default value for the message. The use of curly braces in a message means that the message should be loaded from a localised resource bundle rather than embedding localised messages in the code.

As for the actual class containing the validation logic, we will simply extend the EmailValidator class to add a check on the domain-part of the email address:

NonLocalEmailValidator.java

public class NonLocalEmailValidator extends EmailValidator
  implements Validator<NonLocalEmail> {

  public void initialize(NonLocalEmail annotation){}

  public void isValid(Object email){
    // null values are validated by other validators
    if(email == null)
      return true;
    boolean validEmail = super.isValid(email);
    // if the address is not even a valid email,
    // it cannot possibly be a valid non-local email
    if(!validEmail)
      return false;
    // now check that it has a domain part
    String emailValue = (String)email;
    // does it have an '@' sign?
    int atIndex = emailValue.indexOf('@');
    if(atIndex == -1)
      return false;
    // does it have a fully qualified domain name after it?
    return emailValue.indexOf('.', atIndex+1) != -1;
  }
}

As you can see, all we have to do is implement the Validator interface, and define two methods. The initialize method is used if our validator logic can be parameterised by the constraint annotation, which is not the case here. The isValid method takes a value and checks whether this value is a valid non-local email address. All very straightforward and incredibly nice.

What are the limitations?

Now that we hope to have convinced you that Hibernate Validation is the way to go because it is so nice and allows you to not duplicate your validation code, we have to admit it has a number of limitations that we’ve hit (not theoretical limitations, but limitations that forced us into duplicating our code and bypassing the automatic validation integration we’ve described earlier with Seam).

Integration with JPA constraints

JPA actually comes with several constraints declarations such as:

  • @Column(nullable = false) which is the JPA equivalent to Hibernate Validation’s @NotNull.

  • @Column(unique = true) which checks for column unicity in the database and has no Hibernate Validation equivalent.

  • @Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"firstColumn", "secondColumn"})}) which checks for multi-column unicity in the database and has no Hibernate Validation equivalent.

The duplication of the "NOT NULL" database constraint between JPA and Hibernate Validation is not merely unfortunate:

  • @Column(nullable = false) does not allow us to specify a custom error message.

  • @Column(nullable = false) generates a different exception than Hibernate Validation when attempting to persist an invalid entity.

  • @Column(nullable = false) is not used by Hibernate Validation or Seam when checking for invalid values in the view.

  • @NotNull only generates database-level constraints when using Hibernate for persistence (which is a moot point currently since Hibernate Validation is mostly used with Hibernate persistence, but will become relevant once standardised as JSR-303)

Furthermore, for the same reasons, unicity constraints defined in JPA cannot be localised, generate a different exception, and are not used in Hibernate Validation and Seam. What is worse though is that they cannot be replaced by an equivalent custom Hibernate Validation constraint for several practical reasons (API problems we can overcome), and one more fundamental and implacable reason: unicity can only be checked reliably while committing the transaction. Indeed, because it depends on other values in the database, nothing prevents other concurrent transactions from modifying other values after you’ve checked manually for unicity and before you commit your transaction (aside from locking).

Forgetting the fundamental issue, we’ve attempted to implement our own unicity constraint in Hibernate Validation, for sport mainly, and in order to check if that framework was really capable of providing an alternative to JPA’s unicity constraints.

Our attempt at checking for unicity in Hibernate Validation

Let us start simply by adding a single-column unicity constraint to Hibernate Validation on our entity:

Person.java

@Entity
public class Person {

 ...

 @NotNull         // instead of @Column(nullable = false)
 @Unique          // instead of @Column(unique = true)
 @EMailValidator  // no JPA equivalent
 private String emailAddress;

 ...
}

Here is how the @Unique annotation would be defined:

Unique.java

@Documented
@ValidatorClass(UnicityValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Unique {
  String message() default "{validator.unique}";
}

And here is how we would define the validation class:

UnicityValidator.java

public class UnicityValidator implements Validator<Unique> {

  public void initialize(Unique annotation){}

  public void isValid(Object value){
    // null values are validated by other validators
    if(value == null)
      return true;

    // here we check for unicity by checking if any other entity
    // of the same class holds the same value
    // ... wait a sec ... how do we do that without having
    // the entity and the property we are validating???
    Query query = getEntityManager().createQuery("SELECT count(x) FROM ?? x"
                  + " WHERE x.?? = :value AND x.id != :id LIMIT 1");
    query.setParameter("value", value);
    query.setParameter("id", ??);
    return ((Number)query.getSingleResult()).intValue() == 0;
  }
}

As you can see, we only get the value we are validating (a particular email address), and with only that we simply cannot check for unicity: we need to know the type of object we are validating, and the particular property we are checking for unicity.

There are five ways out of this limitation:

  • Change @Unique into @UniquePersonEmail and make the validation code specific to our particular entity type and property. This is rather inelegant as it does away with all the genericity we expect of such a validation constraint.

  • Add some parameters to @Unique to specify the entity class and property name so they can be used in the validator class. Using this annotation would require silly syntax such as @Unique(type = Person.class, property = "emailAddress"), and really this is inelegant too [Hibernatevalidation-footback1]# 1.

  • Give up, but we never give up.

  • Extend the Hibernate Validation API to provide us with the required information. It is fairly easy to extend Hibernate Validation so that the Validator.isValid method takes an extra two parameters (the bean instance and the property name), which would be enough for our single-column unicity check.

  • Use a bean-level validator. This is a validator which will validate the entire bean, and as such it will be passed the entire bean instance in its isValid method, so it can access the bean type and properties. In order to specify the names of the properties which have to be unique we can simply reuse the @UniqueConstraint annotation to specify the sets of unique columns. This has two downsides: it puts the validation constraint away from the property we are validating (not that far away, as it stays in the same file), but more importantly, Seam does not use bean-level validation to validate entities, so our view will not be validated.

Since we personally dislike the first 3 options, we will discuss the last two:

  • Extending Hibernate Validation to fix single-column unicity constraints is not enough, because it will not provide us with multi-column constraints. On the other hand it is necessary if we are to provide friendly single-column unicity constraints in line with JPA (even with the duplication).

  • Because Seam does not invoke bean-level validation, using bean-level validation is not the solution either.

So we are left with the option of doing both. Extending Hibernate Validation to provide more information about the bean and the annotated property for property-level constraints and extending Seam so that bean-level validation is executed during the Process Validations lifecycle. Any bean-level validation error messages would be displayed globally rather than next to the edited field since they cannot be associated to a particular property.

Our modifications in Hibernate Validation is very straight-forward and backwards-compatible: we define a subclass of Validator called ExtendedValidator which provides us with the appropriate information when validating:

public interface ExtendedValidator<A extends Annotation> extends Validator<A> {
  /**
   * Returns true if the given bean's property can be set to the given value
   */
  public boolean isValid(Object bean, String propertyName, Object value);
}

This method is then invoked in ClassValidator when the validator happens to implement ExtendedValidator. This makes sure that all previously-defined validators still work. We then have to overload the Validator.getPotentialInvalidValues method with an extra parameter for the bean instance, which we use in Seam.

In Seam we then still have to invoke bean validation, but this is at odds with the approach during the Process Validations lifecycle, which does not set the bean properties but uses Validator.getPotentialInvalidValues to check for validity without touching the bean instance. Because there is no equivalent potent equivalent to bean-level validation, something deeper has to change. What then? We’re not sure, but we’re still working on the solution.

Additionally, it would be really nice if bean-level validation could specify different error messages for different errors, as well as specify more than one error (not just returning false) and map errors to property names, so that a bean-level validator could do just as much as property-level validators.

Conditional and event-based validation

When we integrated web services in our application, we decided to go with bean validation all the way, and get rid of all the validation (validation: not permissions) in the web services frontend. We simply attempt to use whatever the client gives us and the validating persistence layer will complain if needed, at which point we can easily access the error message and format it for the web service client. We just had to move the validation from the action beans into the persistence tier.

While this seems like a good idea, our validation is actually more complex than what we abstracted into the entity beans. In other words, we got hit by limitations in Hibernate Validation. This includes what has been described earlier: the lack of bean-level validation in Seam, its limitations in error reporting and property association, and the lack of support for unicity constraints in Hibernate Validation.

We also discovered that we had more fundamental problems with validation: our beans actually have states, in the sense than some properties should be validated differently based on other properties, thus creating a dependency graph. To give you an example, suppose we add SMS integration in our system, and users can now be contacted either by email, SMS, or both, but having neither email nor SMS is invalid. How can we validate this?

Person.java

@Entity
public class Person {

 ...

 @NotNull // iff phoneNumber is null
 @Unique
 @EMailValidator
 private String emailAddress;

 @NotNull // iff emailAddress is null
 @Unique
 @PhoneNumberValidator
 private String phoneNumber;

 ...
}

Clearly with property-level validation there is no way to specify that one of those can be null if and only if the other is not null. We are left with bean-level validation, which has several drawbacks:

  • Not used by Seam (our view layer)

  • Does not give meaningful error messages

  • Does not map to properties

  • Moves the validation away from the bean where it will end up out-of-sync with the bean after refactoring

  • Introduces code duplication: how many beans do we have where one property out of several is required?

Not happy with bean-level validation, we set out to declare that we need conditional validation: validation which is enabled or disabled based on some conditions. Just at the same time, we noticed that there is a JSR under way to standardise Bean Validation: JSR-303, so we set out to look at it and see if it solves our problems.

Bean Validation (JSR-303)

JSR-303 is a new JSR which is aimed at providing a standard way to validate a Java Bean. It is edited by Emmanuel Bernard (author of Hibernate Validation), and consists mainly in abstracting Hibernate Validation from its Hibernate dependency so that it can be used to validate any Java Bean, persistent or not. In a spirit of transparency and openness, he has opened a forum where everyone can give feedback on the JSR as it evolves. We should point out that we think Emmanuel Bernard is doing great work, not only because we’re using software he wrote, but also because he’s doing us all a favour by standardising Bean Validation, which is something that will prove very useful once people understand its full potential.

The main differences we see with Hibernate Validation are validation groups and partial validation, which provides a way to declare several "layers" or "sets and subsets" of validation, that can be enabled or disabled when validating programmatically. The JSR also defines an entire reflection framework to access the validation constraints at run-time.

While it may seem that validation groups are what we need, they are different in that they do not specify the conditions for which those groups should be enabled or not.

Conditional validation proposal

We proposed a framework for conditional validation which provides several types of conditions:

Validation conditions are referred to by name in validation constraints that want to depend on those conditions. For example, the @NotNull(validationConditions={"admin"}) validation constraint is only enabled iff the admin validation condition is true.

Validation condition on boolean value

Validation conditions are then defined in various flavours, depending on the condition they are checking. The most simple example is the @ValidationConditionOnTrue which defines a validation condition on a bean property which evaluates to true:

@ValidationConditionOnTrue

public class User {

  // simple check for admin user
  @ValidationConditionOnTrue(name = "admin")
  private boolean admin;

  // sometimes it can be more complicated
  private List<Permission> permissions = new ArrayList<Permission>();

  @ValidationConditionOnTrue(name = "admin")
  public boolean hasAdminPermission(){
    for(Permission p : permissions)
      if(p.isAdmin())
        return true;
    return false;
  }

  // now that we have defined two ways that the validation condition "admin"
  // could be true, we can use it

  // we require administrators to have a valid email address. other users may
  // have one too, but it is not required.
  @NotNull(validationConditions = {"admin"})
  @Email
  private String emailAddress;
}

Validation condition on expression

Now that we are familiar with the difference between referencing and defining a validation condition, let us look at more complex validation conditions, such as the @ValidationConditionOnUEL which defines a validation condition based on a Unified Expression Language (UEL) expression. This is very useful for checks on properties located anywhere within the bean, multiple properties, or even sub-properties. With this we can accomplish our minimum requirement of having at least one of the postAddress or emailAddress properties set:

@ValidationConditionOnUEL

public class User {

  // we want emailAddress to be set if postAddress isn't set
  @NotNull(validationConditions = {"noPostAddress"})
  @Email
  // define a validation condition true if emailAddress is not set
  @ValidationConditionOnUEL(name = "noEmailAddress", uel = "emailAddress == null")
  private String emailAddress;

  // we want postAddress to be set if emailAddress isn't set
  @NotNull(validationConditions = {"noEmailAddress"})
  @Valid
  // define a validation condition true if postAddress is not set
  @ValidationConditionOnUEL(name = "noPostAddress", uel = "postAddress == null")
  private Address postAddress;

}

Note that since the UEL expression has access to the whole bean, it does not matter really whether the validation condition is placed on a property or on the bean itself. It is simply a matter of style and we prefer to have them located near their source, but naturally, should an UEL expression reference several properties, we would place the validation condition definition on the bean for clarity.

Validation condition on other validators

We still have one more type of validation condition to see: a validation condition which depends on the success or failure of other validation. In the previous example, we’ve used null checks in UEL, but we also defined @NotNull validation constraints. We would like to be able to reuse those constraints so say "validation this property iff this property failed to validate, or succeeded in validating". Because Java Annotations are so limited, we must resort to referencing those validation constraints by name, which we then must assign. Let us redefine the previous example with this new validation condition:

@ValidationConditionOnValue

public class User {

  // we want emailAddress to be set if postAddress isn't set
  @NotNull(name = "nullEmailAddress", validationConditions = {"noPostAddress"})
  @Email
  // define a validation condition true if nullPostAddress fails to validate
  @ValidationConditionOnValue(name = "noEmailAddress",
                              failedValidators = {"nullPostAddress"})
  private String emailAddress;

  // we want postAddress to be set if emailAddress isn't set
  @NotNull(name = "nullPostAddress", validationConditions = {"noEmailAddress"})
  @Valid
  // define a validation condition true if nullEmailAddress fails to validate
  @ValidationConditionOnValue(name = "noPostAddress",
                              failedValidators = {"nullEmailAddress"})
  private Address postAddress;

}

Using this last type of validation condition we can even define some dependency: validation which does not make sense if a required validator already failed. In the complex validation required by postal addresses, we want to validate the country code and the post code. But the post code depends on the street and country code, so in order for its validation to be meaningful, we must make sure that the street and country codes have already been validated:

@ValidationConditionOnValue dependency

public class Address {

  @NotEmpty(name = "setStreet")
  private String street;

  @NotNull(name = "setCountryCode")
  @ValidCountryCode(name = "validCountryCode")
  private String countryCode;

  @NotNull
  @ValidPostCode(validationConditions = {"validStreetAndCountryCode"})
  @ValidationConditionOnValue(name = "validStreetAndCountryCode",
                              validators = {"setStreet",
                                            "setCountryCode",
                                            "validCountryCode"})
  private String postCode;

}

Note that this previous example only makes sense if Validator is fixed so as to include the whole bean instance even for property validation, otherwise the @ValidPostCode cannot access the street and countryCode properties.

Also note that while we would like in some cases to actually include the validation condition’s definition in the validation constraints themselves (for instance @NotNull(uel = "otherProperty == null")), it is probably better practice to differentiate condition validation references and definitions so that one definition can be reused in multiple references. Due to the lack of inheritance in annotations, all validation annotations will be required to support two attributes more than the current message attribute: String[] validationConditions : default {} and String name : default "". This is unfortunate, but we don’t see any better alternative.

Validation Events

Sometimes it is not enough to support these conditions, and we would like to condition the validation based on some external event outside the scope of the bean itself. For instance, in JPA — which is where Hibernate Validation is applied right now —, we would often like to validate a bean when it is inserted, updated, or before it is deleted. This would then be based on JPA events such as ON_INSERT, ON_UPDATE, ON_DELETE corresponding to the underlying operation done on the entity bean.

Sometimes we also have expensive validation that we would like to only trigger when its costs has to be paid: when the value being checked has changed. For instance when we want to validate an entire collection of entities, we would like to only validate it when that collection has changed. Or to get back to our previous example of postal address validation, we would want to validate the whole address only if any of its property has changed. This can be seen as an additional event in JPA which we can name ON_CHANGE. The validation framework would then be responsible for checking whether the persisted value differs from the validated value, and trigger the validation only if they differ.

Validation Events

@ValidationConditionOnEvent(name = "changed", events = {ValidationEvent.ON_CHANGE})
public class Address {

  @NotEmpty(name = "setStreet", validationConditions = {"changed"})
  private String street;

  @NotNull(name = "setCountryCode", validationConditions = {"changed"})
  @ValidCountryCode(name = "validCountryCode", validationConditions = {"changed"})
  private String countryCode;

  @NotNull(validationConditions = {"changed"})
  @ValidPostCode(validationConditions = {"validStreetAndCountryCode", "changed"})
  @ValidationConditionOnValue(name = "validStreetAndCountryCode",
                              validators = {"setStreet",
                                            "setCountryCode",
                                            "validCountryCode"})
  private String postCode;

}

We can see from this previous example that we might need some sort of default validation group which would apply to the whole bean or each validation constraint.

Here is an other illustration:

Validation Events 2

// define a condition true when we are going to delete this entity
@ValidationConditionOnEvent(name = "deleted", events = {ValidationEvent.ON_DELETE})
public class User {

  // we want to pay the expensive validation of this collection only when it changes
  @CheckJobs(validationConditions = {"changedJobs"})
  @ValidationConditionOnEvent(name = "changedJobs", events = {ValidationEvent.ON_CHANGE})
  private List<Job> runningJobs = new ArrayList<Job>();

  // we can only delete this entity if it has no more running jobs
  @AssertTrue(validationConditions = {"deleted"})
  public boolean hasNoRunningJobs(){
    return runningJobs.size() == 0;
  }
}

Semantics

Basically because a validation check (running a validator’s isValid) method should be side-effect-free, we can take the simplistic view that during validation we can run every validator, and once we have determined which ones failed with what error messages, and which ones passed, we then proceed to check whether the failed ones were meaningful. They are meaningful if their validation conditions evaluate to true. They could all be resolved at this phase (including the tricky @ValidationConditionOnValue).

In order to be more efficient though, we should attempt to only run validators whose validation conditions evaluate to true. This is very straightforward in the cases of @ValidationConditionOnTrue, @ValidationConditionOnUEL and @ValidationConditionOnEvent. For @ValidationConditionOnValue, which depends on other validators' success or failure, we can attempt to run the referenced validators lazily, unless we get into a dependency loop (which is valid: look at our previous example of requiring at least one of email or postal addresses), in which case we can start resolving the loop by running validators as described previously, since they are supposed to be without side-effect, and validation failures are only meaningful if they pass the validation condition later on.

Implementing @ValidationConditionOnEvent would required a pluggable validation condition from JPA, which could instruct us of the current operation on the bean (insert, update, delete) that triggered the validation. The ON_CHANGE condition event would simply fetch the value currently persisted and use Java equality to check for any change in the value.

JPA 2.0 (JSR-317)

JPA is set for another major revision through the JSR-317. We can only hope that some consistency will be included with Bean Validation (JSR-303) so that our gripes with unicity constraints as well as clear semantics for validation exceptions will be resolved.

[Hibernatevalidation-foot1]#
1: I really wonder why the Annotation reification (reflection) of the annotation instance does not include pointers to the annotated object such as its type and value. Perhaps a question to ask the new annotation JSR project.

About the author

Stéphane Épardaud is a senior software developer at Lunatech Research. Although comments are disabled on this blog, he encourages you to send him comments by mail, corrections as well as opinions. Feedback is valued.
_