Help

Steve mentioned in his Hibernate 3.5.0 Beta2 blog entry briefly the Hibernate Static Metamodel Generator Annotation Processor. Time to have a closer look what this annotation processor actually does.

Typesafe Criteria API

Let's start with a look at one of the new features of JPA 2 - the type-safe Criteria API. The key to this new API are metamodel classes generated for their corresponding managed entities. These metamodel classes allow the construction of object based queries. Lets look at an example:

@Entity public class Order {
    @Id 
    Integer id;
    @ManyToOne 
    Customer customer;
    @OneToMany 
    Set<Item> items;
    BigDecimal totalCost;
    ...
}

In the above example we have a managed entity Order with an id, a reference to a customer, a list of order items and the total costs. The corresponding metamodel class looks like this:

@StaticMetamodel(Order.class)
public class Order_ {
    public static volatile SingularAttribute<Order, Integer> id;
    public static volatile SingularAttribute<Order, Customer> customer;
    public static volatile SetAttribute<Order, Item> items;
    public static volatile SingularAttribute<Order, BigDecimal> totalCost;
}

Having such a metamodel class allows for queries of the form:

CriteriaBuilder cb = ordersEntityManager.getCriteriaBuilder();
CriteriaQuery<Order> cq = cb.createQuery(Order.class);
SetJoin<Order, Item> itemNode = cq.from(Order.class)
	.join(Order_.orderItems);
cq.where( cb.equal(itemNode.get(Item_.id), 5 ) ) 
	.distinct(true);

I don't want to go any deeper into the actual Criteria API here. Refer to JSR-317 for more information. Instead I would like to answer the question on how to generate this metamodel classes? Of course a developer could manually maintain them, but that would hardly be productive. A better solution is to utilize the annotation processing facilities officially available since Java 6. In Java 6 javac behaves analogously to the apt command in Java 5. This is specified by JSR-269.

Hibernate Static Metamodel Generator

Hibernate Static Metamodel Generator is an implementation of such an annotation processor with the task of creating the static metamodel classes for entities (as seen in the example above). In the following I will show how to integrate the annotation processor into your build environment. The good news is that in most cases the annotation processor will automatically run provided the annotation processor jar is on the classpath. This happens due to Java's Service Provider contract and the fact the the Hibernate Static Metamodel Generator jar files contains the file javax.annotation.processing.Processor in the META-INF/services listing org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor as annotation processor.

Maven

There are several ways of running the annotation processor as part of a Maven build. It will automatically run if you are using a JDK 6 compiler and the annotation processor jar is on the classpath. In case you have more than one annotation processors on your classpath you can explicitly pass the processor option to the compiler plugin:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
        <compilerArguments>
        	<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
        </compilerArguments>
    </configuration>
</plugin>

The maven-compiler-plugin approach has the disadvantage that the maven compiler plugin does currently not allow to specify multiple compiler arguments (MCOMPILER-62) and that messages from the Messenger API are suppressed (MCOMPILER-66). A better approach is to disable annotation processing for the compiler plugin:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
        <compilerArgument>-proc:none</compilerArgument>
    </configuration>
</plugin>

and use the maven-annotation-plugin for annotation processing (you will need the following additional maven repositories - maven-annotation-plugin and jfrog):

<plugin>
    <groupId>org.bsc.maven</groupId>
    <artifactId>maven-processor-plugin</artifactId>
    <executions>
        <execution>
            <id>process</id>
            <goals>
                <goal>process</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
                <!-- source output directory -->
                <outputDirectory>target/metamodel</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <version>1.3</version>
    <executions>
        <execution>
            <id>add-source</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>add-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>target/metamodel</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

Last, but not least one could also use the antrun-plugin to run the annotation processor. This approach can be seen here.

Ant

As mentioned before, the annotation processor will run automatically in a Java 6 environment. In case you want configure the processor explicitly or disable it you can use the compilerarg option of the Javac Task. The supported command javac line options are listed here.

Eclipse

Of course you also want to have annotation processing available in your favorite IDE. In Eclipse (at least since the latest Galileo release) exists an additional configuration section under Java Compiler where you can configure all kinds of aspects of annotation processing. Just check the Enable annotation processing option, configure the directory for the generated sources and finally add the Hibernate Static Metamodel Generator and JPA 2 jar files to the factory path.

Idea

Idea does not have an explicit configuration section for annotation processing yet. Instead do you have to rely on the fact that the processing will automatically occur in the case the Hibernate Static Metamodel Generator is on the classpath and you are using a Java 6 compiler. You can, however, specify additional compiler command line options under the Compiler - Java Compiler section.

Feeback

Hopefully this gets you started with the Hibernate Static Metamodel Generator. It would be great to get some feedback. The source code is currently part of the Hibernate svn repository. The trunk is http://anonsvn.jboss.org/repos/hibernate/jpamodelgen/trunk/. The latest SNAPSHOT of the jar file can be retrieved from the JBoss snapshot repository. Bugs should be reported in METAGEN.

Enjoy!

14 comments:
 
10. Nov 2009, 01:15 CET | Link

Great to see the 10 minute prototype I handed to you grew into something working ;)

 

--max

 
10. Nov 2009, 14:45 CET | Link
Yann Cébron

Actually IntelliJ IDEA 9 (currently in Beta) will have dedicated annotation processor settings

 
10. Nov 2009, 21:50 CET | Link

I try the processor plugin but i got always a error:

[INFO] Internal error in the plugin manager executing goal 'org.bsc.maven:maven-processor-plugin:1.2-SNAPSHOT:process': Unable to load the mojo 'org.bsc.maven:maven-processor-plugin:1.2-SNAPSHOT:process' in the plugin 'org.bsc.maven:maven-processor-plugin'. A required class is missing: javax/tools/JavaFileManager

then I add the dependency

<dependencies>
          <dependency>
            <groupId>org.jvnet.sorcerer</groupId>
            <artifactId>sorcerer-javac</artifactId>
            <version>0.8</version>
          </dependency>
        </dependencies>

but then the error will be

[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Trace
org.apache.maven.lifecycle.LifecycleExecutionException
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:703)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:540)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:519)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:371)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:332)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:181)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:356)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:137)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
        at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:41)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
        at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
        at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
        at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.apache.maven.plugin.MojoExecutionException
        at org.bsc.maven.plugin.processor.AbstractAnnotationProcessorMojo.execute(AbstractAnnotationProcessorMojo.java:111)
        at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:483)
        at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:678)
        ... 17 more

is there a complete howto ?

Regards Andreas

 
10. Nov 2009, 22:05 CET | Link

Are you using a JDK 6? A required class is missing: javax/tools/JavaFileManager seems to indicate a wrong JDK version. Java 6 is a prerequisite.

 
19. Nov 2009, 22:52 CET | Link
greg

http://blogs.jetbrains.com/idea/2009/11/userfriendly-annotation-processing-support-jpa-20-metamodel/

 
03. Dec 2009, 22:33 CET | Link

Congratulations! Great work.

I just cant find the jar file which contains the META-INF/services/javax.annotation.processing.Processor file. The hibernate 3.5.b2 distribution zip does not seem to have it. Where can I found it?

 
03. Dec 2009, 22:37 CET | Link

Ok, Just found the jar... it was the last link in this article: http://snapshots.jboss.org/maven2/org/hibernate/hibernate-jpamodelgen/1.0.0-SNAPSHOT/

Thanks! Worked like a charm in Eclipse.

 
08. Jan 2010, 23:10 CET | Link
One thing I don't get, is what if you want to do the same thing but want to get the items here instead of the orders.
 
04. Mar 2010, 04:36 CET | Link
jean-claude
I'm able to follow the instruction given on this webpage
http://relation.to/12805.lace

I've got the Hibernate annotation processor to generate my static metamodel class. "Item_"

However the eclipse compiler does not like these classes. "cannot resolve class"

If I turn off the annotation processor then all is ok. Seems like the fact that the processor is generating the code and compiling it at the same time causes issues?

I'm using JDK 1.6.03
Eclipse Galileo
Hibernate 3.5.0 CR1
jpamodelgen 1.0.0 CR1

Anyone else has this?
Thanks
jean-claude
 
11. Mar 2010, 22:32 CET | Link
Dirk Weil

I followed the instructions for including the processor in maven shown above. It works as expected with the exception that metamodel classes are generated not only for the classes of the project itself, but also for all @Entity, @Embeddable etc. of libraries which are used by the project:

Project class pkg1.A extends pkg2.B from some other library. Annotation processing not only generates pkg1.A but also pkg2.B.

Can you restrict annotation processing to some classes / packages?

Thanks in advance!

 
05. May 2010, 05:55 CET | Link
Thane

@jean-claude

I am having the exact same issue, only I'm using JDK 1.6.11. It appears that while the metamodel source is being built, the class files aren't being compiled. If I go into the properties of the metamodel classes and deselect derived, and then do a clean, it will compile them. I'm guessing this is a bug in Eclipse.

 
10. Jun 2010, 12:23 CET | Link
Rafal

Maven dependency, if you were looking for:

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-jpamodelgen</artifactId>
     <version>1.0.0.Final</version>
</dependency>
 
17. Aug 2010, 18:17 CET | Link

How can I use this to join a subclass as described in my post at My Link ?

 
09. Jan 2014, 17:28 CET | Link

I have problem with maven compilation. When maven try to generate matemodels it crashes on this exception : javax.xml.stream.FactoryConfigurationError: Provider com.ctc.wstx.stax.WstxInputFactory not found

I have no idea where is the problem. I found maybe woodstox artifact is missing. I tried to add lot of dependencies which contains WstxInputFactory but nothing changed. Could somebody help me please?

Thank you