Better JPA, Better JAXB, and Better Annotations Processing with Java SE 6


by Dustin Marx
 

Apply Java SE 6 incorporation of annotation processing and JAXB 2.0 to improve your JPA-based application deployment experience.

Published September 2007

Recently released Java SE (Standard Edition) 6 brings greater convenience and greater usability to several portions of the Java programming language and platform. In particular, Java SE 6 makes it easier for Java developers to apply JAXB (Java Architecture for XML Binding) and annotations processing to their development efforts, including Java Persistence API (JPA).

In this article, you will learn how to take advantage of Java SE 6 support for JAXB and annotations processing to make deployment of JPA-based applications easier, especially for application deployers who may lack familiarity with the underlying Java code. Along the way, a few additional new Java SE 6 features will be demonstrated as well.

Java 6 Brings It All Together

Java 6 not only introduces new tools and features, but it also integrates improved versions of existing features into its standard toolset. The following table demonstrates how Java 6 includes features that are the culmination of lessons learned from previous experiences with these technologies. The table includes the Java Specifications (JSRs) under which J2SE 5 and Java SE 6 were managed as well as the JSRs under which the other technologies in the table were managed in the Java Community Process (JCP).

Feature J2SE 5.0 ("Tiger") JSR 176 Java SE 6 ("Mustang") JSR 270
JAXB Reference Implementation Obtained separately via JWSDP (Java Web Services Developer Pack) JSR 222 ("Java Architecture for XML Binding 2.0")
Annotations Processing

JSR 175 ("A Metadata Facility for the Java Programming Language")

"apt" tool available for processing these new annotations
JSR 269 ("Pluggable Annotation Processing API")
JPA JSR 220 (Enterprise JavaBeans 3.0)*

In the case of both JAXB and annotation processing, separate tools for working with these concepts were integrated into the standard Java SE 6 toolset. The JAXB reference implementation (RI) formerly had to be downloaded as part of the Java Web Services Developer Pack (JWSDP) separate from J2SE downloads. Likewise, annotation processing before Java SE 6 was accomplished with a fairly complicated apt tool (annotations processing tool) that was separate from the Java compiler. Now, with Java SE 6, the JAXB reference implementation (including its xjc compiler) is delivered with Java SE 6 and an easier-to-use and more sophisticated annotation processing framework is also available "out of the box" as part of the javac Java compiler.

While JPA was a product of the Java EE (Enterprise Edition) specification for improved Enterprise JavaBeans (EJB) 3.0, the Java Persistence API can be used with both SE and EE versions of Java. JPA has not been formally brought into Java SE as of Java SE 6, but the JPA specification spells out Java SE support as a requirement of all JPA providers. Additionally, there is talk of the JSR 220 persistence mechanism being incorporated officially in Java 7.

How JPA, Annotations Processing, and JAXB Go Together

The JPA specification allows developers to annotate Java classes as entities that can be persisted to a data store and to provide other metadata information in the actual Java source code. JPA also allows developers and application deployers to designate this same object-relational mapping data in an external XML file. Developers and deployers can choose to have the XML file override in-source annotations in select cases or have the XML mapping completely override any source code annotations.

Developers like in-source annotations because the metadata is in the same location as the code it describes. On larger projects, the application deployment team may not always be the same as the development team and, in such cases, there may be advantages to overriding in-source annotations with externally described object-relational mapping metadata. Other reasons for preferring externally defined object-relational mapping data over in-source annotations include stylistic preferences (general preference for external configuration over in-code configuration), frequently changing annotation values, and the ability to make configuration changes and without recompiling source code.

Because developers generally seem to prefer in-source annotations, but there may be advantages to expressing object-relational metadata externally, it can be useful to have a tool that converts in-source annotations into the external XML format. The JPA specification describes both the in-source annotations and the external XML format for expressing this metadata and thus explicitly describes the source format and target format of such a metadata conversion.

To convert source-code annotations into XML, the annotations and their values must be read. This is where Java 6's built-in support for annotations processing comes in. Once the annotations and their values are read, they must be written out in the XML format prescribed via XML Schema in the JPA specification. JAXB handles this part. The JAXB reference implementation's xjc compiler will create Java classes that write appropriate XML that will be valid against the JPA's XML Schema. Figure 1 shows a simplified graphical perspective of how a tool might use annotations processing and the JAXB reference implementation to convert in-source Java annotations to external XML data describing the same JPA mapping metadata.

Figure 1
Figure 1. Proposed JPA Annotations Processing Tool Using Java 6 SE

The JPA specification allows the XML metadata to either selectively override in-source annotations or completely override all annotations. The choice of these override approaches is specified in the XML file, but not in the source code.

Additional Brief Introduction to the Java Persistence API

Some addition introductory discussion of JPA is necessary here to help explain the need for the proposed annotations processor and to help explain the JPA annotations involved. (For more thorough background information on JPA, there are many excellent JPA resources provided by OTN.) The JPA reference implementation is provided by Oracle Corporation individually as TopLink Essentials or bundled with GlassFish (the open source reference implementation Java EE application server). Further JPA details are also available at the GlassFish persistence page.

The Java Persistence API was introduced as part of the EJB 3.0 specification to accommodate simpler development of entity EJBs. While JPA was introduced for EJB 3.0, one of the major advantages of JPA is that it can be used in a Java EE or Java SE environment. With JPA, we finally have a single standards-based approach for database access that is common across Java SE and Java EE.

For EJB 3.0, EJBs are now normal Java classes with no special interfaces to implement or parent classes to extend. Nearly any existing Java class can be converted into an Entity simply by annotating that class as such. This is either done with in-source annotations that directly "decorate" the code or with external XML configuration files or with a combination of the two. The latter approach of using external XML configuration files is more reminiscent of earlier versions of EJBs with their XML-formatted deployment descriptors.

The JPA specification allows for JPA classes to be annotated or decorated either via in-source annotations, via the XML configuration files, or both. This article proposes and partially builds an annotation processor that converts in-source JPA annotations to their equivalent XML representation. This example not only demonstrates how in-source annotations could be used to develop external configuration files, but more importantly provides a real-life example of the power and flexibility of Java 6. In particular, it highlights Java 6's built-in support for annotation processing and Java-to-XML binding with a JAXB 2.0 implementation.

Annotations Processing in Java

Annotations were only introduced with Java 5, but they have already become a predominant language feature in Java SE, Java EE, and in many third-party products and frameworks used with Java SE and Java EE. There seems to be no limit to reasons people have found to write and use annotations as a Java metadata mechanism.

Annotations Processing in Java 5: Annotations Introduced

