RSS

Criteria JPA 2.0 and database metamodel auto generation with Maven

07 Jul

In the previous post JPA basic example with EntityManager , Spring and Maven we use JPA through an entityManager instance.

In this post we are going how we can create a database metamodel in our project (At this point let’s be clear, I’m not talking about annotated Java classes with @Entity, but Java classes to prepare queries from them).For that purpose we are going to use the JPA 2.0 Criteria API.

In JPA we can use JQL to create our own SQL queries, but we can also use the Criteria API. This one has some advantages like allowing us to know if our query is correct in compilation time, add dinamic constraints, pagination, add filters, … . We are going to compare two queries that give us the same result, for that we use JPQL and Criteria.

Our project configuration (applicationContext.xml y persistence.xml) is the same we used within JPA basic example with EntityManager , Spring and Maven. What we need is modifying our Dao implemenation and the pom file.
Firtsly, we have to modify the pom.xml file. To generate the database metamodel we need our Java entities and some Maven plugins.

  • maven-compiler-plugin. It allows us compile our project. To generate our metamodel we need to add a compiler argument called org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor. This option will generate some special Java classes from our entities. These generated classes will be named with the entity name followed to an underscore mark.
  • maven-processor-plugin. It copies to src/main/java the generated classes by the other plugin.

Our pom.xml file:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.hopcroft.examples.spring</groupId>
	<artifactId>jpaModelGenerator</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<pluginRepositories>
		<pluginRepository>
			<id>maven-annotation</id>
			<url>http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo</url>
		</pluginRepository>
	</pluginRepositories>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
					<compilerArguments>
						<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
					</compilerArguments>
				</configuration>
				<version>2.3.2</version>
			</plugin>
			<plugin>
				<groupId>org.bsc.maven</groupId>
				<artifactId>maven-processor-plugin</artifactId>
				<version>2.0.2</version>
				<executions>
					<execution>
						<id>process</id>
						<goals>
							<goal>process</goal>
						</goals>
						<phase>generate-sources</phase>
						<configuration>
							<outputDirectory>src/main/java</outputDirectory>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>javax.security</groupId>
			<artifactId>jacc</artifactId>
			<version>1.0</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.10</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>3.0.5.RELEASE</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>3.5.6-Final</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.ow2.easybeans</groupId>
			<artifactId>easybeans-uberjar-hibernate</artifactId>
			<version>1.2.1</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-jpamodelgen</artifactId>
			<version>1.0.0.Final</version>
			<type>jar</type>
			<scope>compile</scope>
		</dependency>
	</dependencies>
</project>

After executing these plugins, we get the follow Car_ class:

package com.hopcroft.examples.domain;

import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@StaticMetamodel(Car.class)
public abstract class Car_ {

	public static volatile SingularAttribute<Car, Long> id;
	public static volatile SingularAttribute<Car, String> model;
	public static volatile SingularAttribute<Car, Long> price;
	public static volatile SingularAttribute<Car, String> company;

}

Now we already have a Car_ to create queries through the JPA 2.0 Criteria API. We are going to compare the two ways of queries we mentioned previously. The CarDaoImpl content is next.

package com.hopcroft.examples.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;

import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.hopcroft.examples.domain.Car;
import com.hopcroft.examples.domain.Car_;
@Repository
public class CarDaoImpl implements CarDao {
	
	protected EntityManager entityManager;
	
	public EntityManager getEntityManager() {
		return entityManager;
	}
	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public List<Car> getCars() throws DataAccessException {
		Query query = getEntityManager().createQuery("select c from Car c");
		List<Car> resultList = query.getResultList();
		return resultList;
	}
	@Transactional
	public Car getCar(Long carId) throws DataAccessException {
		return getEntityManager().find(Car.class, carId);
	}

	@Transactional
	public List<Car> getCarByCompany(String company) {
		CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
		CriteriaQuery<Car> criteriaQuery = criteriaBuilder.createQuery(Car.class);
		Root<Car> root = criteriaQuery.from(Car.class);
		Path<String> company_ = root.get(Car_.company);
		criteriaQuery.where(criteriaBuilder.equal(company_, company));
		return getEntityManager().createQuery(criteriaQuery).getResultList();
		
	}
	@SuppressWarnings("unchecked")
	@Transactional
	public List<Car> getCarByCompanyNamedQuery(String company) {
		Query query = getEntityManager().createNamedQuery("Car.getCarsByCompany");
		query.setParameter("company",company);
		return query.getResultList();
	}

}

There are two new methods called getCarByCompany and getCarByCompanyNamedQuery. The second one is the common way to create a query with JQL. We have a NamedQuery within our Car entity.In that particular query we need to set the parameter company.NamedQuery aspect is:

@NamedQuery(name="Car.getCarsByCompany",query="select car from Car car where company = :company")

It is quite easy to use a NamedQuery. On the other hand we could write a bad former SQL query and notice that right after we execute it. To solve this we can use the getCarByCompany method that uses JPA Criteria.
The first line of this method get a criteriaBuilder to create a query later. This criteriaBuilder will select Car objects(line 2).In line 3 we can asociate the from field of a query with the Car entity. In line 4 we say that we are going to need the company_ attribute from Car_. Later we are going to use the company_ attribute within the where clause.
The advantage of criteriaBuilder is that allows us to know in compilation time if the query is well formed. At the same time we could add filter, pagination, … . The disadvantage is we can add lots of verbosity to a SQL query.

If we want to use our metamodel we have two run the Maven lifecycle twice. The first time we are going to generate our metamodel class. After this we can run our application using the metamodel recently created.

To write this post I have based on an example from this book Spring Persistence with Hibernate de Paul Tepper Fischer and Brian D. Murphy.

 
7 comentarios

Publicado por en 7 julio, 2011 en J2EE, JPA

 

Etiquetas: , , , , , ,

7 Respuestas a “Criteria JPA 2.0 and database metamodel auto generation with Maven

  1. admin

    15 julio, 2011 at 10:28 pm

    Hi,

    I am editor of http://www.javainterview.co.in. I need Java technology writers for my site. Would you be interested in writing articles for us.

    If interested please mail us

    Thanks,

     
  2. Luciano Pontes

    31 octubre, 2013 at 2:27 pm

    Parabéns pelo seu artigo meu amigo! No meu caso, são geradas as classes coretamente mas retorna o seguinte erro:
    «Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.0:compile (default-compile) on project e-maps: Compilation failure: Compilation failure:
    [ERROR] /home/luciano/dev/emaps_dev/workspace/e-maps/target/generated-sources/annotations/br/embrapa/cnpmf/emaps/entity/Dog_.java:[7,17] duplicate class: br.embrapa.cnpmf.emaps.entity.Prioridade_
    [ERROR] /home/luciano/dev/emaps_dev/workspace/e-maps/target/generated-sources/annotations/br/embrapa/cnpmf/emaps/entity/Cat_.java:[7,17] duplicate class: br.embrapa.cnpmf.emaps.entity.LinhaAcao_
    »
    Você tem idéia de como corrigir este problema?

     
    • hop2croft

      31 octubre, 2013 at 3:39 pm

      Hi Luciano,

      it seems like you have some duplicated classes within your classpath.

       
      • Daniel Medina

        11 noviembre, 2013 at 1:38 pm

        Same problem, with this configuration it works like a charm:

        maven-compiler-plugin
        3.1

        1.6
        1.6
        -proc:none

        It seems that metamodel generator was probably executing twice.

         
      • hop2croft

        11 noviembre, 2013 at 1:50 pm

        thank you Daniel!

         

Deja un comentario