Generating JPA Canonical Model Metadata In Maven

One of the nice features of JPA 2.x is type-safe queries using the Criteria API. Instead of referencing a string or building an endless bounty of static parameters JPA implementations provide a means to generate them. For example:

EntityManager em = ...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> at = cq.from(Person.class);
cq.select(at);
// Person_.java is the generated canonical so we don't have to use
// at.get("firstName") and at.get("lastName")
cq.where(cb.and(cb.equal(at.get(Person_.firstName), "John"),
		cb.equal(at.get(Person_.lastName), "Doe")));
TypedQuery<AuditReg> q = em.createQuery(cq);
q.getResultList();

Each JPA implementation has it’s own means for generating canonical model metadata, but most of them provide a javax.annotation.processing.Processor implementation. I’ve seen a few posts demonstrating how to integrate this feature into Maven builds. Most of them use some external Maven plugin to accomplish this, but I found it better to just use the maven-compiler-plugin within a Maven profile. A Maven profile allows us to decouple the projects compilation from the canonical generation so that we aren’t generating canonical metadata every time the project is compiled. This approach makes sense seeing how infrequently the metadata needs to be generated (not to mention it could potentially cause problems using Maven IDE integration tools). It also prevents issues with dependency resolutions. Here is an example using eclipselink:

<build>
	<plugins>
		<plugin>
			<!-- source/target java version needs to be at least 1.6 -->
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.0</version>
			<configuration>
				<source>1.7</source>
				<target>1.7</target>
			</configuration>
		</plugin>
	</plugins>
</build>
<profiles>
	<profile>
		<id>jpa-canonical-gen</id>
		<build>
			<plugins>
				<plugin>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.0</version>
					<configuration>
						<proc combine.self="override">only</proc>
						<!-- compilerArguments can be removed if there is only one -->
						<!-- PU in the project or eclipselink.canonicalmodel.subpackage -->
						<!-- has been defined for each additional PU added in 
						<!-- persistence.xml for more details see 
						<!-- https://bugs.eclipse.org/bugs/show_bug.cgi?id=308713 -->
						<compilerArguments combine.children="append">
							<Aeclipselink.persistenceunits>
								myPuName
							</Aeclipselink.persistenceunits>
						</compilerArguments>
						<!-- edit the unique directory path to match where the -->
						<!-- JPA entities exist -->
						<includes combine.self="override">
							<include>**/entity/jpa/*.java</include>
						</includes>
						<generatedSourcesDirectory>
							${project.build.sourceDirectory}
						</generatedSourcesDirectory>
						<annotationProcessors combine.self="override">
							<annotationProcessor>
								org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor
							</annotationProcessor>
						</annotationProcessors>
					</configuration>
				</plugin>
			</plugins>
		</build>
		<dependencies>
			<dependency>
				<groupId>org.eclipse.persistence</groupId>
				<artifactId>org.eclipse.persistence.jpa.modelgen</artifactId>
				<version>2.4.0</version>
			</dependency>
		</dependencies>
	</profile>
</profiles>
<dependencies>
	<dependency>
		<groupId>org.eclipse.persistence</groupId>
		<artifactId>org.eclipse.persistence.jpa</artifactId>
		<version>2.4.0</version>
		<scope>compile</scope>
	</dependency>
	<dependency>
		<groupId>org.eclipse.persistence</groupId>
		<artifactId>javax.persistence</artifactId>
		<version>2.0.0</version>
	</dependency>
</dependencies>

The above Maven POM snippet can be executed using the following Maven command and will generate the canonical metadata java source in the same package where the JPA entities exists:

mvn -P jpa-canonical-gen

The example can easily be adapted for other JPA implementations as well by replacing the appropriate project dependencies, profile dependencies (for the annotation processor), and the annotationProcessor with one of the following values: