Help

Now that Bean Validation is officially part of Java EE 6 and that Java EE6 is officially voted YES, let's see how Bean Validation integrates with the rest of the eco system.

What is Bean Validation

It's goal is to let application developers declare their data constraints once by annotating their model and make sure these constraints are validated by the different layers of the application in a consistent manner. Without Bean Validation, people have to write their validation rules in their favorite presentation framework, then in their business layer, then in their persistent layer, to some degree in the database schema and keep all of them synchronized.

Here is how this centralized constraint declaration looks like:

class User {
  @NotEmpty @Size(max=100)
  String getLogin() { ... }

  @NotEmpty @Size(max=100)
  String getFirstname() { ... }
  
  @Email @Size(max=250)
  String getEmail() { ... }
  ...
}

There are many more features like constraint composition, grouping but let's focus on how Bean Validation integrates with the EE 6 stack.

So what do I have to do to make it work in Java EE 6

The short answer is nothing. Not even an XML configuration trick.

Simply add your constraints on your domain model and the platform does the rest for you.

JSF and how to expose constraint violations to the user

In JSF, you bind form inputs to properties of your domain model. JSF 2 and Bean Validation smartly figure out which property you are binding to and execute the constraints associated to it.

<h:form id="register">
    <div style="color: red">
        <h:messages id="messages" globalOnly="true"/>
    </div>

    <div>
        Login:
        <h:inputText id="login" value="#{identifier.user.login}"/>
        <h:message style="color: red" for="login"/>
        <br/>
        Password:
        <h:inputSecret id="password" value="#{identifier.user.password}"/>
        <h:message style="color: red" for="password"/>
        <br/>
        Firstname:
        <h:inputText id="firstname" value="#{identifier.user.firstname}"/>
        <h:message style="color: red" for="firstname"/>
        <br/>
        Email:
        <h:inputText id="email" value="#{identifier.user.email}"/>
        <h:message style="color: red" for="email"/>
        <br/>

        <h:commandButton id="Login" value="Login" action="#{identifier.register}"/>
        <br/>
        <h:button id="cancel" value="Cancel" outcome="/home.xhtml"/>
    </div>
</h:form>

If the email, for example is malformed and the first name is left empty, Bean Validation will return the constraint violations to JSF 2 that will expose them to the user in a localized error message. By default, it just works and you don't even have to think about it.

For more advanced use cases, like disabling constraint validation for one or several fields or using a specific group or set of groups instead of the default one, you can use the <f:validateBean/> tag (check line 8 and 20 in the following example).

<h:form id="register">
    <div style="color: red">
        <h:messages id="messages" globalOnly="true"/>
    </div>

    <div>
        <!-- ***** use a specific group ***** -->
        <f:validateBean validationGroups="${identifier.validationGroups}">
            Login:
            <h:inputText id="login" value="#{identifier.user.login}"/>
            <h:message style="color: red" for="login"/>
            <br/>
            Password:
            <h:inputSecret id="password" value="#{identifier.user.password}"/>
            <h:message style="color: red" for="password"/>
            <br/>
            Firstname:
            <!-- ***** disable validation for firstname ***** -->
            <h:inputText id="firstname" value="#{identifier.user.firstname}">
                <f:validateBean disabled="true"/>
            </h:inputText>
            <h:message style="color: red" for="firstname"/>
            <br/>
            Email:
            <h:inputText id="email" value="#{identifier.user.email}"/>
            <h:message style="color: red" for="email"/>
            <br/>

            <h:commandButton id="Login" value="Login" action="#{identifier.register}"/>
            <br/>
            <h:button id="cancel" value="Cancel" outcome="/home.xhtml"/>
        </f:validateBean>
    </div>
</h:form>

In the future, we want to work with RichFaces so that the constraints declared on the object model are validated in the JSF components on the client side. This is something we had prototyped already and that Pete, Dan and I proposed to the JSF 2 expert group initially but we had to scale down our ambitions :) Expect some innovations from us in this area.

But not all your data comes from the presentation layer.

JPA 2: last line of defense

Again, by default, your JPA 2 provider runs Bean Validation on the entities you are about to persist or update. You are then guaranteed to not put invalid data in your database and thus increasing the quality of your data overall. Oh, and these are the same constraints you would have validated in JSF 2.0.