Annotations were introduced with J2SE 5 as a standardized method for specifying metadata directly within in Java language source code. The concept and syntax behind Java annotations are similar to Javadoc-style tags and XDoclet-style tags. J2SE 5 provided a separate tool for processing these newly introduced annotations called the Annotation Processing Tool (apt).

In 2004 OTN published a series of articles by Jason Hunter ("Making the Most of Java's Metadata") on writing, using, and processing annotations in Java 5. The first article in the series introduces Java annotations and the second article describes creating custom annotations. The third article in the series describes annotation processing in Java 5 using apt.

The four primary packages used with J2SE 5 annotation processing reference implementation are all sub-packages under com.sun.mirror. They are apt, declaration, type, and util. It is worth emphasizing here that in J2SE 5 the apt tool uses packages that all begin with "com.sun.mirror."

Annotations Processing in Java 6: Built-in to javac

Java SE 6 has introduced multiple improvements to annotation processing. One of the most obvious improvements is incorporating annotation processing as an integral function of the javac compiler. Developers wishing to process annotations no longer need to run a separate tool like apt, but can instead use the javac compiler directly.

The Java 6 changes to javac are easily identified by simply running "javac -help" for both J2SE 5's javac and Java SE 6's javac. The most notable additions to the "help" documentation for javac is the addition of options related to annotations processing. Options new to Java SE 6 for annotations processing include the options briefly described in the table below.

Java 6 javac Annotation Processing Options Description
-processor Used to explicitly identify classes over which annotation processing should be run. This will be the method discussed most here, though automatic discovery can be used instead when processing annotations in Java code.
-proc Optional flag that allows person compilng code to designate whether annotations processing should not be performed when normal Java source code compilation occurs or if annotation processing without any normal compilation should occur.
-processorpath Path which annotation javfac processor should search for classes with annotations to be processed.
-A<key>[=value] Option for passing options from javac command to the annotation processor. The value is optional.
-XprintRounds Nonstandard option that provides detailed information about the annotation processor's rounds. This includes useful information such as the input files being processed by the annotation processor and all the annotations that were found and potentially processed in each round.
-XprintProcessorInfo Provides list of annotations that annotation processor is requested to process.

The final two options listed above are nonstandard (note they bein with "-X"), but are highly useful in annotations processing with the Sun-provided javac. The javac classpath options (-cp or -classpath) are used in annotation processing as well.

The main packages used in Java SE 6 annotation processing are javax.annotation.processing and javax.lang.model (and sub-packages). The javax.tools package also provides several useful classes in annotations processing.

The javax.annotation.processing package is new to Java SE 6 and is a significant aid in writing annotation processors. Figure 2 shows a simplified class diagram that demonstrates the relationships between some key interfaces and classes in this package.

Figure 2
Figure 2. Key Annotations Processing Interfaces and Classes

The Processor interface (javax.annotation.processing package) is the interface used in annotation processing. Rather than implement this interface directly, annotation processors are often written to extend the AbstractProcessor class (same javax.annotation.processing package). The most significant method spelled out in the Processor interface and implemented by the concrete classes that extend AbstractProcessor is the process method. It is through this method that the annotation processor provider provides the annotations available for processing. This method also provides information about the current round of processing to the custom processor extending implementing the Processor interface (and probably extending the AbstractProcessor).

The classes shown in Figure 2 as part of the package marx.apt.jpa are custom-developed classes for this article. These classes partially implement an annotation processor focused on writing an object-relational mapping XML descriptor file based on in-code JPA annotations.

The next code listing (Listing 1) shows how the process method might be defined for an implementation of the Processor interface intended to specifically process JPA-related annotations. Besides showing the process method, some of the encompassing class structure is also shown.

Listing 1

/**
 * Creates orm.xml file based on annotations in JPA-based code.
 */
@SupportedAnnotationTypes("javax.persistence.*")
@SupportedSourceVersion(RELEASE_6)
public class AnnotationEntityProcessor extends AbstractProcessor
{
   // . . . other code . . .

   /**
    * Process JPA-specific annotations in Java entity classes.
    * 
    * @param aAnnotations Matching annotations to be processed.
    * @param aRoundEnvironment Annotation processing round environment.
    * @return
    */
   @Override
   public boolean process( Set<? extends TypeElement> aAnnotations, 
                           RoundEnvironment aRoundEnvironment )
   {
      processingEnv.getMessager().printMessage(
         Diagnostic.Kind.NOTE,
         "Entered AnnotationEntityProcessor.process ...");

      Set<? extends Element> elements = aRoundEnvironment.getRootElements();
    
      for (Element element: elements)
      {
         handleRootElementAnnotationMirrors(element);
      }

      if (aRoundEnvironment.processingOver())
      {
         processingEnv.getMessager().printMessage(
            Diagnostic.Kind.NOTE,
            "EntityProcessor processing completed." );

         preparePersistenceUnitMetadata();
         prepareFixedEntityMappings();

         writeMappingXmlFile();
      }

      return true;
   }

   // . . . other code . . .
}


While the bulk of the actual annotation processing is buried in the handleElementAnnotationMirrors(Element) method, there are still several items of note in this code listing. The code listing shows that this class does extend the AbstractProcessor class and does override the process method.

In an interesting twist, this annotation processing class has its own set of annotations. The @SupportedAnnotationTypes("javax.persistence.*") and @SupportedSourceVersion(RELEASE_6) annotations provide metadata information about the AnnotationEntityProcessor class and are generally used to decorate any annotation processing class one might write. In this example, the first annotation states that this annotation processor handles all JPA-related annotations. The javax.persistence.* limits the annotation processor to JPA-related annotations because all JPA-related annotations, and only JPA-related annotations, are defined in that package. The second annotation, as its name implies, designates the Java version supported for processed source code (Java SE 6 in this case).

A third annotation decorating the processor code in the listing above is the @Override annotation. This is a supported built-in annotation in J2SE 5 and is useful in ensuring that one has correctly overridden a parent's method. In this case, it helps ensure that AnnotationEntityProcessor's process method implementation really does override AbstractProcessor's process method.

Among other things, the AbstractProcessor class provides all descendent classes access to a ProcessingEnvironment (same package). The ProcessingEnvironment is a hook for the annotation processor to several useful items such as a custom file handler for annotation processing (Filer), a custom messager for annotation processing (Messager), and access to an Elements interface (javax.lang.model.util package) that provides annotation processing utilities. The code listing above demonstrates use of the processEnv field provided by AbstractProcessor to access a Messager (getMessager method) to log information (printMessage method).

