Spring Data JPA + Querydsl Integration

As you’ve get introduced at Spring Data – JPA, about Inquiring entities using all of those benefits that provided by Repository. So, either you are using those default methods such as findOne() and findAll() or you are using those User-Defined ones that depends on the Query Derivation concept, you are almost getting headache, because of those many details that you’ve to learn for achieving what you want to do.

Spring Data has been coming with ready made integration with the Querydsl through its Repository. Querydsl is a framework which enables the construction of type-safe SQL-Like queries for multiple backends including JPA, MongoDB and SQL in Java.

Instead of writing queries as inline strings or externalizing them into XML files, they are constructed via a fluent API. What you have to do is by extending your custom Repository through extends QueryDslPredicateExecuter, this will pull methods like findAll(Predicate predicate) and findOne(Predicate predicate) into the API. We now have everything in place, so we can actually start using the generated classes. To generate your meta model through using of JPAAnnotationProcessor for being able to execute a predicate queries.

Configuring Querydsl

The configuration of Querydsl is so easier, cause there is no additional XML or annotations that should be added for your Entities. The configuration can be happened through the following given steps:

  • Configure your maven pom.xml file by including the required maven plugin. That plugin should be used for generating your Meta Model classes from your entities. That plugin will search about all of those annotated @Entity, @Embeddable or relationship between Entities and generates a list of Meta Model classes that located under your provided location as you’ll see.
  • Add the required libraries for Querydsl; querydsl has been coming with a lot of libraries for serving the different kinds of persistence stores. Libraries such as querydsl-core, querydsl-apt and querydsl-jpa are the required libraries, cause we are going to use the Querydsl with the JPA.
  • Makes an extension for your Repository by extending the QueryDslPredicateExecutor, that’s generic interface has given the developer ability to provide it the entity that it being processed in its query.

For more clarifications, find below all of these related files:

Maven pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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>net.javabeat.springdata</groupId>
	<artifactId>SpringData-JPA</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>

	<name>Spring Data</name>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
					<encoding>UTF-8</encoding>
				</configuration>
			</plugin>
			<plugin>
				<groupId>com.mysema.maven</groupId>
				<artifactId>apt-maven-plugin</artifactId>
				<version>1.1.1</version>
				<executions>
					<execution>
						<phase>generate-sources</phase>
						<goals>
							<goal>process</goal>
						</goals>
						<configuration>
							<!-- Specifies the directory in which the query types are generated -->
							<outputDirectory>target/generated-sources</outputDirectory>
							<!-- States that the APT code generator should look for JPA annotations -->
							<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<!-- Querydsl dependencies -->
		<dependency>
			<groupId>com.mysema.querydsl</groupId>
			<artifactId>querydsl-core</artifactId>
			<version>3.3.2</version>
		</dependency>
		<dependency>
			<groupId>com.mysema.querydsl</groupId>
			<artifactId>querydsl-apt</artifactId>
			<version>3.3.2</version>
		</dependency>
		<dependency>
			<groupId>com.mysema.querydsl</groupId>
			<artifactId>querydsl-jpa</artifactId>
			<version>3.3.2</version>
		</dependency>
		<!-- SLF4J dependency -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.6.1</version>
		</dependency>
		<!-- Dependencies for Eclipse JPA Persistence API -->
		<dependency>
			<groupId>org.eclipse.persistence</groupId>
			<artifactId>eclipselink</artifactId>
			<version>2.5.0-RC1</version>
			<exclusions>
				<exclusion>
					<groupId>org.eclipse.persistence</groupId>
					<artifactId>commonj.sdo</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- Spring Data Dependency -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>1.5.1.RELEASE</version>
		</dependency>
		<!-- Dependency for MySql Java connector -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.30</version>
		</dependency>

	</dependencies>
</project>

EmployeeRepository.java

package net.javabeat.springdata.repo;

import net.javabeat.springdata.jpa.data.Employee;

