Bean Validation with JPA 1.0

For those of you who still don’t know Bean Validation, you should check the JSR 303 and the documentation of the reference implementation Hibernate Validator. In fact, like many other JSRs, Hibernate Validator existed on its own for quite a long time as an open source project (until version 3.1.x) and then got specified under the JSR 303 and became the reference implementation from its version 4.x. Bean Validation 1.0 was born and is now part of Java EE 6. But keep in mind that Bean Validation doesn’t need Java EE 6 to run (like other specs such as JPA 2.0, JSF 2.0, CDI 1.0…) and can be used outside any container.

So, what is it for ? Well, the short answer is to validate your POJOs. Imagine that you have a Book POJO and you want to check that the title of the book is not null and the author’s name is greater than 5 caracters and shorter than 20. Where do you validate these simple business rules ? In which later ? Different schools exist. In the Anemic Domain Model school, your model doesn’t have any intelligence and the validation should be done outside (typically in a service layer). In the Rich Object Model school, the object should validate itself and is the best place to encapsulate its own business logic. Bean Validation is part of the second school. So, how do you add validation constraints to your POJO with Bean Validation ? Through annotations. Bean Validation comes with existing annotations (in the javax.validation.constraints package) such as @Null, @NotNull, @Max, @Min, @Size, @Past, @Future… and gives you an API to create your own.

A Book POJO with Bean Validation annotations would look like this :

[sourcecode language=”java”]
public class Book {
@NotNull
private String title;

@NotNull
@Size(min = 5, max = 20)
private String author;

}
[/sourcecode]

Then, to validate it, you just need to use the Validator API, validate your POJO and get a set of ConstraintViolation back :

[sourcecode language=”java”]
@Test
public void shouldRaiseTwoConstraintViolations() {
// Creates an instance of book with null values
Book book = new Book(null, null);

// Validates the Book manually
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation> constraintViolations = validator.validate(book);
assertEquals("Should raise 2 constraints because title and author are null", 2, constraintViolations.size());
}
[/sourcecode]

Download the code of the examples

Integration with JPA 2.0

One nice thing with Bean Validation is that it’s integrated with JPA 2.0 (Hibernate 3.5.x) as well as JSF 2.0. This means that without any specific code your entity is validated during the @PrePersist, @PreUpdate and @PreRemove lifecycle phases (this is by default, you can always change the phases if you want). So let’s take the Book POJO and turn it into a JPA 2.0 entity (by adding the @Entity and @Id annotations) :

[sourcecode language=”java”]
@Entity
public class Book {

@Id @GeneratedValue
private Long id;

@NotNull
private String title;

@NotNull
@Size(min = 5, max = 20)
private String author;

}
[/sourcecode]

How do you validate it ? Well, just by calling the EntityManager.persist() for example. No need to explicitly use the Validator API. As you can see in the test case below, persisting a non valid Book entity will throw a ConstraintViolationException :

[sourcecode language=”java”]
@Test(expected = ConstraintViolationException.class)
public void shouldThrowAnExceptionBecauseNotAValidBookWhenPersisting() {
// Creates an instance of book with null values
Book book = new Book(null, null);

// Persists the book to the database
tx.begin();
em.persist(book);
tx.commit();
}
[/sourcecode]

Making it work with JPA 1.0

Bean Validation is integrated with JPA 2.0 (Hibernate 3.5) but not with JPA 1.0 (Hibernate 3.4). So if you want to have the same features, you need to code it by hand. But this is easy with entity listeners. The custom code below is an entity listener that is triggered at @PrePersist, @PreUpdate and @PreRemove phase. It uses the Validator API and throws an exception if any validation constraints is violated.

[sourcecode language=”java”]
public class MyBeanValidationEventListener {

@PrePersist
@PreUpdate
@PreRemove
public void validate(Object entity) {
TraversableResolver tr = new MyTraversableResolver();
Validator validator = Validation.buildDefaultValidatorFactory().usingContext().traversableResolver(tr).getValidator();
final Set
if (constraintViolations.size() > 0) {
Set&gt; propagatedViolations = new HashSet&gt;(constraintViolations.size());
Set classNames = new HashSet();
for (ConstraintViolation violation : constraintViolations) {
propagatedViolations.add(violation);
classNames.add(violation.getLeafBean().getClass().getName());
}
StringBuilder builder = new StringBuilder();
builder.append("validation failed for classes ");
builder.append(classNames);
throw new ConstraintViolationException(builder.toString(), propagatedViolations);
}
}
}
[/sourcecode]