The code in Listing 1 also makes use of the passed-in information about the round's environment to access the root elements of that round (getRootElements() method). In this case, the root elements will be the entity Java classes being processed.

JPA Annotations to be Processed

The annotations that our annotation processor needs to handle are those that decorate Java classes as entities and provide object-relational mapping metadata about these entity classes. OTN provides a comprehensive list of these Java Persistence API annotations with useful details on each JPA annotation. Of course, the JPA specification (the EJB 3.0 specification broken out from the Core EJB 3.0 specification and focusing on JPA) also spells out the expected annotations in Chapter 8 ("Metadata Annotations") and Chapter 9 ("Metadata for Object/Relational Mapping"). Finally, the Javadoc-created API documentation for the package with these annotations (interfaces) provides examples of using many of the JPA annotations in each annotation interface's description comments.

The code in Listing 1 includes its own annotation, @SupportedAnnotationTypes("javax.persistence.*"), to denote which annotations this particular annotation processor supports. While I could have expressed each JPA annotation to be processed individually with the @SupportedAnnotationTypes annotation, it was simpler to use the asterisk as a wild card.

One of the most significant of the JPA annotations is @Entity, the annotation used to decorate a normal Java class as a persistable entity. The following code snippet (Listing 2) shows one approach to processing Java input source files for classes marked as entities with this attribute. This is a partial listing of code for the method handleRootElementAnnotationMirrors that was invoked in Listing 1.

Listing 2

/**
    * Process the provided Element for its JPA-related annotations.
    * It is expected that the Element provided will be one of the "root elements"
    * encountered when JPA classes are processed by the annotation processor.
    * This will ensure that annotations such as @Entity and @NamedNativeQuery
    * will be at this level.
    * 
    * @param aElement A "root" element (JPA-decorated class).
    */
   private void handleRootElementAnnotationMirrors(Element aElement)
   {
      processingEnv.getMessager().printMessage(
         Diagnostic.Kind.NOTE,
         "Entered handleElementAnnotationMirrors("
            + aElement.getSimpleName() + ") method..." );
      final String simpleName = aElement.getSimpleName().toString();
      List<? extends AnnotationMirror> annotationMirrors = 
         aElement.getAnnotationMirrors();
      marx.jpa.persistence.jaxb.Entity entity = this.of.createEntity();

      for (AnnotationMirror mirror: annotationMirrors)
      {
         final String annotationType = mirror.getAnnotationType().toString();

         if ( annotationType.equals(javax.persistence.Entity.class.getName()) )
         {
            populateEntity(entity, mirror, simpleName);
            furtherPopulateEntity( entity, aElement );
         }
         else if ( annotationType.equals(
            javax.persistence.NamedNativeQueries.class.getName()) )
         {
            createNativeNamedQuery(mirror);
         }
         else if ( annotationType.equals(
            javax.persistence.DiscriminatorColumn.class.getName()) )
         {
            addDiscriminatorColumnToEntity(entity, mirror);
         }
         else if ( annotationType.equals(
            javax.persistence.DiscriminatorValue.class.getName()) )
         {
            addDiscriminatorValueToEntity(entity, mirror);
         }
         else if ( annotationType.equals(
            javax.persistence.Inheritance.class.getName()) )
         {
            addInheritanceToEntity(entity, mirror);
         }
         else if ( annotationType.equals(
            javax.persistence.SequenceGenerator.class.getName()) )
         {
            addSequenceGeneratorToEntity(entity, mirror);
         }
         else if (    annotationType.equals(
                         javax.persistence.PersistenceContext.class.getName()) 
                   || annotationType.equals(
                         javax.persistence.PersistenceContexts.class.getName())
                   || annotationType.equals(
                         javax.persistence.PersistenceUnit.class.getName()) 
                   || annotationType.equals(
                         javax.persistence.PersistenceUnits.class.getName()) )
         {
            processingEnv.getMessager().printMessage(
               Diagnostic.Kind.MANDATORY_WARNING,
               "AnnotationType " + annotationType +
                  " should map to the persistence.xml file instead of orm.xml.");
         }
         else    // Flag any JPA annotations available but not handled
         {
            processingEnv.getMessager().printMessage(
               Diagnostic.Kind.MANDATORY_WARNING,
               "AnnotationType " + annotationType + " not handled currently." );
         }
      }  // end of for loop over annotationMirrors

      // An @Entity annotation was encountered and is ready to be added to
      // the JAXB EntityMappings element as a sub-element.
      if ( (entity.getName() != null) && (!entity.getName().isEmpty()) )
      {
         this.em.getEntity().add(entity);
      }
   }


Listing 2 provides a glimpse of the power behind the AnnotationMirror interface, which its Javadoc comments describe as a representation of a particular annotation. This code obtains the annotation type from the AnnotationMirror and handles different annotation types appropriately.

Some JAXB-related code appears in this listing as well (code related to the local Entity variable called "entity"), but I will cover the JAXB portion in more detail a little later. An interesting side note here is that the method isEmpty() that was added to Java's String class in Java 6 is used in this listing toward the end of the listing. This new method removes the need to check for length greater than zero or for an empty String.

In Listing 2, the final "else if" statement checks for annotations on the root elements that are one of the annotations associated with a JPA-based application's persistence.xml file. Because these annotations would be written to a persistence.xml file rather than to an orm.xml or otherwise named object-relational mapping file, this particular processor does not need to do anything with them. We could have excluded these altogether from use by our annotations process by spelling out (using the @SupportedAnnotationTypes processor-level annotation) only the annotations in javax.persistence that we wanted to process (those with corresponding entries in an orm.xml file), but that is a long list of annotations. It was easier to simply specify javax.persistence.* to get all JPA-related annotations for processing and filter out the relatively small percentage of these annotations that do not apply to the orm.xml file.

The final "else" block in Listing 2 is present to make it easier to identify annotations that this tool should likely handle, but for some reason does not handle. If code execution ever gets to this point in the code, it means that the @SupportedAnnotationTypes annotation on that processor class advertised that this processor supports that particular annotation, but this handleRootElementAnnotationMirrors method was not written to expect and handle that annotation. This might occur if we had not yet written code to handle that annotation or if a new annotation was added in a future JPA release

The handleRootElementAnnotationMirrors method in Listing 2 calls several other methods to build up the JAXB objects for writing out of the JPA object-relational mapping file. We will look at only a small sample of these in detail here, but most of them follow the same general patterns and extract in-source annotation information and store that information in JAXB objects that will later be used to write the ORM XML file.

The Power of AnnotationMirror

One of the methods invoked in the code in Listing 2 is the populateEntity method. This method's code is shown in Listing 3.

Listing 3.

