Spring Data Repositories: Query by Specification

Working with Spring and Spring Data repositories I got the need to have flexible queries based on some of the property of an entity class that should be put in OR in the query if they were present. Instead of creating a query for each combination of properties, I used one of the facilities provided by Spring-Data: the query by Specification.

Before going into the details some background information. What I am going to show is an example based on the JpaRepository using Hibernate as persistency layer.

A full working example can be found here: query-by-spec

The trick is in the use of the Specification interface in combination with a JpaSpecificationExecutor.
Let's start...

First we define an entity with some properties:

@Entity
@Table(name = "person")
public class Person {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

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

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

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

@Column(name = "age")
private Integer age;

        ....

}

Then we define our repository:

public interface PersonRepository extends JpaRepository<Person, Long>, JpaSpecificationExecutor<Person> {

}

As you can see we have extended another interface the JpaSpecificationExecutor. This interface defines the methods to perform the search via a Specification class.

What we have to do now is to define our specification that will return the Predicate containing the constraints for the query (in the example the PersonSpecification is performing the query select * from person where name = ? or (surname = ? and age = ?) ):

public class PersonSpecification implements Specification<Person> {

    private Person filter;

    public PersonSpecification(Person filter) {
        super();
        this.filter = filter;
    }

    public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq,
            CriteriaBuilder cb) {

        Predicate p = cb.disjunction();

        if (filter.getName() != null) {
            p.getExpressions()
                    .add(cb.equal(root.get("name"), filter.getName()));
        }

        if (filter.getSurname() != null && filter.getAge() != null) {
            p.getExpressions().add(
                    cb.and(cb.equal(root.get("surname"), filter.getSurname()),
                            cb.equal(root.get("age"), filter.getAge())));
        }

        return p;

    }

}

Now it is time to use it. The following code fragment shows how to use the Specification we just created: 

...
        Person filter = new Person();
        filter.setName("Mario");
        filter.setSurname("Verdi");
        filter.setAge(25);

        Specification<Person> spec = new PersonSpecification(filter);

        List<Person> result = repository.findAll(spec);

...


Comments

Popular posts from this blog

WebSphere stdout/stderr logging redirect

JSF2 View Scope with Spring Core