Criteria API

Learn how to build queries programmatically using Hibernate's Criteria API. This approach is useful for constructing dynamic queries based on runtime conditions. We'll cover the basics of building criteria queries and executing them.


Executing Criteria Queries in Hibernate

Explanation: Executing Criteria Queries

Once you have built your CriteriaQuery using the Criteria API, the next step is to execute it to retrieve the desired data from your database. This is done through the EntityManager, which provides methods for creating a TypedQuery from the CriteriaQuery and subsequently executing the query.

Here's a breakdown of the process:

  1. Create a TypedQuery: The EntityManager's createQuery(CriteriaQuery<T>) method transforms the CriteriaQuery into a TypedQuery<T>. The generic type T represents the type of the result you expect (e.g., your entity class or the type of the projected value). This TypedQuery object is then used for executing the query.
  2. Execute the Query: The TypedQuery interface provides several methods for executing the query:
    • getResultList(): Returns a List<T> containing all the entities or projected values that match the criteria. This is the most common method for retrieving multiple results.
    • getSingleResult(): Returns a single result of type T. This method throws a NoResultException if no results are found, and a NonUniqueResultException if more than one result is found. Use with caution and appropriate exception handling when you're sure the query will return exactly one result.
    • getSingleResultOrNull(): (Available in recent Hibernate versions with appropriate configuration). Returns a single result of type T or `null` if no results are found. Throws a NonUniqueResultException if more than one result is found. A safer alternative to `getSingleResult()` when expecting zero or one result.
  3. Handle the Results: The returned List (from getResultList()) will contain either instances of your entity class or the projected values, depending on how you defined your CriteriaQuery. You can then iterate through this list to process the retrieved data. If using `getSingleResult()` or `getSingleResultOrNull()`, you can directly access the returned object.

Demonstration: Executing CriteriaQuery with EntityManager

This example demonstrates how to execute a CriteriaQuery using the EntityManager and retrieve the results as a list of entities. It also illustrates projecting a specific attribute.

Example 1: Retrieving a List of Entities

 import jakarta.persistence.*;
        import jakarta.persistence.criteria.*;
        import java.util.List;

        // Assume you have an entity class named 'Product'

        public class CriteriaQueryExample {

            public static void main(String[] args) {
                EntityManagerFactory emf = Persistence.createEntityManagerFactory("yourPersistenceUnit"); // Replace with your persistence unit name
                EntityManager em = emf.createEntityManager();

                try {
                    // 1. Create CriteriaBuilder
                    CriteriaBuilder cb = em.getCriteriaBuilder();

                    // 2. Create CriteriaQuery
                    CriteriaQuery<Product> cq = cb.createQuery(Product.class);

                    // 3. Create Root (FROM clause)
                    Root<Product> root = cq.from(Product.class);

                    // (Optional) 4. Add Predicates (WHERE clause) - Example: price > 100
                    cq.where(cb.gt(root.get("price"), 100.0));


                    // 5. Create TypedQuery
                    TypedQuery<Product> query = em.createQuery(cq);

                    // 6. Execute Query and Retrieve Results
                    List<Product> products = query.getResultList();

                    // 7. Process Results
                    for (Product product : products) {
                        System.out.println("Product Name: " + product.getName() + ", Price: " + product.getPrice());
                    }

                } finally {
                    em.close();
                    emf.close();
                }
            }
        } 

Explanation:

  • The code first obtains an EntityManager.
  • It then constructs a CriteriaQuery to select all Product entities where the price is greater than 100.
  • A TypedQuery is created from the CriteriaQuery using em.createQuery(cq).
  • The getResultList() method is called on the TypedQuery to retrieve a list of Product entities matching the criteria.
  • Finally, the code iterates through the list and prints the name and price of each product.

Example 2: Retrieving a List of Projected Values (Product Names)

 import jakarta.persistence.*;
        import jakarta.persistence.criteria.*;
        import java.util.List;

        public class CriteriaQueryProjectionExample {

            public static void main(String[] args) {
                EntityManagerFactory emf = Persistence.createEntityManagerFactory("yourPersistenceUnit"); // Replace with your persistence unit name
                EntityManager em = emf.createEntityManager();

                try {
                    CriteriaBuilder cb = em.getCriteriaBuilder();
                    CriteriaQuery<String> cq = cb.createQuery(String.class); // Note the String.class here

                    Root<Product> root = cq.from(Product.class);

                    //Select only the product name
                    cq.select(root.get("name")); // Projecting the 'name' attribute

                    TypedQuery<String> query = em.createQuery(cq);
                    List<String> productNames = query.getResultList();

                    for (String name : productNames) {
                        System.out.println("Product Name: " + name);
                    }

                } finally {
                    em.close();
                    emf.close();
                }
            }
        } 

Explanation:

  • This example is similar to the previous one, but instead of retrieving the entire Product entity, it retrieves only the name attribute (which is assumed to be a String).
  • Note the use of CriteriaQuery<String> and TypedQuery<String> to indicate that the query will return a list of strings.
  • The cq.select(root.get("name")) line specifies that we are projecting the name attribute of the Product entity.
  • The result is a List<String> containing the names of all products.

Example 3: Using Single Result

 import jakarta.persistence.*;
import jakarta.persistence.criteria.*;

public class CriteriaQuerySingleResultExample {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("yourPersistenceUnit"); // Replace with your persistence unit name
        EntityManager em = emf.createEntityManager();

        try {
            CriteriaBuilder cb = em.getCriteriaBuilder();
            CriteriaQuery<Product> cq = cb.createQuery(Product.class);
            Root<Product> root = cq.from(Product.class);

            // Find a product with a specific ID (assuming 'id' is a field in Product)
            Predicate idPredicate = cb.equal(root.get("id"), 1L); // Assuming ID is 1L (Long)
            cq.where(idPredicate);

            TypedQuery<Product> query = em.createQuery(cq);

            try {
                Product product = query.getSingleResult();
                System.out.println("Found product: " + product.getName());
            } catch (NoResultException e) {
                System.out.println("No product found with ID 1.");
            } catch (NonUniqueResultException e) {
                System.out.println("Multiple products found with ID 1.  This should not happen if ID is a primary key.");
            }

        } finally {
            em.close();
            emf.close();
        }
    }
} 

Explanation:

  • This code demonstrates the use of getSingleResult().
  • It constructs a query to find a `Product` with a specific ID.
  • A `try-catch` block handles potential exceptions:
    • `NoResultException`: Thrown if no product is found with the specified ID.
    • `NonUniqueResultException`: Thrown if multiple products are found with the same ID (which would indicate a data integrity issue if `id` is the primary key).