/**
    * Accept an Entity object that corresponds to an Entity XML element and
    * populate the given Entity element with its name as acquired from the
    * in-source @Entity annotation or use entity class' name as default name if
    * it is not explicitly specified in the annotation.
    * 
    * @param aEntity XML Entity element for which entity name should be supplied.
    * @param aMirror In-code Entity's annotation mirror.
    * @param aSimpleName Name of class holding @Entity annotation.
    */
   private void populateEntity( marx.jpa.persistence.jaxb.Entity aEntity,
                                final AnnotationMirror aMirror,
                                final String aSimpleName )
   {
      processingEnv.getMessager().printMessage(
         Diagnostic.Kind.NOTE,
         "Entered createEntity(Entity,AnnotationMirror,String)..." );

      try
      {
         Map<? extends ExecutableElement, ? extends AnnotationValue> mirrorMap = 
            aMirror.getElementValues();

         String entityName = aSimpleName; // Use element's name by default

         for (Map.Entry mirrorEntry : mirrorMap.entrySet())
         {
            String mirrorKey = mirrorEntry.getKey().toString();

            // The name() attribute of the Entity annotation will only be
            // available if it was explicitly set when that annotation was used.
            // If it was not explicitly set, this attribute will not be available.
            if ( mirrorKey.equals(ANNOTATION_KEY_NAME) )
            {
               // The string-formatted entity name includes double quotes
               // that must be removed before storing names in XML.
               entityName = trimDoubleQuotes(mirrorEntry.getValue().toString());
            }
         }

         aEntity.setName(entityName);
      }
      catch (Exception ex)
      {
         processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            "createEntity: " + ex.getMessage() );
      }
   }


The method populateEntity in Listing 3 accepts a JAXB-generated class for eventual use in writing out the Entity element in the object-relational XML mapping file. It is fully scoped in this code listing because the processor class also accesses the Entity annotation interface, which is javax.persistence.Entity. Scoping the JAXB-generated Entity class allows for easy differentiation of the JAXB-generated class for writing an Entity element from the JPA Entity annotation.

Listing 3 also demonstrates a pattern that is repeated multiple times in processing of JPA annotations for constructing the orm.xml file. The annotation mirror passed into populateEntity provides its elements as a Map<? extends ExecutableElement, ? extends AnnotationValue> via its getElementValues() method. The "key" portion of this Map, as indicated by the generics notation "? extends ExecutableElement", satisfies the ExecutableElement interface. The ExecutableElement is used to represent, among other things, annotation type elements. The "value" portion of the returned Map, as indicated by the generics notation "? extends AnnotationValue", represents an Annotation's value. Therefore, the AnnotationMirror.getElementValues() method is useful for returning annotation elements and their respective annotation values in this Map. This technique is used throughout the example JPA annotations processor code to obtain annotations and their values for writing out to the XML mapping file.

Using the powerful annotation modeling of Java SE 6 as partially demonstrated in the previous code listings makes all JPA annotations in source code available to our processor. Next, we will turn our attention to using JAXB to write out all of this data retrieved from in-source annotations as an XML mapping file.

Additional Information on Annotations Processing

There are several good references on annotations processing with J2SE 5 and the apt tool. As mentioned previously, OTN has published a series of articles with the titles "Making the Most of Java's Metadata" and the third part ("Advanced Processing") focuses on Java 5 annotations processing with apt.

Some of the best documentation on annotations processing with Java SE 6 is available from the Java SE 6 Javadoc-based API documentation for the significant classes used in annotations processing. In particular, the Javadoc-generated API documentation for the Processor (in the javax.annotation.processing package) interface contains highly useful text in the main section describing the interface itself and how annotation processing is meant to work in Java SE 6.

Finally, the Java SE 6 distribution includes an example annotations processor class in the sample/javac/processing/src directory under the JDK directory. This particular annotations processor provides a useful example of applying the Visitor design pattern in a processor and using the ElementScanner6 class.

Java 6 and the Java Architecture for XML Binding (JAXB)

JAXB has been around for many years, but Java SE 6 is the first version of Java to incorporate a JAXB implementation into its standard delivery. Originally, the Java Web Services Developer Pack (JWSDP) was the primary source of the JAXB reference implementation. Then, JAXB RI (reference implementation) was made available as a separate download. Now, JAXB RI is obtained directly with a download of Java SE 6 and no extra download of JAXB is required.

The easiest way to verify that the JAXB 2.0 reference implementation has been delivered with a Java SE 6 download is to run its XJC compiler with the -version option. You can either place the directory in which the xjc compiler resides (<JDK_1.6.x_HOME>/bin) in your path or go to that directory and then type in the command: xjc -version. On my computer, I see the following version message:

xjc version "JAXB 2.0 in JDK 1.6" 
JavaTM Architecture for XML Binding(JAXB) Reference Implementation, (build JAXB 2.0 in JDK 1.6)


As the output message above shows, the JAXB 2.0 Reference Implementation (RI) is explicitly associated with the JDK 1.6 downloaded from Sun Microsystems' web site.

There are several advantages to having a JAXB implementation delivered as part of the Java SE distribution. Before Java SE 6, several steps were required to acquire and correctly install the JAXB reference implementation for use with Java applications. The JAXB reference implementation had to be downloaded separately from Java SE distributions and then had to be unzipped and the JAXB_HOME environment variable had to be set correctly. Then, any Java applications making use of JAXB-generated classes and runtime binding to XML needed to include the JAXB libraries during compilation and runtime. None of these steps are necessary with Java SE 6 because the JAXB reference implementation is available and ready for use with the normal installation of Java SE 6.

Note that newer versions of the JAXB RI or different implementations of the JAXB specification must still be downloaded separately from Java SE 6. For example, as of this article's writing, JAXB RI 2.1.4 can be downloaded separately while only JAXB RI 2.0 is available with Java 6 SE. Also, there are implementations of JAXB other than the reference implementation. These other versions include TopLink's JAXB implementation (TopLink 11g Preview includes an implementation that is JAXB 2 compliant) and Apache's JaxMe 2.

The Advantages of JAXB 2.0

Because Java SE 6 incorporates JAXB 2.0 rather than an older version, there are additional benefits we can take advantage of from this newer version. The benefits of JAXB 2.0 over JAXB 1.0 are described briefly here and then we move onto how to apply JAXB to write out the XML files produced from the in-source annotations we processed.

