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!
Great to see the 10 minute prototype I handed to you grew into something working ;)
--max
Actually IntelliJ IDEA 9 (currently in Beta) will have dedicated annotation processor settings
I try the processor plugin but i got always a error:
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 moreis there a complete ?
Regards Andreas
Are you using a JDK 6? seems to indicate a wrong JDK version. Java 6 is a prerequisite.
http://blogs.jetbrains.com/idea/2009/11/userfriendly-annotation-processing-support-jpa-20-metamodel/
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?
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.
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
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!
@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 , and then do a clean, it will compile them. I'm guessing this is a bug in Eclipse.
Maven dependency, if you were looking for:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>1.0.0.Final</version> </dependency>How can I use this to join a subclass as described in my post at My Link ?