JPA Criteria API

Introduction

This tutorial will show you how to use JPA(Java Persistence API) Criteria API Queries for selecting data from database. The Java Persistence API (JPA) is a Java specification for accessing, persisting, and managing data between Java objects / classes and a relational database.

The Criteria API is used to define queries for entities and their persistent state by creating query-defining objects. Criteria API queries are written using Java programming language APIs, are typesafe, and are portable. Such queries work regardless of the underlying data store.

The major advantage of Criteria API is that errors can be detected earlier during the compile time. String-based JPQL(Java Persistence Query Language) queries and JPA criteria based queries are same in performance and efficiency.

Related Posts:

Steps to create Criteria Query

  1. EntityManager instance is used to create a CriteriaBuilder object.
  2. CriteriaQuery instance is used to create a query object from CriteriaBuilder object. The attributes of this object will be modified with the details of the query.
  3. CriteriaQuery.form() method is called to set the Root of the query.
  4. CriteriaQuery.select() is called to set the result list type for a particular POJO class mapped to a database table.
  5. TypedQuery<T> instance is used to prepare a query for execution and specifying the type of the query result.
  6. getResultList() method is used on the TypedQuery<T> object for executing a query to return the result list of a collection of entities.

The following example shows you how to use JPA Criteria Queries in Java based application.

Prerequisites

Java at least 8, Gradle 6.5.1, Maven 3.6.3, Hibernate Entity Manager 5.4.21, MySQL Connector 8.0.21, JAXB API 2.4.0 (if JDK > 8)

Project Setup

You can create gradle or maven based Java project in your favorite IDE or tool.

Use below build.gradle script if you are using gradle based project:

plugins {
    id 'java-library'
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    jcenter()
}

dependencies {
	implementation 'org.hibernate:hibernate-entitymanager:5.4.21.Final'
	
	implementation 'mysql:mysql-connector-java:8.0.21'
}

Use below pom.xml file if you create maven based project:

<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.roytuts</groupId>
	<artifactId>jpa-criteria-api</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>at least 1.8</java.version>
		<hibernate.version>5.4.21.Final</hibernate.version>
		<mysqlconnector.version>8.0.21</mysqlconnector.version>
	</properties>
	
	<dependencies>
		<!-- JPA -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		
		<!-- mysql java connector -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysqlconnector.version}</version>
		</dependency>
	</dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Persistence Unit

Create an XML file persistence.xml under src/main/resources/META-INF folder.

In the following file I have added MySQL database configurations, any table/class mapping Java file, persistence unit name, any other extra configuration.

<?xml version="1.0" encoding="UTF-8"?>
<persistence 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_2.xsd"
	version="2.2">

	<persistence-unit name="userPersistanceUnit"
		transaction-type="RESOURCE_LOCAL">
		<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
		
		<class>com.roytuts.jpa.criteria.api.entity.User</class>

		<properties>
			<property name="javax.persistence.jdbc.driver"
				value="com.mysql.cj.jdbc.Driver" />
			<property name="javax.persistence.jdbc.url"
				value="jdbc:mysql://localhost:3306/roytuts" />
			<property name="javax.persistence.jdbc.user" value="root" />
			<property name="javax.persistence.jdbc.password"
				value="root" />
			<property name="dialect"
				value="org.hibernate.dialect.MySQLDialect" />

			<property
				name="hibernate.transaction.flush_before_completion" value="true" />

			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
		</properties>
	</persistence-unit>
</persistence>

In the above XML file, I have declared transaction-type as RESOURCE_LOCAL. There are two types of Transaction management types supported in JPA.

  • RESOURCE LOCAL Transactions
  • JTA or GLOBAL Transactions

Resource local transactions refer to the native transactions of the JDBC Driver whereas JTA transactions refer to the transactions of the JEE server. A Resource Local transaction involves a single transactional resource, for example a JDBC Connection. Whenever you need two or more resources (for example, a JMS Connection and a JDBC Connection)  within a single transaction, you use  JTA Transaction.

Container Managed Entity Managers always use JTA transactions as the container takes care of transaction life cycle management and spawning the transaction across multiple transactional resources. Application Managed Entity Managers can use either Resource Local Transactions or JTA transactions.

Normally in JTA or global transaction, a third party transaction monitor enlists the different transactional resources within a transaction, prepares them for a commit and finally commits the transaction. This process of first preparing the resources for transaction(by doing a dry run) and then committing(or rolling back) is called a 2 phase commit.

Entity Class

Create below entity class User under src/main/java folder. This class maps the fields or attributes to the table columns.

package com.roytuts.jpa.criteria.api.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id")
	private int id;

	@Column(name = "name")
	private String name;

	@Column(name = "phone")
	private String phone;

	@Column(name = "email")
	private String email;

	@Column(name = "address")
	private String address;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getAddress() {
		return address;
	}

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

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", phone=" + phone + ", email=" + email + ", address=" + address
				+ "]";
	}

}