Besides the integration of the JAXB 2.0 reference implementation with Java 6, arguably the biggest advantage of JAXB 2.0 reference implementation over the JAXB 1.0 reference implementation is how much simpler the JAXB-generated class structure is with JAXB 2.0 RI as compared with JAXB 1.0 RI. Prior to JAXB 2.0 RI, the JAXB xjc compiler would create many different interfaces and implementation classes for each XML element to be modeled. JAXB 2.0 RI significantly reduces this number of generated classes by replacing the interfaces and implementations with "value classes."

There is no bgm.ser file to worry about with JAXB 2.0 and there is only one class per modeled structure from the source XML Schema file. Besides the simple Java classes that map to the XML Schema structures, the only other JAXB 2.0 RI-generated classes are the ObjectFactory class (also part of JAXB 1.0) and package-info.java. No jaxb.properties file is required with the JAXB 2.0 reference implementation if no special mappings or customization is necessary and the reference implementation is the implementation used.

Another significant difference in JAXB 2.0 as compared to JAXB 1.0 is the introduction of JAXB-specific annotations in JAXB 2.0. JAXB 1.0 predated J2SE 5, so it is no surprise that this earlier version of JAXB did not have annotation support. The newly introduced annotations are part of the reason that JAXB 2.0 generates so many fewer (and cleaner) classes. Support for annotations in JAXB 2.0 also makes customizing generated Java classes easier than using the external binding configuration file approach to customization or changing source XML Schema file for JAXB's benefit. For our purposes, we do not need to customize the Java classes generated for the JPA XML Schema, so we will not need to use any of these customization approaches.

Related to customizing JAXB generation of Java classes, one of the advantages for our particular use of JAXB 2.0 is that we do not need to do anything special to customize our generated classes for use with our orm_1_0.xsd schema. If we had only JAXB 1.0 RI available to us, we would have to use a customization binding file to tell the xjc compiler how to handle attributes in our schema that are named "class" and conflict with Java's Object.getClass method.

JAXB 2.0 RI automatically names the generated methods for handling "class" attributes with names like "clazz" so the get/set methods do not conflict with Object's methods of the same name. JAXB 2.0 RI-generated classes annotate these "clazz" attributes with the @XmlAttribute(name="class", true) annotation to map them to attributes in the schema called "class." So, not only does JAXB 2.0 RI make it easier to customize bindings of Java objects to existing XML Schema, but its xjc compiler also does it automatically in our particular case when a customization is required. With JAXB 1.0 RI, we would have had to customize the Java binding ourselves and would have had to do so with a customization file (or by altering the source XML Schema) rather than with annotations on the generated code.

One final JAXB 2.0 advantage is its support for generating XML Schema from Java classes. Prior to JAXB 2.0, JAXB was only used to generate Java classes from a source XML Schema or similar XML definition document (such as DTD and RelaxNG). With JAXB 2.0, however, the automatic generation can occur in the opposite direction, generating XML Schema from existing Java classes. We do not delve any further into this feature here because we only need the ability to generate Java classes that map to existing XML Schema for our purposes and that has been available since JAXB's humble beginnings.

Practical Use of JAXB 2.0 in Our Annotations Processor

When no customization is required or desired, the JAXB reference implementation is very simple to use. However, there are situations in which some customization is necessary and JAXB spells out how this customization can be achieved. In our case, the XML Schema that is being used for generation of Java classes has its elements nicely named in a manner that makes for convenient names in the JAXB-generated Java classes as well. Because the default JAXB conventions work well for our situation and to keep things simple, we will not use either customization method in our example.

It is a fairly straightforward process to generate Java classes from a source XML Schema. The first step is to identify the XML Schema for which the JAXB-generated classes are desired. Because we ultimately wish to create an orm.xml (or similarly named) object-relational mapping file for our JPA application, we need to locate the XML Schema file describing that mapping file's structure. There are several places we can find this file. One obvious choice is the JPA specification itself. Specifically, the XML Schema is available in Section 10.2 of the "Java Persistence API" document that is one part of the EJB 3 specification.

JAXB implementations provide a "binding compiler" for creating Java classes that can be bound to the XML Schema from which the classes were generated. Typing "xjc -help" for the JAXB reference implementation obtains usage and help information regarding the JAXB reference implementation's binding compiler. For our example, we only need to know three things: the XML Schema from which we wish to generate our binding Java classes, the Java package we wish to place these generated classes in, and the destination directory where we wish the generated Java classes to be placed.

If we assume that we want all JAXB-generated classes to be placed in a Java package called marx.jpa.persistence.jaxb and that we want these generated classes to be written under the directory C:\jpaJaxb\src, we can use the following JAXB binding compiler command to make it happen: xjc -p marx.jpa.persistence.jaxb -d C:\jpaJaxb\src orm_1_0.xsd The JAXB reference implementation's binding compiler is invoked with the "xjc" executable. The package under which we want all the generated Java classes to live is provided with the -p option and the destination directory for the files containing these generated classes is specified with the -d option. Finally, the XML Schema upon which these generated Java classes should be based is provided as the final argument to the xjc binding compiler.

Note that if the specified schema cannot be found by the xjc binding compiler command, we will see an error message that says something like, "the system cannot find the file specified: unknown location." If no XML Schema file is provided at all, a message stating something like "grammar is not specified" will be seen.

The following table contains the output of the JAXB 1.0 RI and JAXB 2.0 RI binding compiler commands when they are run without the -verbose option or -suppress option set. The output indicates which files are generated by the JAXB RI xjc binder compiler and, as is obvious in this table, there are many more files generated by JAXB 1.0 RI than by JAXB 2.0 RI. The bottom of the table shows the difference net size of all generated files and the difference in size of files generated is even more dramatic than the difference in number of files generated.

  JAXB 1.0 RI (1.0.6) JAXB 2.0 RI (in JDK 1.6)

xjc Generated Files for orm_1_0.xsd file.

(all files created in sub-directory structure specified with -p option)

parsing a schema...

compiling a schema...

impl\AssociationOverrideImpl.java

impl\AttributeOverrideImpl.java

impl\AttributesImpl.java

impl\BasicImpl.java

impl\CascadeTypeImpl.java

impl\ColumnImpl.java

impl\ColumnResultImpl.java

impl\DiscriminatorColumnImpl.java

impl\EmbeddableAttributesImpl.java

impl\EmbeddableImpl.java

impl\EmbeddedIdImpl.java

impl\EmbeddedImpl.java

impl\EmptyTypeImpl.java

impl\EntityImpl.java

impl\EntityListenerImpl.java

impl\EntityListenersImpl.java

impl\EntityMappingsImpl.java

impl\EntityMappingsTypeImpl.java

impl\EntityResultImpl.java

impl\FieldResultImpl.java

impl\GeneratedValueImpl.java

impl\IdClassImpl.java

impl\IdImpl.java

