openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] OpenJPA > Bean Validation Primer
Date Wed, 02 Feb 2011 02:36:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2036/9/5/_/styles/combined.css?spaceKey=openjpa&amp;forWysiwyg=true" type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/openjpa/Bean+Validation+Primer">Bean Validation Primer</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~techhusky">Jeremy Bauer</a>
    </h4>
        <div id="versionComment">
        <b>Comment:</b>
        Remove extraneous TX commit in code example<br />
    </div>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >            img.setType(ImageType.GIF); <br>            em.remove(img); <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">            em.getTransaction().commit(); <br></td></tr>
            <tr><td class="diff-unchanged" >        }  catch (ConstraintViolationException cve) { <br>            // Rollback the active transaction and handle the exception <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h2><a name="BeanValidationPrimer-OpenJPABeanValidationPrimer"></a>OpenJPA Bean Validation Primer</h2>

<p>A new feature defined by the JPA 2.0 specification is the ability to seamlessly integrate with a <a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303</a> bean validation provider.   With minimal effort, <a href="http://openjpa.apache.org/openjpa-200.html" class="external-link" rel="nofollow">OpenJPA 2.0</a> can be coupled with a <a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303</a> validation provider to provide runtime data validation.  By combining these two technologies, you get a standardized persistence solution with the added ability to perform standardized data validation.</p>

<h3><a name="BeanValidationPrimer-WhatisBeanValidation%3F"></a>What is Bean Validation?</h3>

<p>Most applications, especially those that gather input from a user, contain a significant amount of code to perform data validation.  It is typically custom code, fragmented and littered throughout the application.  Worse, there may be duplicate validation code at the presentation (Web) , business (EJB), and persistence layers.  The task of keeping duplicate code in synch can be especially problematic.  A slight modification in the code at one layer can lead to an unforseen breakage in another.</p>

<p>The Bean Validation API was designed to provide a standardized method for validating data within Java beans.  As an added feature, it seemlessly integrates with several JEE6 technologies including <a href="http://jcp.org/en/jsr/detail?id=317" class="external-link" rel="nofollow">JPA 2.0</a>, <a href="http://jcp.org/en/jsr/summary?id=322" class="external-link" rel="nofollow">JCA 1.6</a>, and <a href="http://jcp.org/en/jsr/detail?id=314" class="external-link" rel="nofollow">JSF 2.0</a>.  Additionally, JEE6 complaint servers are required to support bean validation and must include a validation provider.  While a JEE6 environment provides some simplified packaging and configuration benefits, bean validation works equally well in a JSE environment.  For simplicity, this primer will use a JSE environment, but the example code could be used within a JEE6 environment with very few modifications.</p>

<p>While the <a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303 specification</a> is very feature rich and extensible, there are three core concepts that will be of most interest to the majority of users:  constraints, constraint violation handling, and the validator itself.  If you are running in an integrated environment like JPA, you will rarely have to concern yourself with the validator; simplifying things even further.</p>

<h3><a name="BeanValidationPrimer-ValidationConstraints"></a>Validation Constraints</h3>

<p>Constraints are a fundamental component of bean validation.  Constraints can be placed on Java beans and/or fields and properties (collectively labeled attributes in JPA terminology) to constrain the data within the bean.  A constraint can either be defined using annotations or XML.  The bean validation specification defines a small set of constraints that must be included with every validation provider.  This small set covers most types of simple validation in addition to a powerful regular expression based validator.  If the built-in constraints don't fit the bill, you can very easily build your own custom validators and matching constraints.  Let's start by looking at some simple constraints and then move to creating some custom constraints.</p>

<h3><a name="BeanValidationPrimer-ConstraininganEntity"></a>Constraining an Entity</h3>

<p>For the purposes of an example, let's start building the JPA domain model for a digital image storage system. For the sake of simplicity, we'll start with a simple entity "Image".  An <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Image.java" class="external-link" rel="nofollow">Image</a> has an ID, image type, file name, and image data.  Our system has a requirement that the image type must be specified and the image file name must include a valid JPEG or GIF extension.  The code below shows the annotated Image entity with some built-in bean validation constraints applied.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>Image.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.model;

<span class="code-keyword">import</span> javax.persistence.Entity;
<span class="code-keyword">import</span> javax.persistence.EnumType;
<span class="code-keyword">import</span> javax.persistence.Enumerated;
<span class="code-keyword">import</span> javax.persistence.GeneratedValue;
<span class="code-keyword">import</span> javax.persistence.Id;
<span class="code-keyword">import</span> javax.validation.constraints.NotNull;
<span class="code-keyword">import</span> javax.validation.constraints.Pattern;

@Entity
<span class="code-keyword">public</span> class Image {

    <span class="code-keyword">private</span> <span class="code-object">long</span> id;
    <span class="code-keyword">private</span> ImageType type;
    <span class="code-keyword">private</span> <span class="code-object">String</span> fileName;
    <span class="code-keyword">private</span> <span class="code-object">byte</span>[] data;

    @Id
    @GeneratedValue
    <span class="code-keyword">public</span> <span class="code-object">long</span> getId() {
        <span class="code-keyword">return</span> id;
    }

    <span class="code-keyword">public</span> void setId(<span class="code-object">long</span> id) {
        <span class="code-keyword">this</span>.id = id;
    }

    @NotNull(message=<span class="code-quote">"Image type must be specified."</span>)
    @Enumerated(EnumType.STRING)
    <span class="code-keyword">public</span> ImageType getType() {
        <span class="code-keyword">return</span> type;
    }

    <span class="code-keyword">public</span> void setType(ImageType type) {
        <span class="code-keyword">this</span>.type = type;
    }

    @Pattern(regexp = <span class="code-quote">".*\\.jpg|.*\\.jpeg|.*\\.gif"</span>,
        message=<span class="code-quote">"Only images of type JPEG or GIF are supported."</span>)
    <span class="code-keyword">public</span> <span class="code-object">String</span> getFileName() {
        <span class="code-keyword">return</span> fileName;
    }

    <span class="code-keyword">public</span> void setFileName(<span class="code-object">String</span> fileName) {
        <span class="code-keyword">this</span>.fileName = fileName;
    }

    <span class="code-keyword">public</span> <span class="code-object">byte</span>[] getData() {
        <span class="code-keyword">return</span> data;
    }

    <span class="code-keyword">public</span> void setData(<span class="code-object">byte</span>[] data) {
        <span class="code-keyword">this</span>.data = data;
    }
}
</pre>
</div></div>

<p><br class="atl-forced-newline" />
The <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Image.java" class="external-link" rel="nofollow">Image</a> class uses two built in constraints @NotNull and @Pattern. The @NotNull constraint ensures that an <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/ImageType.java" class="external-link" rel="nofollow">ImageType</a> is specified and @Pattern constraint uses regular expression pattern matching to ensure the image file name is suffixed with a supported image format.  Each constraint has corresponding validation logic that gets executed at runtime when the Image entity is validated.  If either constraint is not met, the JPA provider will throw a ConstraintViolationException with the defined message.  The <a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303 specification</a> also makes provisions for the use of a variable within the message attribute.  The variable references a keyed message in a resource bundle.  That allows for environment specific messages and localization of messages.  See the <a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303 specification</a>, section 4.1.3 for additional details regarding the customization and localization of messages.</p>

<h3><a name="BeanValidationPrimer-CustomConstraintsandValidators"></a>Custom Constraints and Validators</h3>

<p>If the built-in constraints do not meet your needs, you can create your own custom validators and constraints.  In our previous example, the Image entity used the @Pattern constraint to validate the file name of the image.  However, it did no constraint checking on the actual image data itself.  A pattern-based constraint could potentially be used, but this is rather inflexible and will get messy.  A custom constraint and validator provides a more robust and flexible solution.  First, let's create a custom method level constraint annotation named <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContent.java" class="external-link" rel="nofollow">ImageContent</a>.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>ImageContent.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.constraint;

<span class="code-keyword">import</span> java.lang.annotation.Documented;
<span class="code-keyword">import</span> java.lang.annotation.Retention;
<span class="code-keyword">import</span> java.lang.annotation.Target;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> java.lang.annotation.ElementType.METHOD;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> java.lang.annotation.ElementType.FIELD;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> java.lang.annotation.RetentionPolicy.RUNTIME;

<span class="code-keyword">import</span> javax.validation.Constraint;
<span class="code-keyword">import</span> javax.validation.Payload;

<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.model.ImageType;

@Documented
@Constraint(validatedBy = ImageContentValidator.class)
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
<span class="code-keyword">public</span> @<span class="code-keyword">interface</span> ImageContent {
    <span class="code-object">String</span> message() <span class="code-keyword">default</span> <span class="code-quote">"Image data is not a supported format."</span>;
    <span class="code-object">Class</span>&lt;?&gt;[] groups() <span class="code-keyword">default</span> {};
    <span class="code-object">Class</span>&lt;? <span class="code-keyword">extends</span> Payload&gt;[] payload() <span class="code-keyword">default</span> {};
    ImageType[] value() <span class="code-keyword">default</span> { ImageType.GIF, ImageType.JPEG };
}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Now, let's create the validator class, <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContentValidator.java" class="external-link" rel="nofollow">ImageContentValidator</a>.  The logic within this validator gets executed by validation provider when the constraint is validated.  Notice, the validator class is bound to the constraint annotation via the validatedBy attribute on the @Constraint annotation.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>ImageContentValidator.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.constraint;

<span class="code-keyword">import</span> java.util.Arrays;
<span class="code-keyword">import</span> java.util.List;

<span class="code-keyword">import</span> javax.validation.ConstraintValidator;
<span class="code-keyword">import</span> javax.validation.ConstraintValidatorContext;

<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.model.ImageType;

/**
 * Simple check that file format is of a supported type
 */
<span class="code-keyword">public</span> class ImageContentValidator <span class="code-keyword">implements</span> ConstraintValidator&lt;ImageContent, <span class="code-object">byte</span>[]&gt; {
    <span class="code-keyword">private</span> List&lt;ImageType&gt; allowedTypes = <span class="code-keyword">null</span>;
    /**
     * Configure the constraint validator based on the image
     * types it should support.
     * @param constraint the constraint definition
     */
    <span class="code-keyword">public</span> void initialize(ImageContent constraint) {
        allowedTypes = Arrays.asList(constraint.value());
    }

    /**
     * Validate a specified value.
     */
    <span class="code-keyword">public</span> <span class="code-object">boolean</span> isValid(<span class="code-object">byte</span>[] value, ConstraintValidatorContext context) {
        <span class="code-keyword">if</span> (value == <span class="code-keyword">null</span>) {
            <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
        }
        <span class="code-comment">// Verify the GIF header is either GIF87 or GIF89
</span>        <span class="code-keyword">if</span> (allowedTypes.contains(ImageType.GIF)) {
            <span class="code-object">String</span> gifHeader = <span class="code-keyword">new</span> <span class="code-object">String</span>(value, 0, 6);
            <span class="code-keyword">if</span> (value.length &gt;= 6 &amp;&amp;
                (gifHeader.equalsIgnoreCase(<span class="code-quote">"GIF87a"</span>) ||
                 gifHeader.equalsIgnoreCase(<span class="code-quote">"GIF89a"</span>))) {
                <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
            }
        }
        <span class="code-comment">// Verify the JPEG begins with SOI &amp; ends with EOI
</span>        <span class="code-keyword">if</span> (allowedTypes.contains(ImageType.JPEG)) {
            <span class="code-keyword">if</span> (value.length &gt;= 4 &amp;&amp;
                value[0] == 0xff &amp;&amp; value[1] == 0xd8 &amp;&amp;
                value[value.length - 2] == 0xff &amp;&amp;
                value[value.length -1] == 0xd9) {
                <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
            }
        }
        <span class="code-comment">// Unknown file format
</span>        <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
    }
}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Finally, let's apply the new constraint to the getData() method on our Image class.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @ImageContent
    <span class="code-keyword">public</span> <span class="code-object">byte</span>[] getData() {
        <span class="code-keyword">return</span> data;
    }
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>When validation of the "data" attribute occurs, the isValid() method in our <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContentValidator.java" class="external-link" rel="nofollow">ImageContentValidator</a> will fire.  This method contains logic for performing simple validation of  the format of the binary image data.  A potentially overlooked feature in the <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContentValidator.java" class="external-link" rel="nofollow">ImageContentValidator</a> is that it can also validate for a specific image type.  By definition, it accepts for JPEG or GIF formats, but it can also validate for a specific format.  For example, by changing the annotation to:</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @ImageContent(ImageType.JPEG)
    <span class="code-keyword">public</span> <span class="code-object">byte</span>[] getData() {
        <span class="code-keyword">return</span> data;
    }
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>instructs the validator to only permit image data with valid JPEG content.</p>

<h3><a name="BeanValidationPrimer-TypelevelConstraints"></a>Type-level Constraints</h3>

<p>The examples thus far have shown the use of validation constraints on individual attributes.  That is sufficient in many cases, but validation logic often needs to consider combinations of attributes when validating an entity.  For example, the constraints applied to the Image entity validate that an image type is set (not null), the extension on the image file name are of a supported type, and the data format is correct for the indicated type. But, for example, it will not collectively validate that a file named "img0.gif" is of type GIF and the format of the data is for a valid GIF image.  There are several options to provide collective validation.  One option is to create subclasses of Image, JPEGImage and GIFImage, with constraints geared for each of these types.  Another, less invasive and simpler option is to type-level constraint.  Let's modify our Image class to use a custom type-level constraint.  Here is the updated Image entity with the new type-level constraint.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>Image.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.model;

<span class="code-keyword">import</span> javax.persistence.Embedded;
<span class="code-keyword">import</span> javax.persistence.Entity;
<span class="code-keyword">import</span> javax.persistence.EnumType;
<span class="code-keyword">import</span> javax.persistence.Enumerated;
<span class="code-keyword">import</span> javax.persistence.GeneratedValue;
<span class="code-keyword">import</span> javax.persistence.Id;
<span class="code-keyword">import</span> javax.persistence.ManyToOne;
<span class="code-keyword">import</span> javax.validation.Valid;
<span class="code-keyword">import</span> javax.validation.constraints.NotNull;

<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.constraint.ImageConstraint;
<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.constraint.SequencedImageGroup;

@Entity
@ImageConstraint(groups=ImageGroup.class)
<span class="code-keyword">public</span> class Image {

    <span class="code-keyword">private</span> <span class="code-object">long</span> id;
    <span class="code-keyword">private</span> ImageType type;
    <span class="code-keyword">private</span> <span class="code-object">String</span> fileName;
    <span class="code-keyword">private</span> <span class="code-object">byte</span>[] data;

    @Id
    @GeneratedValue
    <span class="code-keyword">public</span> <span class="code-object">long</span> getId() {
        <span class="code-keyword">return</span> id;
    }

    <span class="code-keyword">public</span> void setId(<span class="code-object">long</span> id) {
        <span class="code-keyword">this</span>.id = id;
    }

    @NotNull(message=<span class="code-quote">"Image type must be specified."</span>)
    @Enumerated(EnumType.STRING)
    <span class="code-keyword">public</span> ImageType getType() {
        <span class="code-keyword">return</span> type;
    }

    <span class="code-keyword">public</span> void setType(ImageType type) {
        <span class="code-keyword">this</span>.type = type;
    }

    @NotNull(message=<span class="code-quote">"Image file name must not be <span class="code-keyword">null</span>."</span>)
    <span class="code-keyword">public</span> <span class="code-object">String</span> getFileName() {
        <span class="code-keyword">return</span> fileName;
    }

    <span class="code-keyword">public</span> void setFileName(<span class="code-object">String</span> fileName) {
        <span class="code-keyword">this</span>.fileName = fileName;
    }

    @NotNull(message=<span class="code-quote">"Image data must not be <span class="code-keyword">null</span>."</span>)
    <span class="code-keyword">public</span> <span class="code-object">byte</span>[] getData() {
        <span class="code-keyword">return</span> data;
    }

    <span class="code-keyword">public</span> void setData(<span class="code-object">byte</span>[] data) {
        <span class="code-keyword">this</span>.data = data;
    }
}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Notice that the @Pattern and @ImageContent were replaced by @NotNull constraints.  The new class level constraint will perform the duties previously performed by @Pattern and @ImageContent.  The @NotNull constraints have been added as a first level check.  If these constraints succeed, the type level validator @ImageConstraint will fire, providing complex validation.  Sequenced validation is provided using validation groups and group sequences.  These concepts will be explained shortly.</p>

<p>Here is the code for the new <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageConstraint.java" class="external-link" rel="nofollow">ImageConstraint</a> annotation:</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>ImageConstraint.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.constraint;

<span class="code-keyword">import</span> java.lang.annotation.Documented;
<span class="code-keyword">import</span> java.lang.annotation.Retention;
<span class="code-keyword">import</span> java.lang.annotation.Target;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> java.lang.annotation.ElementType.TYPE;
<span class="code-keyword">import</span> <span class="code-keyword">static</span> java.lang.annotation.RetentionPolicy.RUNTIME;

<span class="code-keyword">import</span> javax.validation.Constraint;
<span class="code-keyword">import</span> javax.validation.Payload;

<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.model.ImageType;

@Documented
@Constraint(validatedBy = ImageValidator.class)
@Target({ TYPE })
@Retention(RUNTIME)
<span class="code-keyword">public</span> @<span class="code-keyword">interface</span> ImageConstraint {
    <span class="code-object">String</span> message() <span class="code-keyword">default</span> <span class="code-quote">"Image data is not a supported format."</span>;
    <span class="code-object">Class</span>&lt;?&gt;[] groups() <span class="code-keyword">default</span> {};
    <span class="code-object">Class</span>&lt;? <span class="code-keyword">extends</span> Payload&gt;[] payload() <span class="code-keyword">default</span> {};
    ImageType[] value() <span class="code-keyword">default</span> { ImageType.GIF, ImageType.JPEG };
}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Unlike the <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageContent.java" class="external-link" rel="nofollow">ImageContent</a> constraint, <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageConstraint.java" class="external-link" rel="nofollow">ImageConstraint</a> is targeted for a TYPE.  This allows this annotation to be applied at a type level (class or interface).  This constraint has a new validator class, <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageValidator.java" class="external-link" rel="nofollow">ImageValidator</a>.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>ImageValidator.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.constraint;

<span class="code-keyword">import</span> java.util.Arrays;
<span class="code-keyword">import</span> java.util.List;

<span class="code-keyword">import</span> javax.validation.ConstraintValidator;
<span class="code-keyword">import</span> javax.validation.ConstraintValidatorContext;

<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.model.Image;
<span class="code-keyword">import</span> org.apache.openjpa.example.gallery.model.ImageType;

/**
 * Simple check that an Image is consistent in type, name, and format.
 */
<span class="code-keyword">public</span> class ImageValidator <span class="code-keyword">implements</span> ConstraintValidator&lt;ImageConstraint, Image&gt; {
    <span class="code-keyword">private</span> List&lt;ImageType&gt; allowedTypes = <span class="code-keyword">null</span>;
    /**
     * Configure the constraint validator based on the image
     * types it should support.
     * @param constraint the constraint definition
     */
    <span class="code-keyword">public</span> void initialize(ImageConstraint constraint) {
        allowedTypes = Arrays.asList(constraint.value());
    }

    /**
     * Validate a specified value.
     */
    <span class="code-keyword">public</span> <span class="code-object">boolean</span> isValid(Image value, ConstraintValidatorContext context) {
        <span class="code-keyword">if</span> (value == <span class="code-keyword">null</span>) {
            <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
        }

        <span class="code-comment">// All these fields will be pre-validated with @NotNull constraints
</span>        <span class="code-comment">// so they are safe to use without <span class="code-keyword">null</span> checks.
</span>        <span class="code-object">byte</span>[] data = value.getData();
        <span class="code-object">String</span> fileName = value.getFileName();
        ImageType type = value.getType();

        <span class="code-comment">// Verify the GIF type is correct5, has the correct extension and
</span>        <span class="code-comment">// the data header is either GIF87 or GIF89
</span>        <span class="code-keyword">if</span> (allowedTypes.contains(ImageType.GIF) &amp;&amp;
            type == ImageType.GIF &amp;&amp;
            fileName.endsWith(<span class="code-quote">".gif"</span>)) {
            <span class="code-keyword">if</span> (data != <span class="code-keyword">null</span> &amp;&amp; data.length &gt;= 6) {
                <span class="code-object">String</span> gifHeader = <span class="code-keyword">new</span> <span class="code-object">String</span>(data, 0, 6);
                <span class="code-keyword">if</span> (gifHeader.equalsIgnoreCase(<span class="code-quote">"GIF87a"</span>) ||
                     gifHeader.equalsIgnoreCase(<span class="code-quote">"GIF89a"</span>)) {
                    <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
                }
            }
        }
        <span class="code-comment">// Verify the JPEG type is correct, has the correct extension and
</span>        <span class="code-comment">// the data begins with SOI &amp; ends with EOI markers
</span>        <span class="code-keyword">if</span> (allowedTypes.contains(ImageType.JPEG) &amp;&amp;
                value.getType() == ImageType.JPEG &amp;&amp;
                (fileName.endsWith(<span class="code-quote">".jpg"</span>) ||
                 fileName.endsWith(<span class="code-quote">".jpeg"</span>))) {
            <span class="code-keyword">if</span> (data.length &gt;= 4 &amp;&amp;
                    data[0] == 0xff &amp;&amp; data[1] == 0xd8 &amp;&amp;
                    data[data.length - 2] == 0xff &amp;&amp;
                    data[data.length - 1] == 0xd9) {
                <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
            }
        }
        <span class="code-comment">// Unknown file format
</span>        <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
    }
}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>One thing that must be considered in a JPA environment is the load state of the attributes of an entity when doing type-level validation.  Simply accessing attributes can have some side effects.  If the attribute is marked LAZY fetch it may get loaded in order to perform validation.  If the attribute is not loaded and cannot be loaded (for several reasons, most likely due to detachment) you'll be validating  inconsistent data.  The JPA 2.0 specification provides a utility interface to help in these situations.  It can be obtained statically so squirreling away a copy of the JPA entity manager is not necessary.  Here is an example of how PersistenceUtil could be used in the ImageValidator.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
        <span class="code-object">byte</span>[] data = value.getData();
        PersistenceUtil putil = Persistence.getPersistenceUtil();
        <span class="code-keyword">if</span> (!putil.isLoaded(value, <span class="code-quote">"data"</span>)) {
            <span class="code-comment">// don't validate the data
</span>        }
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<h3><a name="BeanValidationPrimer-TheCompleteDomainModel"></a>The Complete Domain Model</h3>

<p>Now that some of the basics of bean validation are covered, let's finish up the domain model for our simple application and then get into JPA specifics through an example.</p>

<p><span class="image-wrap" style=""><img src="/confluence/download/attachments/21792194/ig_domain_model.gif?version=1&amp;modificationDate=1275678905000" style="border: 0px solid black" /></span></p>

<p>The persistent types <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Album.java" class="external-link" rel="nofollow">Album</a>, <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Creator.java" class="external-link" rel="nofollow">Creator</a>, and <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Location.java" class="external-link" rel="nofollow">Location</a> are new to the domain model.  An Album entity contains a reference to collection of its Image entities.  The Creator entity contains a reference to the Album album entities the image Creator contributed to and a reference to the Image entities they've created.  This provides full navigational capabilities to and from each of the entities in the domain.  An embeddable, Location, has been added to Image to allow location information to be stored along with the Image.</p>

<p>The Album and Creator entities have a few built-in constraints and are pretty run of the mill.  The embeddable <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/model/Location.java" class="external-link" rel="nofollow">Location</a> is a bit more interesting in that it demonstrates the use of the @Valid annotation to validate embedded objects.  In order to embed location into an image a new field and corresponding persistent properties were added to the Image class:</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    <span class="code-keyword">private</span> Location location;

    @Valid
    @Embedded
    <span class="code-keyword">public</span> Location getLocation() {
        <span class="code-keyword">return</span> location;
    }

    <span class="code-keyword">public</span> void setLocation(Location location) {
        <span class="code-keyword">this</span>.location = location;
    }
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Notice the use of the @Valid annotation.  This provides chained validation of embeddables within a JPA environment.  Thus, when Image is validated, any constraints on the Location it references are also validated.  If @Valid was not specified, Location would not get validated.  In a JPA environment, chained validation via @Valid is only available for embeddables.  Referenced entities and collections of entities are validated separately in order to prevent circular validation.</p>

<h3><a name="BeanValidationPrimer-ValidationGroups"></a>Validation Groups</h3>

<p>Bean validation uses validation groups to determine what and when validation occurs.  There are no special interfaces to implement or annotations to apply in order to create a validation group.  A validation group is denoted simply by a class definition. However, it is strongly recommended that simple interfaces are used.  This is a best practice since it makes validation groups more usable in multiple environments.  Whereas, if a class or entity definition were used as a validation group, it may pollute the object model of another application by bringing in domain classes and logic that do no make sense for the application. By default, if a validation group or multiple groups is not specified on an individual constraint, it will be validated using the <b>javax.validation.groups.Default</b> group.  Creating a custom group is as simple as creating a new interface definition.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>ImageGroup.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.constraint;

<span class="code-keyword">public</span> <span class="code-keyword">interface</span> ImageGroup {

}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>This new <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageGroup.java" class="external-link" rel="nofollow">ImageGroup</a> validation group can now be applied to a constraint.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @ImageContent(groups={Default.class,ImageGroup.class})
    <span class="code-keyword">public</span> <span class="code-object">byte</span>[] getData() {
        <span class="code-keyword">return</span> data;
    }
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>This @ImageContent constraint in this example will validate when either or both the Default and/or <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/main/java/org/apache/openjpa/example/gallery/constraint/ImageGroup.java" class="external-link" rel="nofollow">ImageGroup</a> group is/are validated.</p>

<p>By default there is no order applied or short circuiting behavior when validation occurs.  Validation ordering and short circuiting is an extremely useful function that can be achieved by defining a group sequence.  A group sequence is defined via the @GroupSequence annotation with an ordered array of validation groups.  When the group sequence is validated, the constraints supplied in its grouping are validated in the order they are specified.  In addition, if a constraint within a group fails, the groups that follow will not be validated.  This allows lightweight validation such as @NotNull or @Size constraints to validate before heavyweight constraints.  The Image class uses a group sequence to validate its lightweight @NotNull constraints (which use the Default validation group) before validating its heavyweight constraint @ImageValidator (which validates when the ImageGroup) is validated.  Sequenced validation can be accomplished by defining a new validation group.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-style: solid;border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;border-bottom-style: solid;"><b>SequencedImageGroup.java</b></div><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.apache.openjpa.example.gallery.constraint;

<span class="code-keyword">import</span> javax.validation.GroupSequence;
<span class="code-keyword">import</span> javax.validation.groups.Default;

@GroupSequence({Default.class, ImageGroup.class})
<span class="code-keyword">public</span> <span class="code-keyword">interface</span> SequencedImageGroup {

}
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>This group is then specified during validation.  How to specify which groups will be validated in a JPA environment is explained in the sections to follow.</p>

<h3><a name="BeanValidationPrimer-JPAIntegration"></a>JPA Integration</h3>

<p>The JPA 2.0 specification makes integration with JSR-303 very simple.  In a JSE environment all you need to do is provide the JSR-303 API and a JSR-303 bean validation provider on your runtime classpath and bean validation is enabled by default.  OpenJPA adds one additional caveat.  With OpenJPA you must also be using a version 2.0 persistence.xml file.  A version 1.0 persistence.xml provides no means to configure bean validation.  Requiring a version 2.0 persistence.xml prevents a pure JPA 1.0 application from incurring the validation startup and runtime costs.  This is important given that there is no standard means for a 1.0-based application to disable validation.  Besides adding the necessary bean validation jars to your classpath, enabling validation in an existing 1.0 application may be as simple as modifying the root element of your persistence.xml to:</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;?xml version=<span class="code-quote">"1.0"</span> encoding=<span class="code-quote">"UTF-8"</span>?&gt;</span>
&lt;persistence xmlns=<span class="code-quote">"http://java.sun.com/xml/ns/persistence"</span>
    <span class="code-keyword">xmlns:xsi</span>=<span class="code-quote">"http://www.w3.org/2001/XMLSchema-instance"</span>
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version=<span class="code-quote">"2.0"</span> &gt;
...
<span class="code-tag">&lt;/persistence&gt;</span>
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>While the JPA 2.0 specification is backward compatible with JPA 1.0, there are some OpenJPA specific extensions that are not.  By switching to a 2.0 persistence.xml, in some cases you may need to specify compatibility flags in order to get OpenJPA 1.0 behavior.  See the <a href="http://openjpa.apache.org/builds/latest/docs/manual/migration_considerations.html" class="external-link" rel="nofollow">migration considerations</a> section of the <a href="http://openjpa.apache.org/builds/latest/docs/manual/main.html" class="external-link" rel="nofollow">OpenJPA 2.x manual</a> for additional information.</p>

<h5><a name="BeanValidationPrimer-ValidationModes"></a>Validation Modes</h5>

<p>Bean validation provides three modes of operation within the JPA environment: <em>auto</em>, <em>callback</em>, and <em>none</em>.  As you may have guessed, <em>none</em> disables bean validation for a particular persistence unit.  The <em>auto</em> mode, which is the default, enables bean validation if a validation provider is available within the classpath.  When <em>callback</em> mode is specified, a bean validation provider must be available for use by the JPA provider.  If not, the JPA provider will throw an exception upon instantiation of a new JPA entity manager factory.  While <em>auto</em> mode simplifies deployment, it can lead to problems if validation unexpectedly not taking place due to a configuration problem.  It is a good practice to use either specify <em>none</em> or <em>callback</em> mode explicitly in order to get consistent behavior.  In addition, if <em>none</em> is specified, OpenJPA will do optimization at startup and will not attempt to perform unexpected validation.  Explicitly disabling validation is especially important in a JEE6 environment where the container is mandated to provide a validation provider.  Thus, unless specified, a JPA 2.0 app running in a container will have validation enabled.  This will add additional processing during lifecycle events. We'll get to lifecycle events shortly.</p>

<p>There are two means to configure validation modes in JPA 2.0.  Perhaps the simplest is to add a validation-mode element to your persistence.xml with the desired validation mode.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
    <span class="code-tag">&lt;persistence-unit name=<span class="code-quote">"auto-validation"</span>&gt;</span>
        ...
        <span class="code-tag"><span class="code-comment">&lt;!-- Validation modes: AUTO, CALLBACK, NONE --&gt;</span></span>
        <span class="code-tag">&lt;validation-mode&gt;</span>AUTO<span class="code-tag">&lt;/validation-mode&gt;</span>
        ...
    <span class="code-tag">&lt;/persistence-unit&gt;</span>
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>In addition, the validation mode can be configured programmatically by specifying the <b>javax.persistence.validation.mode</b> property with value <em>auto</em>, <em>callback</em>, or <em>none</em> when creating a new JPA entity manager factory.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
        Map&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt; props = <span class="code-keyword">new</span> HashMap&lt;<span class="code-object">String</span>,<span class="code-object">String</span>&gt;();
        props.put(<span class="code-quote">"javax.persistence.validation.mode"</span>, <span class="code-quote">"callback"</span>);
        EntityManagerFactory emf = 
            Persistence.createEntityManagerFactory(<span class="code-quote">"validation"</span>, props);

</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<h5><a name="BeanValidationPrimer-ValidationinJPA"></a>Validation in JPA</h5>

<p>We've covered the basics of constraints and validation configuration, now on to actually doing some validation within JPA.  Bean validation within JPA occurs during JPA's lifecycle event processing.  If enabled, validation will occur at the final stage of the PrePersist, PreUpdate, and PreRemove lifecycle events.  Validation will occur only after all user defined lifecycle events, since some of those events may modify the entity that is being validated.   By default, JPA enables validation for the Default validation group for PrePersist and PreUpdate lifecycle events.  If you need to validate other Validation groups or enable validation for the PreRemove event you can specify the validation groups to validate for each lifecycle event in the <a href="http://svn.apache.org/repos/asf/openjpa/trunk/openjpa-examples/image-gallery/src/test/resources/META-INF/persistence.xml" class="external-link" rel="nofollow">persistence.xml</a>.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
    <span class="code-tag">&lt;persistence-unit name=<span class="code-quote">"non-default-validation-groups"</span>&gt;</span>
        <span class="code-tag">&lt;class&gt;</span>my.Entity<span class="code-tag">&lt;/class&gt;</span>
        <span class="code-tag">&lt;validation-mode&gt;</span>CALLBACK<span class="code-tag">&lt;/validation-mode&gt;</span>
        <span class="code-tag">&lt;properties&gt;</span>
            &lt;property name=<span class="code-quote">"javax.persistence.validation.group.pre-persist"</span>
             value=<span class="code-quote">"org.apache.openjpa.example.gallery.constraint.SequencedImageGroup"</span>/&gt;
            &lt;property name=<span class="code-quote">"javax.persistence.validation.group.pre-update"</span>
             value=<span class="code-quote">"org.apache.openjpa.example.gallery.constraint.SequencedImageGroup"</span>/&gt;
            &lt;property name=<span class="code-quote">"javax.persistence.validation.group.pre-remove"</span>
             value=<span class="code-quote">"javax.validation.groups.Default"</span>/&gt;
        <span class="code-tag">&lt;/properties&gt;</span>
    <span class="code-tag">&lt;/persistence-unit&gt;</span>
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Now that we've gone through validation basics and JPA configuration options the validation part is a piece of cake.  In general, you simply need to handle validation exceptions that may result from various JPA operations.  Here are some simple examples for the persist, update, and remove operations.  We'll get to more in-depth exception handling in a moment.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
        EntityManagerFactory emf = 
            Persistence.createEntityManagerFactory(<span class="code-quote">"BeanValidation"</span>);
        EntityManager em = emf.createEntityManager();

        Location loc = <span class="code-keyword">new</span> Location();
        loc.setCity(<span class="code-quote">"Rochester"</span>);
        loc.setState(<span class="code-quote">"MN"</span>);
        loc.setZipCode(<span class="code-quote">"55901"</span>);
        loc.setCountry(<span class="code-quote">"USA"</span>);

        <span class="code-comment">// Create an Image with non-matching type and file extension
</span>        Image img = <span class="code-keyword">new</span> Image();
        img.setType(ImageType.JPEG);
        img.setFileName(<span class="code-quote">"Winter_01.gif"</span>);
        loadImage(img);
        img.setLocation(loc);
        
        <span class="code-comment">// *** PERSIST ***
</span>        <span class="code-keyword">try</span> {
            em.getTransaction().begin();
            <span class="code-comment">// Persist the entity with non-matching extension and type
</span>            em.persist(img);
        } <span class="code-keyword">catch</span> (ConstraintViolationException cve) {
            <span class="code-comment">// Transaction was marked <span class="code-keyword">for</span> rollback, roll it back and
</span>            <span class="code-comment">// start a <span class="code-keyword">new</span> one
</span>            em.getTransaction().rollback();
            em.getTransaction().begin();
            <span class="code-comment">// Fix the file type and re-<span class="code-keyword">try</span> the persist.
</span>            img.setType(ImageType.GIF);
            em.persist(img);
            em.getTransaction().commit();
        }

        <span class="code-comment">// *** UPDATE ***
</span>        <span class="code-keyword">try</span> {
            em.getTransaction().begin();
            <span class="code-comment">// Modify the file name to a non-matching file name 
</span>            <span class="code-comment">// and commit to trigger an update
</span>            img.setFileName(<span class="code-quote">"Winter_01.jpg"</span>);
            em.getTransaction().commit();
        }  <span class="code-keyword">catch</span> (ConstraintViolationException cve) {
            <span class="code-comment">// Handle the exception.  The commit failed so the transaction
</span>            <span class="code-comment">// was already rolled back.
</span>            handleConstraintViolation(cve);
        }
        <span class="code-comment">// The update failure caused img to be detached. It must be merged back 
</span>        <span class="code-comment">// into the persistence context.
</span>        img = em.merge(img);

        <span class="code-comment">// *** REMOVE ***
</span>        em.getTransaction().begin();
        <span class="code-keyword">try</span> {
            <span class="code-comment">// Remove the type and commit to trigger removal
</span>            img.setType(ImageType.GIF);
            em.remove(img);
        }  <span class="code-keyword">catch</span> (ConstraintViolationException cve) {
            <span class="code-comment">// Rollback the active transaction and handle the exception
</span>            em.getTransaction().rollback();
            handleConstraintViolation(cve);
        }
        em.close();
        emf.close();
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<h5><a name="BeanValidationPrimer-ExceptionHandling"></a>Exception Handling</h5>

<p>If one or more constraints fail to validate during a lifecycle event, a ConstraintViolationException is thrown by the JPA provider.  The ConstraintViolationException thrown by the JPA provider includes the a set of ConstraintViolations that occurred.  Individual constraint violations contain information regarding the constraint, including a message, the root bean (JPA entity), the leaf bean (useful when validating JPA embeddables), the attribute which failed to validate, and the value that caused the failure.  Here is a sample exception handling routine:</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    <span class="code-keyword">private</span> void handleConstraintViolation(ConstraintViolationException cve) {
      Set&lt;ConstraintViolation&lt;?&gt;&gt; cvs = cve.getConstraintViolations();
      <span class="code-keyword">for</span> (ConstraintViolation&lt;?&gt; cv : cvs) {
          <span class="code-object">System</span>.out.println(<span class="code-quote">"------------------------------------------------"</span>);
          <span class="code-object">System</span>.out.println(<span class="code-quote">"Violation: "</span> + cv.getMessage());
          <span class="code-object">System</span>.out.println(<span class="code-quote">"Entity: "</span> + cv.getRootBeanClass().getSimpleName());
          <span class="code-comment">// The violation occurred on a leaf bean (embeddable)
</span>          <span class="code-keyword">if</span> (cv.getLeafBean() != <span class="code-keyword">null</span> &amp;&amp; cv.getRootBean() != cv.getLeafBean()) {
              <span class="code-object">System</span>.out.println(<span class="code-quote">"Embeddable: "</span> + cv.getLeafBean().getClass().getSimpleName());
          }
          <span class="code-object">System</span>.out.println(<span class="code-quote">"Attribute: "</span> + cv.getPropertyPath());
          <span class="code-object">System</span>.out.println(<span class="code-quote">"Invalid value: "</span> + cv.getInvalidValue());
      }
    }
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<p>Constraint violation processing is not quite as straight forward when using type-level level validators with type-level constraints as using attribute level constraints. When type-level constraints are used it can be more difficult to determine  which attribute or combination of attributes failed to validate.  In addition, the entire object is returned as the invalid value instead of an individual attribute.  In cases where specific failure information is required, either use an attribute level constraint or a custom constraint violation may be provided as described in section 2.4 of the <a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303 specification</a>.</p>

<h3><a name="BeanValidationPrimer-OpenJPAandApacheBeanValidationLibraries"></a>OpenJPA and Apache Bean Validation Libraries</h3>

<p>If you are not using a maven build environment, the use of bean validation in a JPA environment requires a 2.0 level JPA provider and a bean validation provider libraries.  Apache OpenJPA 2.0 includes a test suite which exercises the Apache Bean Validation provider and this has proven to be a very stable environment.  Here are the libraries you'll need.</p>

<ul>
	<li>Apache OpenJPA 2.0 provides an "all" library which makes it very simple to get OpenJPA 2.0 and all its dependencies in a single jar.  Download and extract the <a href="http://www.apache.org/dyn/closer.cgi/openjpa/2.0.0/apache-openjpa-2.0.0-binary.zip" class="external-link" rel="nofollow">OpenJPA 2.0 binary package</a> and reference the openjpa-all-2.0.0.jar on your build and runtime classpath.</li>
</ul>


<ul>
	<li>Apache Bean Validation does not yet have a canned binary package, but it does publish nightly snapshots of the core valiation library and JSR-303 extensions.  Until a binary package is available from the <a href="http://projects.apache.org/projects/bean_validation__incubating_.html" class="external-link" rel="nofollow">Apache Bean Validation site</a>. You can obtain the core library, JSR-303 packages, and Geronimo JSR-303 spec API directly from the maven repository.
	<ul>
		<li><a href="https://repository.apache.org/content/groups/public/org/apache/bval/bval-core/0.1-incubating/bval-core-0.1-incubating.jar" class="external-link" rel="nofollow">Apache Bean Validation Core</a></li>
		<li><a href="https://repository.apache.org/content/groups/public/org/apache/bval/bval-jsr303/0.1-incubating/bval-jsr303-0.1-incubating.jar" class="external-link" rel="nofollow">Apache Bean Validation JSR-303</a></li>
		<li><a href="https://repository.apache.org/content/groups/public/org/apache/geronimo/specs/geronimo-validation_1.0_spec/1.1/geronimo-validation_1.0_spec-1.1.jar" class="external-link" rel="nofollow">Apache Geronimo Bean Validation Spec API</a></li>
		<li><a href="http://commons.apache.org/beanutils/download_beanutils.cgi" class="external-link" rel="nofollow">Apache Commons Bean Utils</a> - Extract the distribution and include commons-beanutils-1.8.3.jar on your runtime classpath.</li>
		<li><a href="http://archive.apache.org/dist/commons/lang/binaries/commons-lang-2.4-bin.zip" class="external-link" rel="nofollow">Apache Commons Lang</a> - Extract the distribution and <b>include commons-lang-2.4.jar in your classpath before openjpa-all-2.0.0.jar</b>.</li>
	</ul>
	</li>
</ul>


<h3><a name="BeanValidationPrimer-MavenConfiguration"></a>Maven Configuration</h3>

<p>If you a maven user, dependency management is much simpler.  Here are the dependencies you'll need in order to use OpenJPA 2.0 and the current (as of this writing) snapshot of Apache Bean Validation.</p>

<p><br class="atl-forced-newline" /></p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
  <span class="code-tag">&lt;dependencies&gt;</span>
    <span class="code-tag"><span class="code-comment">&lt;!-- When using OpenJPA 2.0.0 with bval, commons-lang must be in the dependency --&gt;</span></span>
    <span class="code-tag"><span class="code-comment">&lt;!-- tree before openjpa-all-2.0.0 since openjpa-all bundles commons-lang 2.1   --&gt;</span></span>
    <span class="code-tag"><span class="code-comment">&lt;!-- and bval requires version 2.4                                              --&gt;</span></span>
    <span class="code-tag">&lt;dependency&gt;</span>
       <span class="code-tag">&lt;groupId&gt;</span>commons-lang<span class="code-tag">&lt;/groupId&gt;</span>
       <span class="code-tag">&lt;artifactId&gt;</span>commons-lang<span class="code-tag">&lt;/artifactId&gt;</span>
       <span class="code-tag">&lt;version&gt;</span>2.4<span class="code-tag">&lt;/version&gt;</span>
       <span class="code-tag">&lt;scope&gt;</span>test<span class="code-tag">&lt;/scope&gt;</span>
    <span class="code-tag">&lt;/dependency&gt;</span>
    <span class="code-tag">&lt;dependency&gt;</span>
      <span class="code-tag">&lt;groupId&gt;</span>commons-beanutils<span class="code-tag">&lt;/groupId&gt;</span>
      <span class="code-tag">&lt;artifactId&gt;</span>commons-beanutils<span class="code-tag">&lt;/artifactId&gt;</span>
      <span class="code-tag">&lt;version&gt;</span>1.8.3<span class="code-tag">&lt;/version&gt;</span>
      <span class="code-tag">&lt;scope&gt;</span>test<span class="code-tag">&lt;/scope&gt;</span>
    <span class="code-tag">&lt;/dependency&gt;</span>
    <span class="code-tag">&lt;dependency&gt;</span>
      <span class="code-tag">&lt;groupId&gt;</span>org.apache.geronimo.specs<span class="code-tag">&lt;/groupId&gt;</span>
      <span class="code-tag">&lt;artifactId&gt;</span>geronimo-validation_1.0_spec<span class="code-tag">&lt;/artifactId&gt;</span>
      <span class="code-tag">&lt;version&gt;</span>1.0<span class="code-tag">&lt;/version&gt;</span>
    <span class="code-tag">&lt;/dependency&gt;</span>
    <span class="code-tag">&lt;dependency&gt;</span>
      <span class="code-tag">&lt;groupId&gt;</span>org.apache.bval<span class="code-tag">&lt;/groupId&gt;</span>
      <span class="code-tag">&lt;artifactId&gt;</span>org.apache.bval.bundle<span class="code-tag">&lt;/artifactId&gt;</span>
      <span class="code-tag">&lt;version&gt;</span>0.1-incubating-SNAPSHOT<span class="code-tag">&lt;/version&gt;</span>
      <span class="code-tag">&lt;scope&gt;</span>test<span class="code-tag">&lt;/scope&gt;</span>
    <span class="code-tag">&lt;/dependency&gt;</span>
    <span class="code-tag">&lt;dependency&gt;</span>
      <span class="code-tag">&lt;groupId&gt;</span>org.apache.openjpa<span class="code-tag">&lt;/groupId&gt;</span>
      <span class="code-tag">&lt;artifactId&gt;</span>openjpa-all<span class="code-tag">&lt;/artifactId&gt;</span>
      <span class="code-tag">&lt;version&gt;</span>2.0.0<span class="code-tag">&lt;/version&gt;</span>
    <span class="code-tag">&lt;/dependency&gt;</span>
  <span class="code-tag">&lt;/dependencies&gt;</span>
</pre>
</div></div>
<p><br class="atl-forced-newline" /></p>

<h3><a name="BeanValidationPrimer-GettingtheSampleApplication"></a>Getting the Sample Application</h3>

<p>The code for the example provided in this primer is currently available from the OpenJPA subversion repository.  Obtaining, building and running the example requires <a href="http://subversion.apache.org" class="external-link" rel="nofollow">Subversion</a>, <a href="http://maven.apache.org/download.html" class="external-link" rel="nofollow">Maven</a> version 2.2.1 or later and JDK version 1.6.  Be sure to set your JAVA_HOME environment variable to the location of the 1.6 JDK.  To get and run the sample you must first get the source for OpenJPA via:</p>

<p><b>svn co</b> <a href="http://svn.apache.org/repos/asf/openjpa/trunk" class="external-link" rel="nofollow">http://svn.apache.org/repos/asf/openjpa/trunk</a></p>

<p>Next, from the trunk directory, run a quick build to generate the OpenJPA provider jars.</p>

<p><b><em>mvn -Dtest install -DfailIfNoTests=false -Dmaven.test.skip=true</em></b></p>

<h3><a name="BeanValidationPrimer-RunningtheSampleApplication"></a>Running the Sample Application</h3>

<p>Currently, the sample only runs a jUnit which shows how bean validation can be used within the context of JPA persist, update, and remove operations.  To build and run the sample using maven:</p>

<p>1. Goto the <b>openjpa-parent/openjpa-examples/image-gallery</b> directory within the source tree you downloaded in the previous section.</p>

<p>2. Run: <b><em>mvn clean install</em></b></p>

<p>This command builds the image-gallery sample, enhances the entity classes, and runs the jUnits.  Within the Maven output, you should see output similar to the following.</p>

<p><br class="atl-forced-newline" /></p>
<blockquote>
<p>Persisting an entity with non-matching extension and type<br/>
------------------------------------------------<br/>
Violation: Image data is not a supported format.<br/>
Entity: Image<br/>
Attribute:<br/>
Invalid value: org.apache.openjpa.example.gallery.model.Image@8d5aad<br/>
------------------------------------------------<br/>
Fixing the file type and re-attempting the persist.<br/>
Persist was successful<br/>
Modifying file name to use an extension that does not<br/>
match the file type.  This will cause a CVE.<br/>
Update failed as expected<br/>
------------------------------------------------<br/>
Violation: Image data is not a supported format.<br/>
Entity: Image<br/>
Attribute:<br/>
Invalid value: org.apache.openjpa.example.gallery.model.Image@8d5aad<br/>
------------------------------------------------<br/>
Setting the type to an invalid type.  This will cause a<br/>
validation exception upon removal<br/>
Remove failed as expected<br/>
------------------------------------------------<br/>
Violation: Image type must be specified.<br/>
Entity: Image<br/>
Attribute: type<br/>
Invalid value: null<br/>
------------------------------------------------<br/>
Done</p></blockquote>
<p><br class="atl-forced-newline" /></p>

<h3><a name="BeanValidationPrimer-ImportingtheSampleintoEclipse"></a>Importing the Sample into Eclipse</h3>

<p>If you'd like to import the sample into Eclipse, from the image-gallery directory run:</p>

<p><b>mvn eclipse:eclipse</b></p>

<p>This will generate the necessary Eclipse project files.  Next, direct Eclipse to import a project from this directory.  You'll need to set the M2_REPO path variable to point at the location of your local Maven repository in order to get the project to build.</p>

<h3><a name="BeanValidationPrimer-References"></a>References</h3>

<p><a href="http://jcp.org/en/jsr/detail?id=317" class="external-link" rel="nofollow">JSR-317 - JPA 2.0 Specification</a><br/>
<a href="http://jcp.org/en/jsr/detail?id=303" class="external-link" rel="nofollow">JSR-303 - Bean Validation Specification</a><br/>
<a href="http://openjpa.apache.org/openjpa-200.html" class="external-link" rel="nofollow">OpenJPA 2.0 Release</a><br/>
<a href="http://projects.apache.org/projects/bean_validation__incubating_.html" class="external-link" rel="nofollow">Apache Bean Validation Home</a></p>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action" class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/openjpa/Bean+Validation+Primer">View Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=21792194&revisedVersion=10&originalVersion=9">View Changes</a>
                |
        <a href="https://cwiki.apache.org/confluence/display/openjpa/Bean+Validation+Primer?showComments=true&amp;showCommentArea=true#addcomment">Add Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message