import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EmployeeRepository extends CrudRepository<Employee, Integer>,QueryDslPredicateExecutor<Employee>{
	public Employee findByEmployeeId(Integer id);
}

Generating Meta Model Classes

Generating Meta Model classes isn’t big issue, as you’ve got the pom.xml that’s contains the required libraries and plugin. Simple, you have to navigate into your project folder and execute the following command:

<strong>mvn generate-sources 

 

That command should invoke the Querydsl API plugin to be executed at the generated-sources maven phase. By executing that plugin the JPAAnnotationProcessor will do its discovering about all of those classes annotated using @Entity, @Embeddable or Relationships to another Entity to generate their Meta Model classes and put them at the same location that you’ve chosen via <outputDirectory/> Tag. Find below the generated Meta Model classes.

QEmployee.java

package net.javabeat.springdata.jpa.data;

import static com.mysema.query.types.PathMetadataFactory.*;

import com.mysema.query.types.path.*;

import com.mysema.query.types.PathMetadata;
import javax.annotation.Generated;
import com.mysema.query.types.Path;
import com.mysema.query.types.path.PathInits;

/**
 * QEmployee is a Querydsl query type for Employee
 */
@Generated("com.mysema.query.codegen.EntitySerializer")
public class QEmployee extends EntityPathBase<Employee> {

    private static final long serialVersionUID = 1711013517L;

    private static final PathInits INITS = PathInits.DIRECT2;

    public static final QEmployee employee = new QEmployee("employee");

    public final QAddress address;

    public final NumberPath<Integer> employeeId = createNumber("employeeId", Integer.class);

    public final StringPath employeeName = createString("employeeName");

    public QEmployee(String variable) {
        this(Employee.class, forVariable(variable), INITS);
    }

    public QEmployee(Path<? extends Employee> path) {
        this(path.getType(), path.getMetadata(), path.getMetadata().isRoot() ? INITS : PathInits.DEFAULT);
    }

    public QEmployee(PathMetadata<?> metadata) {
        this(metadata, metadata.isRoot() ? INITS : PathInits.DEFAULT);
    }

    public QEmployee(PathMetadata<?> metadata, PathInits inits) {
        this(Employee.class, metadata, inits);
    }

    public QEmployee(Class<? extends Employee> type, PathMetadata<?> metadata, PathInits inits) {
        super(type, metadata, inits);
        this.address = inits.isInitialized("address") ? new QAddress(forProperty("address"), inits.get("address")) : null;
    }
}

QAddress.java

package net.javabeat.springdata.jpa.data;

import static com.mysema.query.types.PathMetadataFactory.*;

import com.mysema.query.types.path.*;

import com.mysema.query.types.PathMetadata;
import javax.annotation.Generated;
import com.mysema.query.types.Path;
import com.mysema.query.types.path.PathInits;

/**
 * QAddress is a Querydsl query type for Address
 */
@Generated("com.mysema.query.codegen.EntitySerializer")
public class QAddress extends EntityPathBase<Address> {

    private static final long serialVersionUID = -715355083L;

    private static final PathInits INITS = PathInits.DIRECT2;

    public static final QAddress address = new QAddress("address");

    public final StringPath addressCity = createString("addressCity");

    public final StringPath addressCountry = createString("addressCountry");

    public final NumberPath<Integer> addressId = createNumber("addressId", Integer.class);

    public final QEmployee employee;

    public QAddress(String variable) {
        this(Address.class, forVariable(variable), INITS);
    }

    public QAddress(Path<? extends Address> path) {
        this(path.getType(), path.getMetadata(), path.getMetadata().isRoot() ? INITS : PathInits.DEFAULT);
    }

    public QAddress(PathMetadata<?> metadata) {
        this(metadata, metadata.isRoot() ? INITS : PathInits.DEFAULT);
    }

    public QAddress(PathMetadata<?> metadata, PathInits inits) {
        this(Address.class, metadata, inits);
    }