impl\InheritanceImpl.java

impl\JAXBVersion.java

impl\JoinColumnImpl.java

impl\JoinTableImpl.java

impl\LobImpl.java

impl\ManyToManyImpl.java

impl\ManyToOneImpl.java

impl\MapKeyImpl.java

impl\MappedSuperclassImpl.java

impl\NamedNativeQueryImpl.java

impl\NamedQueryImpl.java

impl\OneToManyImpl.java

impl\OneToOneImpl.java

impl\PersistenceUnitDefaultsImpl.java

impl\PersistenceUnitMetadataImpl.java

impl\PostLoadImpl.java

impl\PostPersistImpl.java

impl\PostRemoveImpl.java

impl\PostUpdateImpl.java

impl\PrePersistImpl.java

impl\PreRemoveImpl.java

impl\PreUpdateImpl.java

impl\PrimaryKeyJoinColumnImpl.java

impl\QueryHintImpl.java

impl\SecondaryTableImpl.java

impl\SequenceGeneratorImpl.java

impl\SqlResultSetMappingImpl.java

impl\TableGeneratorImpl.java

impl\TableImpl.java

impl\TransientImpl.java

impl\UniqueConstraintImpl.java

impl\VersionImpl.java

impl\runtime\ValidatorImpl.java

impl\runtime\Util.java

impl\runtime\UnmarshallableObject.java

impl\runtime\ValidatingUnmarshaller.java

impl\runtime\XMLSerializable.java

impl\runtime\InterningUnmarshallerHandler.java

impl\runtime\SAXUnmarshallerHandler.java

impl\runtime\IdentityHashSet.java

impl\runtime\ContentHandlerAdaptor.java

impl\runtime\UnmarshallerImpl.java

impl\runtime\ValidatableObject.java

impl\runtime\GrammarInfo.java

impl\runtime\UnmarshallingEventHandlerAdaptor.java

impl\runtime\PrefixCallback.java

impl\runtime\UnmarshallingEventHandler.java

impl\runtime\SAXUnmarshallerHandlerImpl.java

impl\runtime\DefaultJAXBContextImpl.java

impl\runtime\Discarder.java

impl\runtime\ValidationContext.java

impl\runtime\GrammarInfoImpl.java

impl\runtime\NamespaceContext2.java

impl\runtime\AbstractUnmarshallingEventHandlerImpl.java

impl\runtime\XMLSerializer.java

impl\runtime\ErrorHandlerAdaptor.java

impl\runtime\GrammarInfoFacade.java

impl\runtime\MSVValidator.java

impl\runtime\MarshallerImpl.java

impl\runtime\UnmarshallingContext.java

impl\runtime\SAXMarshaller.java

impl\runtime\NamespaceContextImpl.java

AssociationOverride.java

AttributeOverride.java

Attributes.java

Basic.java

CascadeType.java

Column.java

ColumnResult.java

DiscriminatorColumn.java

Embeddable.java

EmbeddableAttributes.java

Embedded.java

EmbeddedId.java

EmptyType.java

Entity.java

EntityListener.java

EntityListeners.java

EntityMappings.java

EntityMappingsType.java

EntityResult.java

FieldResult.java

GeneratedValue.java

Id.java

IdClass.java

Inheritance.java

JoinColumn.java

JoinTable.java

Lob.java

ManyToMany.java

ManyToOne.java

MapKey.java

MappedSuperclass.java

NamedNativeQuery.java

NamedQuery.java

ObjectFactory.java

OneToMany.java

OneToOne.java

PersistenceUnitDefaults.java

PersistenceUnitMetadata.java

PostLoad.java

PostPersist.java

PostRemove.java

PostUpdate.java

PrePersist.java

PreRemove.java

PreUpdate.java

PrimaryKeyJoinColumn.java

QueryHint.java

SecondaryTable.java

SequenceGenerator.java

SqlResultSetMapping.java

Table.java

TableGenerator.java

Transient.java

UniqueConstraint.java

Version.java

bgm.ser

jaxb.properties

parsing a schema...

compiling a schema...

Embeddable.java

EmbeddableAttributes.java

Embedded.java

EmbeddedId.java

EmptyType.java

Entity.java

EntityListener.java

EntityListeners.java

EntityMappings.java

EntityResult.java

EnumType.java

FetchType.java

FieldResult.java

GeneratedValue.java

GenerationType.java

Id.java

IdClass.java

Inheritance.java

InheritanceType.java

JoinColumn.java

JoinTable.java

Lob.java

ManyToMany.java

ManyToOne.java

MapKey.java

MappedSuperclass.java

NamedNativeQuery.java

NamedQuery.java

ObjectFactory.java

OneToMany.java

OneToOne.java

PersistenceUnitDefaults.java

PersistenceUnitMetadata.java

PostLoad.java

PostPersist.java

PostRemove.java

PostUpdate.java

PrePersist.java

PreRemove.java

PreUpdate.java

PrimaryKeyJoinColumn.java

QueryHint.java

SecondaryTable.java

SequenceGenerator.java

SqlResultSetMapping.java

Table.java

TableGenerator.java

TemporalType.java

Transient.java

UniqueConstraint.java

Version.java

package-info.java

Total Generated Files

142

(57 at root level of package,

55 in "impl" subdirectory,

30 in impl/runtime subdirectory)

62

Total Size (All Files)

2.22 MB

279 KB

The JAXB 2.0 RI generated classes are far fewer in number and their total file size is an order of magnitude smaller than the JAXB 1.0 RI generated classes for the same XML Schema. Note that all generated classes are actually written to appropriate subdirectory structure matching the package structure provided with the -p option (marx\jpa\persistence\jaxb\). I removed these package-matching subdirectory structures from the output shown in the table above to conserve space.

The generated Java classes are created with the appropriate subdirectory structure (based on the -p option or by XSD default it -p is not specified) to match the package name underneath the directory specified with the -d option. At this point, these Java classes can be opened in a favorite IDE or text editor to see what the JAXB RI xjc compiler has generated.

JAXB Runtime

The xjc binding compiler allowed us to create Java classes that can be manipulated for reading and writing XML that is compliant with the orm_1_0.xsd XML Schema file without having to directly work with the XML. Our application only needs to write (not read) XML, so we will only be marshalling the JAXB-based objects to XML. Had a requirement existed for us to read XML, we would also have needed to unmarshal XML into Java objects instantiated from the JAXB-generated classes.

JAXB implementations provide runtime libraries that are used when marshalling JAXB object data into XML or when unmarshalling XML data into JAXB objects. Because these are now available implicitly with Java SE 6, no extra JAR need be made available on the classpath if the reference implementation is used to generate the JAXB-generated classes.

Marshalling JAXB Java Objects Data to XML

The next code listing, Listing 4, demonstrates the marshalling of JAXB Java objects' data to XML. The code listing also makes use of the Filer provided by the AbstractProcessor our annotations processor extended. The Filer is used to actually write the JAXB marshaled data to a specific XML file called orm.xml.

Listing 4

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;

   /**
    * Write out XML file that is consistent with JPA in-source annotations found
    * in the JPA-based code.
    * 
    * This method only has a few lines of code, but they are all significant.
    * The example here demonstrates writing of an XML file using JAXB
    * marshalling.  It also demonstrates use of the parent class
    * (AbstractProcessor class) and access to that parent class' handle to a
    * ProcessingEnvironment (processingEnv).  Through access to this
    * ProcessingEnvironment, the code gains access to a Filer that it uses
    * to write out the JAXB-generated XML file to the file system.
    */
   private void writeMappingXmlFile()
   {
      processingEnv.getMessager().printMessage(
         Diagnostic.Kind.NOTE,
         "Entered writeMappingXmlFile() method..." );

      try
      {
         JavaFileManager.Location location = StandardLocation.CLASS_OUTPUT;

         /*
            A few interesting observations for Filer.createResource()
            1) Files created with this method are not available for further
               annotation processing.  This is okay in this context because
               we are writing out an XML file and would have no reason to
               attempt to process that XML for Java annotations.
            2) Limitation of using Filer here is that the supported standard
               locations may not really be where we wish to write this XML
               file.  If so, using a different mechanism for writing a file
               might be preferred because the main benefit of Filer is using it
               in conjunction with the annotation processor and we don't need
               that benefit in this case.
            3) The second argument to createResource() is for package name.
               This XML file we are writing is really not a source or class
               file and packaging concepts do not apply.  Therefore, as
               recommended in the Javadoc for this method, we are passing the
               empty string to that argument.
            4) The final argument is the name of the actual file to which the
               XML will be written.  We only need one file to be specified here.
            5) Filer.createResource uses "new" Java support for "..." (ellipses
               and variable arguments - Java 5)
         */
         FileObject fo =
            processingEnv.getFiler().createResource(location, "", "orm.xml");
         OutputStream os = fo.openOutputStream();
      
         this.marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 
                                     Boolean.TRUE);
         this.marshaller.marshal(this.em, os /* System.err */);
         
         os.close();
      }
      catch (PropertyException propEx)
      {
         processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            "PropertyException trying to write XML mapping file:"
               + propEx.getMessage() );
      }
      catch (JAXBException jaxbEx)
      {
         processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            "JAXBException trying to write XML mapping file:"
               + jaxbEx.getMessage() );
      }
      catch (FilerException filerEx)  // must appear before parent IOException
      {
         processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            "Problem with Processing Environment Filer: "
               + filerEx.getMessage() );
      }
      catch (IOException ioEx)
      {
         processingEnv.getMessager().printMessage(
            Diagnostic.Kind.ERROR,
            "Problem opening file to write ORM XML."
               + ioEx.getMessage() );
      }
   }


Comments in the code listing above call out several interesting things about that code. In our discussion regarding JAXB marshalling of Java objects to XML, two lines were of particular interest:

         this.marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, 
                                     Boolean.TRUE);
         this.marshaller.marshal(this.em, os /* System.err */);


The JAXB_FORMATTED_OUTPUT setting instructed the JAXB runtime marshaller to write the XML with "nice" formatting and indentation. In the second line shown immediately above, the marshaled XML is written to the output stream obtained from the Filer. As the commented out code in that line indicates, it could have easily been written directly to System.err (or System.out) to see the output XML on a console or have it redirected to a file.

The marshaller used in Listing 4 was previously set up in this class as shown in Listing 5:

Listing 5

         ClassLoader cl = Class.forName("marx.jpa.persistence.jaxb.ObjectFactory").getClassLoader();
         jc = JAXBContext.newInstance("marx.jpa.persistence.jaxb", cl);
         this.marshaller = jc.createMarshaller();
         this.of = new ObjectFactory();
         this.em = of.createEntityMappings();


The code in Listing 5 gets a new instance of JAXBContext and uses that instance to acquire the Marshaller used in Listing 4.

Listing 5 is also of interest because it shows how the root element of the orm.xml file is created by calling the ObjectFactory's createEntityMappings() method. The ObjectFactory was generated by the xjc binding compiler along with the Java classes that directly map to the XML structure. The ObjectFactory is used, as shown in Listing 5, to gain access to objects that will map to XML that is written out when marshaled.

Additional Information on JAXB

An excellent overview of JAXB 2.0 is available in "The Java EE 5 Tutorial". While this tutorial is focused on Java EE (Enterprise Edition) rather than Java SE (Standard Edition), most of the principles covered regarding JAXB are pertinent to both EE and SE. The "Unofficial JAXB Guide" also provides useful tips related to working with JAXB.

Running the JPA Annotations Processor

Because Java SE 6 includes annotation processing built into the javac command, running our newly created JPA annotations processor is fairly straightforward. This article earlier listed the options that javac supports for processing annotations.

To run our annotation processor over Java entity classes with JPA annotations, the following is used:

javac -processor marx.apt.jpa.AnnotationEntityProcessor
   -proc:only
   -processorpath C:\otnJava6\classes;C:\TopLink_11.1.1.0_070502_preview\lib\java\api\persistence.jar
   -cp C:\TopLink_11.1.1.0_070502_preview\lib\java\api\persistence.jar
   -AxmlOverrideAnnotations=true
   -AuseUpperCaseColumnNames=true
   -Xlint:unchecked
   *.java


To make it easier to read the options used above, the different arguments to javac are on different lines. The -process option is used to specify the fully scoped name of our annotation processing class (marx.apt.jpa.AnnotationEntityProcessor). The -proc:only option is used to instruct javac to only perform the annotation processing in this case and not actually compile the Java classes over which it is processing annotations. In the example above, the javac command was run in the directory in which the Java JPA annotation-decorated classes reside. This meant that only *.java needed to be specified for javac to know which classes to process for their annotations with the specified annotation processor.

The above example execution of the annotation processor using javac can be modified if additional or different information is desired. For example, adding the option -J"-verbose:class" to the javac call allows the newly launched Java interpreter to report information about each Java class loaded during the annotations processing execution. Similarly, many of the other options normally associated with the java (application launcher) command can be used by the javac (Java compiler) command via the -J option to javac.

As described earlier in this article, we can also pass the -XprintProcessorInfo or -XprintRounds options to javac in conjunction with annotation processing to have additional details about the processing printed out. Turning up the verbosity of javac, of java, or of the annotation processor itself is especially useful in writing and debugging a new annotation processor.

Finally, the -A option can be used to pass the annotation processor specific options via the javac compiler command. For example, the JPA annotations processor being discussed in this article allows the user to specify that in-source code annotations should be completely ignored and only external XML mapping be used by adding -AxmlOverrideAnnotations=true to the javac line shown above to run the processor. This option is probably more likely to be used by annotation processor providers for provider-specific functionality, but it makes a convenient method for passing arguments to our custom built processor classes.

One interesting side note here is that Java SE 6 supports wildcard annotation in classpath specifications for multiple JARs within the same directory. In the classpath used above, the JARs were all in different locations anyway, so it was just as easy to call them out individually. However, the classpath JAR wildcard can save much time and maintenance on scripts if many JARs are needed from the same directory.

The Results

When the annotation processor is run over JPA-decorated classes, an orm.xml file is written that describes the same metadata as found in the original Java source code. The orm.xml file produced from running this annotation processor over a simple set of JPA-enabled entity classes is shown next (Listing 6).

Listing 6

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<entity-mappings xmlns="#" version="1.0">
    <persistence-unit-metadata>
        <xml-mapping-metadata-complete/>
    </persistence-unit-metadata>
    <named-native-query name="findAllMovies">
        <query>SELECT * FROM MOVIE</query>
    </named-native-query>
    <named-native-query name="oracle-getDbDate">
        <query>SELECT sysdate FROM dual</query>
    </named-native-query>
    <entity name="Director" class="marx.Director">
        <discriminator-value>DIRECTOR</discriminator-value>
    </entity>
    <entity name="Genre" class="marx.Genre">
        <attributes>
            <id name="label">
                <column name="LABEL"/>
            </id>
            <many-to-many target-entity="marx.Movie"
                          name="movies" mapped-by="genres"/>
        </attributes>
    </entity>
    <entity name="Movie" class="marx.Movie">
        <attributes>
            <id name="id">
                <column name="ID"/>
            </id>
            <many-to-many target-entity="marx.Genre" name="genres">
                <join-table name="genre_movie">
                    <join-column referenced-column-name="id" name="movie_id"/>
                    <inverse-join-column referenced-column-name="label"
                                         name="genre_label"/>
                </join-table>
            </many-to-many>
            <transient name="somethingNotPersisted"/>
        </attributes>
    </entity>
    <entity name="Person" class="marx.Person">
        <inheritance strategy="SINGLE_TABLE"/>
        <discriminator-column name="ROLE" length="31"
                              discriminator-type="STRING"/>
        <sequence-generator sequence-name="PERSON_SEQ"
                            name="PERSON_SEQ"
                            initial-value="1"
                            allocation-size="1"/>
        <attributes>
            <id name="id">
                <column name="ID"/>
                <generated-value strategy="SEQUENCE" generator="PERSON_SEQ"/>
            </id>
            <basic name="gender">
                <enumerated>STRING</enumerated>
            </basic>
        </attributes>
    </entity>
    <entity name="Rating" class="marx.Rating">
        <attributes>
            <id name="label">
                <column name="LABEL"/>
            </id>
        </attributes>
    </entity>
    <entity name="Studio" class="marx.Studio">
        <attributes>
            <id name="id">
                <column name="ID"/>
            </id>
        </attributes>
    </entity>
</entity-mappings>

There are several things of special interest to note about this XML generated by our annotation processor. The existence of the empty tag <persistence-unit-metadata/> indicates that all JPA-related in-source annotations should be ignored and that this XML file should be used for the metadata instead. This tag was written by our annotations processor because we provided it with the -AxmlOverrideAnnotations=true flag when we ran the processor. Because we used the -A option to specify this flag, it was available to our annotation processor through the ProcessingEnvironment inherited from AbstractProcessor.

This code listing also demonstrates another potential advantage of using annotations processing to write an orm.xml (or other mapping files) from in-source annotations. In this case, the annotations processor wrote all of the <named-native-query..> entries as direct sub-elements to the root <entity-mappings..> element. The XML Schema for JPA object-relational mapping allows these to be written either at this level or specifically to a given entity. However, because named native queries are scoped to the persistence unit rather than to an individual unit, it makes more sense to place them in the XML file so that this scope is obvious. While the Java code had these general named native queries listed in individual Java entity classes, our annotations processor moved them to a more sensible location within the generated XML file.

With our JPA application metadata in XML format, we can now enjoy several advantages over in-source annotations. If our deployers are different than our developers, they no longer need to make changes in the Java code and can instead make changes in the external XML file. Developers can still use in-source annotations during development, but deployers can convert those in-source annotations to XML with our new annotation processor and then deal only with the XML. Also, JPA best practices can be better enforced, such as of the example of placing native named queries in the most appropriate location with the ORM file.

Issues / Enhancements for Annotation Processor

The annotation processor discussed in this article is primarily meant to provide a concrete example of how Java SE 6 has made annotations processing and JAXB use much simpler than previously available. A secondary purpose of the article has been to introduce the concept of an annotation processor that converts in-source Java annotations to external XML configuration files.

The code introduced in conjunction with this article is limited in several ways. First, not all JPA-related annotations are handled. Second, not all possible values and attributes for a given annotation are necessarily handled even if portions of the annotation are handled. However, support for additional annotations and complete support for partially supported annotations can be easily added to this annotation processor following the same patterns already used repeatedly in the processor.

The annotations processor described in this article only covers JPA annotations related to object-relational mapping (typically in an orm.xml file if specified externally). Another possible JPA external XML configuration file is the persistence.xml file (required for Java SE JPA-based applications). While the annotation processor proposed and partially constructed in this article does not cover the persistence.xml file, Sahoo has a blog on the subject of using J2SE 5's apt tool in conjunction with JAXB 2.0 to write a persistence.xml file based on in-source annotations. (Read Sahoo's blog entry on this topic.) Because apt is still available with Java SE 6, this processor should still work with a Java SE 6 installation.

Conclusion

Java SE 6 brings to the Java developer greater convenience in both JAXB development and annotations processing development. Java 6's out of box support for JAXB 2.0 and for sophisticated annotation processing make the task of converting in-source JPA annotations to external XML configuration files much simpler than in the past. This article has considered how these Java SE 6 improvements to JAXB availability and annotations processing can be leveraged to improve the ability to deploy JPA-based applications from either source code annotations or external XML configuration files.


Dustin Marx is a senior software engineer and architect at Raytheon Company.