You can disable validation in JPA 2 using the validation-mode element in persistence.xml or the javax.persistence.validation.mode property and set them to none. More interestingly, you can chose which group will be validated upon entity persist, update and even delete operations. By default, the Default group is validated when you persist or update entities. Use any one of these properties to adjust that.

<property name="javax.persistence.validation.group.pre-persist" 
        value"javax.validation.groups.Default, com.acme.model.Structural"/>
<property name="javax.persistence.validation.group.pre-update" 
        value"javax.validation.groups.Default, com.acme.model.Structural"/>

<property name="javax.persistence.validation.group.pre-delete" 
        value"com.acme.model.SafeDestruct"/>

Hibernate Core and Hibernate Validator go a bit beyond that and propagate the constraints to the database schema (provided that you let Hibernate Core generate or update the schema for you). Simply set the hibernate.hbm2ddl.auto property to create, update or create-drop.

How about my service layer

You can inject a Validator or ValidatorFactory instance in any injectable POJO in Java EE 6.

class SalesService {
  @Inject Validator validator;
  @Inject @Current User user;

  public boolean canBuyInOneClick() {
    return validator.validate(user, BuyInOneClick.class).size() == 0;
  }

Where can I try it?

All of this is now available in JBoss AS 6 M1 that have just been released. Enjoy!

14 comments:
 
03. Dec 2009, 17:02 CET | Link

Bean Validation is pretty awesome. I was living in a cave because I just recently found out about it. Great stuff...now I need to get in our codebase.

ReplyQuote
 
03. Dec 2009, 19:09 CET | Link
Ryan

Very cool. Do you know if there is a way to add validators to classes at runtime? I'm not sure how feasible that would be, but I have a use case where object models, database tables and CRUD screens are designed by users at runtime, and they will need validation. At this point I'm not sure if I can use JPA for that, although I have seen some examples of creating JPA entities at runtime (TopLink specific)

 
03. Dec 2009, 19:27 CET | Link
Hibernate Core and Hibernate Validator go a bit beyond that and propagate the constraints to the database schema

I don't think you're quite doing justice to this feature. It's really nice to know that you can define things in one place, and have it consistent across the UI, persistence layer, and database schema. Even if you don't use Hibernate to generate your production schema (the common case), it's still very nice to be able to have it generate your test schemas for you.

 
03. Dec 2009, 20:15 CET | Link
Drew Arrigoni
In the future, we want to work with RichFaces so that the constraints declared on the object model are validated in the JSF components on the client side. This is something we had prototyped already and that Pete, Dan and I proposed to the JSF 2 expert group initially but we had to scale down our ambitions :) Expect some innovations from us in this area.

Holy feces on a tortilla, yes! Less AJAX calls == Less Server Load == Less Servers == More Profit! Not to mention Less AJAX calls == Faster Response to the User == Happier Customers == Happier Bosses!

Y'all are killing me here! As soon as RichFaces 4 comes out, I'm whole-heartedly moving to the new stack.

 
04. Dec 2009, 08:58 CET | Link
Geoffrey De Smet | ge0ffrey.spam(AT)gmail.com
Great stuff!

But why do we have to do something complicated like
  Set constraintViolations = validator.validate(user, BuyInOneClick.class)
  if (constraintViolations.size() > 0) {
    StringBuilder s = new StringBuilder();
    for (Object o : constraintViolations) {
        s.append(o.getMessage()).append("\n");
    }
    throw new ValidationRuntimeException(s.toString());
  }

Why isn't there a helper method like this which throws ValidationRuntimeException?
  validator.assertValid(user, BuyInOneClick.class);
 
04. Dec 2009, 10:39 CET | Link
Do you know if there is a way to add validators to classes at runtime? I'm not sure how feasible that would be, but I have a use case where object models, database tables and CRUD screens are designed by users at runtime, and they will need validation. At this point I'm not sure if I can use JPA for that, although I have seen some examples of creating JPA entities at runtime (TopLink specific)

Kind of. You can generate the mapping XML InputStream and pass it to the Bean Validation at init time

ValidatorFactory vf = Validation.byDefaultProvider()
   .configure()
     .addMapping(is)
       .buildValidatorFactory();

Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put("javax.persistence.validation.factory", vf);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("users", properties);