    public QAddress(Class<? extends Address> type, PathMetadata<?> metadata, PathInits inits) {
        super(type, metadata, inits);
        this.employee = inits.isInitialized("employee") ? new QEmployee(forProperty("employee"), inits.get("employee")) : null;
    }
}

Location of Meta Model Classes

The below figure shows you the generated Meta Model, once the mvn generate-sources get executed.

Generated MetaModel Querydsl

  • The generated Meta Model has been located at the same package, so copy and paste all of them at the under the same package hierarchy of your entities.

Predicate Builder Class

First we obtain a reference to the QEmployee meta model class and keep that inside the product property. We can now using this to navigate the generated path expressions to create predicates. So such these expressions are almost more over welcomed, either you are using employeeEquation.employeeId.eq(1) for get an Employee or you are using

employeeEquation.employeeName.contains(“Susa”) for get list of employees, you aren’t at all cases going beyond the concept of predicates. And as you’ve seen, the definition of the predicates is remarkably readable and concise. The built predicates can be recombined to construct higher level predicates and thus allow for querying flexibility without adding complexity. Look below at the Executable application that provides you some of used predicates among list of predicates that you’ve got provided.

Executabel.java

package net.javabeat.springdata.executable;

import java.util.List;

import net.javabeat.springdata.beans.RegistrationBean;
import net.javabeat.springdata.jpa.data.Address;
import net.javabeat.springdata.jpa.data.Employee;
import net.javabeat.springdata.jpa.data.QEmployee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mysema.query.types.Predicate;

public class Executable {
	public static RegistrationBean registrationBean;
	public static ClassPathXmlApplicationContext context;

	static {
		// Acquire Context
		context = new ClassPathXmlApplicationContext("SpringContext.xml");
		// Get RegistrationBean That Defined
		registrationBean = (RegistrationBean)context.getBean("registrationBean");
	}

	public static void main(String [] args){
		// Inquiry Employees
		inquiryByEmployeeId(1);
		inquiryByEmployeeName("Susa Hurbert");
		inquiryByNameContaining("Susa");
	}

	public static void inquiryByEmployeeId(Integer id){
		QEmployee employeeEquation = QEmployee.employee;
		Predicate emp = employeeEquation.employeeId.eq(id);
		Employee employee = registrationBean.getEmployeeReposigtory().findOne(emp);
		System.out.println("By Employee ID :: Employee Inquired :: "+employee.getEmployeeId()+" :: "+employee.getEmployeeName());
	}

	public static void inquiryByEmployeeName(String name){
		QEmployee employeeEquation = QEmployee.employee;
		Predicate emp = employeeEquation.employeeName.eq(name);
		Employee employee = registrationBean.getEmployeeReposigtory().findOne(emp);
		System.out.println("By Employee Name :: Employee Inquired :: "+employee.getEmployeeId()+" :: "+employee.getEmployeeName());
	}

	public static void inquiryByNameContaining(String name){
		QEmployee employeeEquation = QEmployee.employee;
		Predicate emp = employeeEquation.employeeName.contains(name);
		Iterable<Employee> employees = registrationBean.getEmployeeReposigtory().findAll(emp);
		for(Employee e : employees){
			System.out.println("By Employee Name Containing :: Employee Inquired :: "+e.getEmployeeId()+" :: "+e.getEmployeeName());
		}

	}

	public static void createEmployee(){

		// Create Employee
		Employee employee = new Employee();
		employee.setEmployeeId(2);
		employee.setEmployeeName("Susa Geraurd");
		// Create Address
		Address address = new Address();
		address.setAddressId(1);
		address.setAddressCountry("United Kingdom");
		address.setAddressCity("London");
		address.setEmployee(employee);
		employee.setAddress(address);
		// Persist Using EmployeeRepository
		registrationBean.getEmployeeReposigtory().save(employee);
	}
}

Where the execution result is:

	bind => [1]
By Employee ID :: Employee Inquired :: 1 :: Susa Hurbert
	bind => [Susa Hurbert]
By Employee Name :: Employee Inquired :: 1 :: Susa Hurbert
	bind => [%Susa%, !]