I also found a bug (that I can’t reproduce now) in the JPATraversableResolver (which is using the JPA 2.0 Persistence.getPersistenceUtil() method which is not part of JPA 1.0) so I wrote my own resolver (used above in the listener) :

[sourcecode language=”java”]
public class MyTraversableResolver implements TraversableResolver {

public boolean isReachable(Object traversableObject, Path.Node traversableProperty, Class rootBeanType, Path pathToTraversableObject, ElementType elementType) {
return traversableObject == null || Hibernate.isInitialized(traversableObject);
}

public boolean isCascadable(Object traversableObject, Path.Node traversableProperty, Class rootBeanType, Path pathToTraversableObject, ElementType elementType) {
return true;
}
}
[/sourcecode]

To use this listener on the Book entity, just use the @EntityListener annotation and you’ll get the job done. Automatically your entity will be validated when calling EntityManager.persist().

[sourcecode language=”java”]
@Entity
@EntityListeners(MyBeanValidationEventListener.class)
public class Book {

}
[/sourcecode]

Because you don’t want to repeat this annotation on every single entity of your application, instead you can use the XML metatdata to set this listener on all your entities :

[sourcecode language=”xml”]
<entity-mappings…>
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener/>
<entity-listener class="org.beginningee6.sample.MyBeanValidationEventListener"/>
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
[/sourcecode]

Open question to Emmanuel Bernard

Emmanuel Bernard, who works for JBoss, is the spec lead of Bean Validation. Few months ago, before the spec was final, I was talking with him about the @PreRemove phase (see chapter 3.6 of the JPA 2.0 specification). For me it doesn’t make much sense to validate an entity before removing it. It should have been validated before getting inserted or updated to the database, but once it’s there, it should be easy to just remove it, without validation. At the time the topic was still in discussion and I can see now that the spec integrates validation before remove. Emmanuel, do you have a quick explanation about this ? Thanks.

Thanks to Emmanuel who answered on his blog
Download the code of the examples

9 thoughts on “Bean Validation with JPA 1.0

  1. Hi,

    Thanks for the interesting article!

    It is a little bit off-topic, but the discussion about Anemic versus Rich Domain Model is quite fascinating 🙂

    When using Bean Validation, you are choosing to go for a Rich Domain Model (constrains defined in your model objects).

    Now let’s become the business and validation rules more complicated: a lot of dependencies to other model objects like reference data and user-defined business rules (loaded from somewhere, Db or cache), mixed with some complex rules.

    What is the “Golden Path” to choose in such a situation?

    You could start defining your own BV custom constraints. However, doing so would imho “externalise” the business logic (into validator objects and constraint annotations, see Hibernate Validator documentation chp. 3), shifting into the direction of an Anemic model (which is considered as anti-pattern by Martin Fowler, for example). Additionally, these classes would be quite complex and numerous.

    You could simply write methods implementing these complex rules. Placing these methods outside goes again toward something “anemic”. Placing them into the model creates huge dependencies with other model objects. This might be acceptable in times of CDI & co, but does this not stretch the CDI concept too far?

    What do you think about this? (Please don’t say “Learn Drools” 🙂 ).

    Best,
    Tasha

    1. I don’t know Drools ;o)

      I think things should be balanced. When you start playing with BV groups and start validating bean A depending on bean B… that can be messy. I love the entire DDD approach, I just don’t know how to apply it correctly in Java (with other languages that might be easier). So what I do (but I would not say it’s a Golden Path) is I like to encapsulate business logic of bean A inside bean A, and business logic of bean B inside bean B. Then, when you need to validate bean A according to bean B, I do that in the service layer. Service layers are not very DDD, but I’m not a very DDD guy either ;o)

  2. Hi, thanks for this guide.

    I was making some experiments, and I found out that using hibernate-jpa 3.3 and hibernate-validator 4.1, I only need hibernate-validator-4.1.jar and validator-api-1.0.jar in my classpath. The jar about JPA 2 is not need it.

    By the way, I check this with your examples.

    And, without the JPA2 jar, the code uses the Listener to validate the Bean Models., before going to the database. It also works with a NON Entity POJO.

    Can u give a hint, Im new in Hibernate Validator, and I have the constraint that I can only use JPA1.

    Thanks again

  3. I am happy to found you! I was searching the code when I found this site. Thank you so much for sharing it.

  4. Hi, Nice Example on Hibernate validation, I am trying out same in application which uses Spring- JPA2-Hibernate. But i have read only access to database. I would like to do validation while fetching/loading the data and would like throw exception. If validation fails. Is it possible to do this?

Leave a Reply