JPA Buddy
announcement - icon

JPA is huge! It covers nearly every aspect of communication between relational databases and the Java application and is deeply integrated into all major frameworks.

If you're using IntelliJ, JPA Buddy is super helpful. The plugin gently guides you through the subtleties of the most popular JPA implementations, visually reminds you of JPA features, generates code that follows best practices, and integrates intelligent inspections to improve your existing persistence code.

More concretely, it provides powerful tooling to generate Spring Data JPA repositories and methods, Flyway Versioned Migrations, Liquibase Differential Changelogs, DDL and SQL statements, DTO objects, and MapStruct interfaces.

Oh, and it actually generates JPA entities from an existing database and gradually update the data model as the database evolves! Yeah.

>> Become a lot more productive with JPA Buddy

Persistence top

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
Authors Top

If you have a few years of experience in the Java ecosystem, and you’d like to share that with the community, have a look at our Contribution Guidelines.

1. Overview

In this tutorial, we'll see how to use JPA and Hibernate queries and the difference between Criteria, JPQL, and HQL queries. Criteria queries enable the user to write queries without using raw SQL. Along with Criteria queries, we'll explore writing Hibernate Named Queries and how to use the @Query annotation in Spring Data JPA.

Before we dive in, we should note that the Hibernate Criteria API has been deprecated since Hibernate 5.2. Therefore, we'll be using the JPA Criteria API in our examples, as it's the new and preferred tool for writing Criteria queries. So, from here on, we'll refer to it simply as the Criteria API.

2. Criteria Queries

The Criteria API helps in building the Criteria query object by applying different filters and logical conditions on top of it. This is an alternate way to manipulate objects and return the desired data from an RDBMS table.

The createCriteria() method from the Hibernate Session returns the persistence object instance for running a criteria query in the application. Simply put, the Criteria API builds up a criteria query that applies different filters and logical conditions.

2.1. Maven Dependencies

Let's grab the latest version of the reference JPA dependency – which implements JPA in Hibernate – and add it to our pom.xml:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.3.2.Final</version>
</dependency>

2.2. Using the Criteria Queries and Expressions

As per the user's conditions, CriteriaBuilder controls the query results. It uses the where() method from CriteriaQuery, which provides CriteriaBuilder expressions.

Let's look at the entity we'll be using in this article:

public class Employee {

    private Integer id;
    private String name;
    private Long salary;

   // standard getters and setters
}

Let's look at a simple criteria query that will retrieve all the rows of “Employee” from the database:

Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cr = cb.createQuery(Employee.class);
Root<Employee> root = cr.from(Employee.class);
cr.select(root);

Query<Employee> query = session.createQuery(cr);
List<Employee> results = query.getResultList();
session.close();
return results;

The above Criteria query returns a set of all the items. Let's see how it happens:

  1. The SessionFactory object creates the Session instance
  2. The Session returns an instance of CriteriaBuilder using the getCriteriaBuilder() method
  3. The CriteriaBuilder uses the createQuery() method. This creates the CriteriaQuery() object that further returns the Query instance
  4. In the end, we call the getResult() method to obtain the query object that holds the results

Let's look at another expression from CriteriaQuery:

cr.select(root).where(cb.gt(root.get("salary"), 50000));

For its result, the above query returns the set of employees having a salary of more than 50000.

3. JPQL

JPQL stands for Java Persistence Query Language. Spring Data provides multiple ways to create and execute a query, and JPQL is one of these. It defines queries using the @Query annotation in Spring to execute both JPQL and native SQL queries. The query definition uses JPQL by default.

We use the @Query annotation to define a SQL query in Spring. Any query defined by the @Query annotation has higher priority over named queries, which are annotated with @NamedQuery.

3.1. Using JPQL Queries

Let's build a dynamic query using JPQL:

@Query(value = "SELECT e FROM Employee e")
List<Employee> findAllEmployees(Sort sort);

With JPQL queries that have argument parameters, Spring Data passes the method arguments to the query in the same order as the method declaration. Let's look at a couple of examples that pass method arguments into the query:

@Query("SELECT e FROM Employee e WHERE e.salary = ?1")
Employee findAllEmployeesWithSalary(Long salary);
@Query("SELECT e FROM Employee e WHERE e.name = ?1 and e.salary = ?2")
Employee findEmployeeByNameAndSalary(String name, Long salary);

For the latter query above, the name method argument is passed as the query parameter with respect to index 1, and the salary argument is passed as the index 2 query parameter.

3.2. Using the JPQL Native Queries

We can execute these SQL queries directly in our databases using native queries, which refer to real databases and table objects. We need to set the value of the nativeQuery attribute to true for defining a native SQL query. The native SQL query will be defined in the value attribute of the annotation.

Let's see a native query that shows an indexed parameter to be passed as an argument for the query:

@Query(
  value = "SELECT * FROM Employee e WHERE e.salary = ?1",
  nativeQuery = true)
Employee findEmployeeBySalaryNative(Long salary);

Using Named Parameters makes the query easier to read and less error-prone in the case of refactoring. Let's see an illustration of a simple Named Query in JPQL and native format:

@Query("SELECT e FROM Employee e WHERE e.name = :name and e.salary = :salary")
Employee findEmployeeByNameAndSalaryNamedParameters(
  @Param("name") String name,
  @Param("salary") Long salary);

The method parameters are passed to the query using named parameters. We can define named queries by using the @Param annotation inside the repository method declaration. As a result, the @Param annotation must have a string value that matches the corresponding JPQL or SQL query name.

@Query(value = "SELECT * FROM Employee e WHERE e.name = :name and e.salary = :salary", 
  nativeQuery = true) 
Employee findUserByNameAndSalaryNamedParamsNative( 
  @Param("name") String name, 
  @Param("salary") Long salary);

4. HQL

HQL stands for Hibernate Query Language. It's an object-oriented language similar to SQL that we can use to query our database. However, the main disadvantage is the code's unreadability. We can define our queries as Named Queries to place them in the actual code that accesses the database.

4.1. Using Hibernate Named Query

A Named Query defines a query with a predefined, unchangeable query string. These queries are fail-fast since they're validated during the creation of the session factory. Let's define a Named Query using the org.hibernate.annotations.NamedQuery annotation:

@NamedQuery(name = "Employee_FindByEmployeeId",
 query = "from Employee where id = :id")

Each @NamedQuery annotation attaches itself to one entity class only. We can use the @NamedQueries annotation to group more than one named query for an entity:

@NamedQueries({
    @NamedQuery(name = "Employee_findByEmployeeId", 
      query = "from Employee where id = :id"),
    @NamedQuery(name = "Employee_findAllByEmployeeSalary", 
      query = "from Employee where salary = :salary")
})

4.2. Stored Procedures and Expressions

In conclusion, we can use the @NamedNativeQuery annotation for storing the procedures and functions:

@NamedNativeQuery(
  name = "Employee_FindByEmployeeId", 
  query = "select * from employee emp where id=:id", 
  resultClass = Employee.class)

5. Advantages of Criteria Queries Over HQL and JPQL Queries

The main advantage of Criteria Queries over HQL is the nice, clean, object-oriented API. As a result, we can detect errors in Criteria API during the compile time.

In addition, JPQL queries and Criteria queries have the same performance and efficiency.

Criteria queries are more flexible and provide better support for writing dynamic queries as compared to HQL and JPQL.

But HQL and JPQL provide native query support that isn't possible with the Criteria queries. This is one of the disadvantages of the Criteria query.

We can easily write complex joins using JPQL native queries, whereas it gets difficult to manage while applying the same with Criteria API.

6. Conclusion

In this article, we mainly looked at the basics of Criteria queries, JPQL queries, and the HQL queries in Hibernate and JPA. In addition, we learned how to use these queries and the advantages and disadvantages of each approach.

As always, the complete code examples used in this article can be found over on GitHub and here.

Persistence bottom
Get started with Spring Data JPA through the reference Learn Spring Data JPA course: >> CHECK OUT THE COURSE
Persistence footer banner
Comments are closed on this article!