Persistence Manager

Now create an enum PersistenceManager for creating EntityManagerFactory and EntityManager.

I am using enum to create singleton instance for this Persistence Manager.

package com.roytuts.jpa.criteria.api.persistence.manager;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public enum PersistenceManager {

	_INSTANCE;

	private EntityManagerFactory emf;

	private PersistenceManager() {
		emf = Persistence.createEntityManagerFactory("userPersistanceUnit");
	}

	public EntityManager getEntityManager() {
		return emf.createEntityManager();
	}

	public void close() {
		emf.close();
	}

}

Main Class

Create a class with main method to test the application.

package com.roytuts.jpa.criteria.api.app;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import com.roytuts.jpa.criteria.api.entity.User;
import com.roytuts.jpa.criteria.api.persistence.manager.PersistenceManager;

public class JpaCriteriaApp {

	public static void main(String[] args) {

		EntityManager em = PersistenceManager._INSTANCE.getEntityManager();

		// create user for id 35
		try {
			em.getTransaction().begin();

			User u = new User();
			u.setName("Liton");
			u.setEmail("liton.sarkar@email.com");
			u.setPhone("1407874760");
			u.setAddress("Mars");

			em.persist(u);

			em.getTransaction().commit();

			System.out.println("User " + u + " successfully saved");
		} catch (Exception e) {
			em.getTransaction().rollback();
			e.printStackTrace();
		}

		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();
		Root<User> from = criteriaQuery.from(User.class);

		// select all records
		System.out.println("Select all records");
		System.out.println("------------------");
		System.out.println();

		CriteriaQuery<Object> select = criteriaQuery.select(from);
		TypedQuery<Object> typedQuery = em.createQuery(select);
		List<Object> resultlist = typedQuery.getResultList();

		for (Object o : resultlist) {
			User u = (User) o;
			System.out.println(u);
		}

		// Ordering the records
		System.out.println();
		System.out.println("Select all records in ascending order");
		System.out.println("-------------------------------------");
		System.out.println();

		CriteriaQuery<Object> select1 = criteriaQuery.select(from);
		select1.orderBy(criteriaBuilder.asc(from.get("name")));

		TypedQuery<Object> typedQuery1 = em.createQuery(select);
		List<Object> resultlist1 = typedQuery1.getResultList();

		for (Object o : resultlist1) {
			User u = (User) o;
			System.out.println(u);
		}

		// update user for id 35
		try {
			User user = em.find(User.class, 35);
			user.setName("Liton Sarkar");
			user.setEmail("liton.sarkar@email.com");
			user.setPhone("140787476990");
			user.setAddress("Earth");

			em.persist(user);

			em.getTransaction().commit();

			System.out.println("User " + user + " successfully updated");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			em.close();
			PersistenceManager._INSTANCE.close();
		}

	}

}

MySQL Table

Create table user under roytuts database in MySQL server and dump some data into it.

CREATE DATABASE IF NOT EXISTS `roytuts`;
USE `roytuts`;

CREATE TABLE IF NOT EXISTS `user` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  `phone` int unsigned NOT NULL,
  `address` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `user` (`id`, `name`, `email`, `phone`, `address`) VALUES
	(1, 'Soumitra', 'soumitra@roytuts.com', 43256789, 'Earth'),
	(2, 'Rahul', 'rahul@roytuts.com', 65465363, 'Mars');

Testing the Application

Run the above main class, you will see below output in the console.

Hibernate: 
    insert 
    into
        user
        (address, email, name, phone) 
    values
        (?, ?, ?, ?)
User User [id=35, name=Liton, phone=1407874760, email=liton.sarkar@email.com, address=Mars] successfully saved
Select all records
------------------

Hibernate: 
    select
        user0_.id as id1_0_,
        user0_.address as address2_0_,
        user0_.email as email3_0_,
        user0_.name as name4_0_,
        user0_.phone as phone5_0_ 
    from
        user user0_
User [id=1, name=Soumitra, phone=43256789, email=soumitra@roytuts.com, address=Earth]
User [id=2, name=Rahul, phone=65465363, email=rahul@roytuts.com, address=Mars]
User [id=35, name=Liton, phone=1407874760, email=liton.sarkar@email.com, address=Mars]

Select all records in ascending order
-------------------------------------

Hibernate: 
    select
        user0_.id as id1_0_,
        user0_.address as address2_0_,
        user0_.email as email3_0_,
        user0_.name as name4_0_,
        user0_.phone as phone5_0_ 
    from
        user user0_ 
    order by
        user0_.name asc
User [id=35, name=Liton, phone=1407874760, email=liton.sarkar@email.com, address=Mars]
User [id=2, name=Rahul, phone=65465363, email=rahul@roytuts.com, address=Mars]
User [id=1, name=Soumitra, phone=43256789, email=soumitra@roytuts.com, address=Earth]

Source Code

Download

Thanks for reading.

Leave a Comment