But that's not entirely satisfactory to create an XML stream for that, so we are going to work on a programmatic mapping API like the new Hibernate Search one.

 
04. Dec 2009, 10:47 CET | Link
But why do we have to do something complicated like [...] Why isn't there a helper method like this which throws ValidationRuntimeException? validator.assertValid(user, BuyInOneClick.class);

Because you don't :)

The BuyInOneClick use case is not about error report, it's about checking that the object graph is in a decent state: no exception needed.

If you need to report errors to your user, I'm sure you don't want a horrible concatenated list of messages, so the generic helper method is not good enough. That's where JSF 2's integration (and I hope soon other presentation frameworks) kicks in. The presentation framework call validation for you and expose the error reports nicely to the user (for example next to the invalid field or fields).

For frameworks like JPA, they run BV and raise a javax.validation.ConstraintViolationException which takes a Set<ConstraintViolation and exposes that as a perperty of the exception.

 
04. Dec 2009, 15:58 CET | Link
Sakuraba | saku(AT)raba.jp
Even if you don't use Hibernate to generate your production schema (the common case), it's still very nice to be able to have it generate your test schemas for you.

That is great, but what about changes to those annotated entities? Can Hibernate take a set of annotated Entities (JPA and Validation Annotations) and a database connection and give me and or execute the diff-DDL to the Schema behind the connection?

 
04. Dec 2009, 17:28 CET | Link
Sakuraba wrote on Dec 04, 2009 15:58:
Even if you don't use Hibernate to generate your production schema (the common case), it's still very nice to be able to have it generate your test schemas for you. That is great, but what about changes to those annotated entities? Can Hibernate take a set of annotated Entities (JPA and Validation Annotations) and a database connection and give me and or execute the diff-DDL to the Schema behind the connection?

In full generality, the problem of schema migration is an extremely difficult one, and not amenable to automation. You not only need to update the table, column, and constraint definitions, but you also need to figure out what to do with the data that already exists in the tables. What happens when the existing data doesn't conform to the newly-defined constraints?

For this reason, schema migration is always a mostly-manual process.

However, yes, Hibernate does have some limited functionality for updating schema definitions. But it can't just magically guess what to do with pre-existing data.

 
04. Dec 2009, 21:41 CET | Link

Yes, this is a hard problem for sure.

But it's a pain point that everybody deals with, so it'd be great if Seam-Gen Encore could integrate nicely with Liquibase or some other migration project. I'm not at the moment sure how this ought to work, but it's something I've thought about off and on.

If we can manage to nail this, I'd be a happy programmer.

 
05. Dec 2009, 10:40 CET | Link
Sakuraba | saku(AT)raba.jp
For this reason, schema migration is always a mostly-manual process.

I agree, but for doing the manual steps it is of interest what the changes/differences between old-schema and new schema are, so it becomes easier to write migration scripts. I dont want Hibernate to be this super intelligent thing that automatically updates a database of patient records, but I think it would be supportive of agile database migration, if it was possible to always see the difference between the schema that results of my current entities and the schema that resides in the database.

In the past the SchemaUpdate tool with Ant did not print out a diff for me, but the full DDL statements for the entire DB.

 
07. Dec 2009, 11:45 CET | Link
Ian

Can you use bean validation in conjunction with the AJAX features of JSF2, so you can run the BeanValidation whenever you (for instance) tab out of the field? I know you can do this without BeanValidation, but it's more painful and this would be ideal.

When I tried to get this to work, I could see a Validator being instantiated so I knew the validation was being performed. But I didn't see any error messages and don't know where I can get them from. Maybe I'm already doing it right but can't get the error message.

 
09. Dec 2009, 13:23 CET | Link

Yes it is definitively possible as I have seen Dan do it. I don't remember how though ;) Dan could you comment?

 
10. Mar 2010, 23:48 CET | Link

Hi Emmanuel,

Greatly appreciate your work in the Bean Validation JSR. I have question regarding the message interpolation in bean validation. Is it possible to retrieve the name of the property or specify a label for the property in the ValidationMessages.properties ? ie, say if I have a property

@Size(min=5,max=8)
String userName;
@Size(min=8,max=15)
String password;

For both userName and password I would like to define a single parametrized validation message like the following:

javax.validation.constraints.Size.message={field} must be between {max} and {min}. You entered {value}.

I know the message interpolator would replace {max}, {min} and {value}, but I don't know how to pass the field.

Please let me know if you know if this can be achieved using the default message interpolator, or if there is any best practice related to this.

Post Comment