By Employee Name Containing :: Employee Inquired :: 1 :: Susa Hurbert
By Employee Name Containing :: Employee Inquired :: 2 :: Susa Geraurd

Spring Configurations

Look below at the Spring Context file that shouldn’t provide any additional statements for making Querydsl supportable.

SpringContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

	<!-- For consider the using of annotations foe defining Spring Bean -->
	<context:annotation-config />

	<!-- For defining Spring Bean -->
	<context:component-scan base-package="net.javabeat.springdata.beans" />

	<!-- For bootstrapping the Spring Repository -->
	<jpa:repositories base-package="net.javabeat.springdata.repo" />

	<!-- Necessary to get the entity manager injected into the factory bean -->
	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<!-- Define EclipseLink JPA Vendor Adapter -->
	<bean id="jpaVendorAdapter"
		class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
		<property name="databasePlatform"
			value="org.eclipse.persistence.platform.database.MySQLPlatform" />
		<property name="generateDdl" value="false" />
		<property name="showSql" value="true" />
	</bean>

	<!-- Entity Manager Factory -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="SpringData"></property>
		<property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
	</bean>

	<!-- Transaction Manager -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<!-- Enable Transactional Manner -->
	<tx:annotation-driven transaction-manager="transactionManager" />

</beans>

JPA Entity Configurations

Over that, the Entities provide no additional changes for making the concept of Querydsl reliable.

Employee.java

package net.javabeat.springdata.jpa.data;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

@Entity
public class Employee {

	@Id
	private Integer employeeId;

	@Basic(optional = false)
	private String employeeName;

	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "AddressId")
	private Address address;

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public Integer getEmployeeId() {
		return employeeId;
	}

	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}

	public String getEmployeeName() {
		return employeeName;
	}

	public void setEmployeeName(String employeeName) {
		this.employeeName = employeeName;
	}

	public boolean equals(Object obj){
		if(obj instanceof Employee){
			Employee emp = (Employee)obj;
			if(this.employeeId == emp.employeeId){
				return true;
			}
		}
		return false;
	}

	public int hashCode(){
		return this.employeeId;
	}
}

Address.java

package net.javabeat.springdata.jpa.data;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;

@Entity(name = "address")
public class Address {
	@Id
	private int addressId;
	private String addressCountry;
	private String addressCity;

	@OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
	private Employee employee;

	public Employee getEmployee() {
		return employee;
	}

	public void setEmployee(Employee employee) {
		this.employee = employee;
	}

	public int getAddressId() {
		return addressId;
	}

	public void setAddressId(int addressId) {
		this.addressId = addressId;
	}

	public String getAddressCountry() {
		return addressCountry;
	}

	public void setAddressCountry(String addressCountry) {
		this.addressCountry = addressCountry;
	}

	public String getAddressCity() {
		return addressCity;
	}

	public void setAddressCity(String addressCity) {
		this.addressCity = addressCity;
	}
}

Persistence Context

Also, the persistence context hasn’t changed when it comes using of Querydsl.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
	xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
	<persistence-unit name="SpringData"
		transaction-type="RESOURCE_LOCAL">
		<class>net.javabeat.springdata.jpa.data.Employee</class>
		<class>net.javabeat.springdata.jpa.data.Address</class>
		<properties>
			<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/JavaBeat" />
			<property name="javax.persistence.jdbc.user" value="root" />
			<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
			<property name="javax.persistence.jdbc.password" value="root" />
			<property name="eclipselink.logging.level" value="OFF" />
		</properties>
	</persistence-unit>
</persistence>

Summary

Spring Data provides the system architecture an awesome integration with Querydsl through its Repository.  By extending the QueryDslPredicateExecuter, your entities have their MetaModel, and the developer has the ability to inquiry your persistence stores in an easy, modern and free of complexity manner. whatever the queries you are tried to execute, Querydsl provides you the proper predicate for doing that.

Comments

comments

About Amr Mohammed

Speak Your Mind

*