Although Spring Data provides easy ways to obtain database results, there are times when a programmatic approach is required. In this article, we will see how to construct Criteria Queries with Spring Data using Spring Boot.
For complex queries with conditions on multiple fields, using Criteria is the most suitable approach.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
With this Spring Boot starter, we will add all the necessary JPA dependencies.
Why use Criteria Queries with Spring Data
Criteria Queries provide a flexible and programmatic approach for constructing complex queries in Spring Data. They allow you to define query conditions dynamically, handle multiple criteria, and build queries based on runtime conditions. This gives you more control and flexibility compared to static queries defined in annotations or methods. Additionally, Criteria Queries seamlessly integrate with other Spring Data features and repositories, allowing you to leverage existing functionality while still benefiting from the power of dynamic queries.
Creating Criteria Queries by Extending Repository
In many cases, we need to create queries where the automatic queries provided by Spring Data are not sufficient, such as adding multiple conditions. For such cases, we can use the Criteria API.
To continue using JpaRepository and have access to default methods, we will create an interface that extends JpaRepository and the interface where we define custom methods.
EntityManager will be necessary to use the Criteria API.
interface ResourceRepository<T> { List<T> findByNameAndSurname(String name, String title); } interface UserRepository extends JpaRepository<User, Long>, ResourceRepository<User> { } @Repository class UserRepositoryImpl implements UserRepository { @PersistenceContext EntityManager em; @Override public List<User> findUsersByNameAndSurname(String name, String surname) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<User> cq = cb.createQuery(User.class); Root<User> user = cq.from(User.class); List<Predicate> predicates = new ArrayList<>(); if (name != null) { predicates.add(cb.equal(user.get("name"), name)); } if (surname != null) { predicates.add(cb.equal(user.get("surname"),surname)); } cq.where(predicates.toArray(new Predicate[0])); return em.createQuery(cq).getResultList(); }
When declaring ResourceRepository as a dependency, Spring will search for the implementation and use it to call the methods we have defined within the interface. This way, we will have our new method along with the methods provided by JPA.
Queries with JPA specification
Spring Data contains the interface org.springframework.data.jpa.domain.Specification, which provides several methods for encapsulating a predicate.
The best way to understand how specifications work is through an example:
Let’s create a class that returns a Specification:
public class specificationExample { public static Specification<UserDbo> getName(String name) { return (Specification<UserDbo>) (user, query, cb) -> { cb.equal(user.get("name"), name); } } }
With the previous example, it would be equivalent to performing an SQL query where the WHERE clause is user.name = ‘name’. However, the power of using specifications lies in the ability to add conditions as shown in the following example:
public class specificationExample { public static Specification<UserDbo> getName(String name, String surname) { return (Specification<UserDbo>) (user, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); predicates.add(cb.equal(user.get("name"), name); if (null != surname) { predicates.add(cb.equal(user.get("surname"), surname); } return cb.and(predicates.toArray(Predicate[]::new)); } } }
In the previous example, we added two predicates to a list with conditions, including a programmatic check for non-null last name.
To use these methods, we can create a bean of the specificationExample class and call the method, or extend the org.springframework.data.jpa.repository.JpaSpecificationExecutor<T> repository. Don’t forget to also extend JpaRepository to make use of the default methods provided by Spring Data.
Conclusion
In this article about Criteria Queries with Spring Data, we have seen how to use the Criteria API by extending Repository. Furthemore we have seen how add new methods that are executed with Predicate, as well as using Specification.
If you want to practice, you can perform some tests with H2
If you need more information, you can leave us a comment or send an email to refactorizando.web@gmail.com You can also contact us through our social media channels on Facebook or twitter and we will be happy to assist you!!