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:
- Create a
TypedQuery
: TheEntityManager
'screateQuery(CriteriaQuery<T>)
method transforms theCriteriaQuery
into aTypedQuery<T>
. The generic typeT
represents the type of the result you expect (e.g., your entity class or the type of the projected value). ThisTypedQuery
object is then used for executing the query. - Execute the Query: The
TypedQuery
interface provides several methods for executing the query:getResultList()
: Returns aList<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 typeT
. This method throws aNoResultException
if no results are found, and aNonUniqueResultException
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 typeT
or `null` if no results are found. Throws aNonUniqueResultException
if more than one result is found. A safer alternative to `getSingleResult()` when expecting zero or one result.
- Handle the Results: The returned
List
(fromgetResultList()
) will contain either instances of your entity class or the projected values, depending on how you defined yourCriteriaQuery
. 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 allProduct
entities where the price is greater than 100. - A
TypedQuery
is created from theCriteriaQuery
usingem.createQuery(cq)
. - The
getResultList()
method is called on theTypedQuery
to retrieve a list ofProduct
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 thename
attribute (which is assumed to be aString
). - Note the use of
CriteriaQuery<String>
andTypedQuery<String>
to indicate that the query will return a list of strings. - The
cq.select(root.get("name"))
line specifies that we are projecting thename
attribute of theProduct
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).