opticyclic
opticyclic

Reputation: 8116

How Can I Conditionally Combine Predicates For A JPA Query?

Lets say I have a Book Entity like this:

@Entity
@Table(uniqueConstraints = {
        @UniqueConstraint(columnNames = {"title"})
})
class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    String title;
    String author;
    String description;
}

and a repository like this:

@Repository
public interface BookRepository extends JpaRepository<Book, Long>, JpaSpecificationExecutor<Book> {

    List<Book> findByAuthor(String author);
    Book  findByTitle(String title);
    List<Book> findByDescriptionContaining(String description);    
    static Specification<Book> hasTitle(String title) {
        return (book, cq, cb) -> cb.equal(book.get("title"), title);
    }   
    static Specification<Book> hasAuthor(String author) {
        return (book, cq, cb) -> cb.equal(book.get("author"), author);
    }   
    static Specification<Book> hasDescription(String description) {
        return (book, cq, cb) -> cb.like(book.get("description"), "%" + description + "%");
    }
}

I can then do a query like this:

repository.findAll(where(hasAuthor("Robert Ludlum")).and(hasTitle("The Bourne Identity")).and(hasDescription("also a film"))).

If I have this in a method with parameters, an empty or null value might be passed.
e.g. a REST API search endpoint that has optional parameters.

In that case I would only want to query by author repository.findAll(where(hasAuthor("Robert Ludlum"))) since adding the other predicates would return no results.

I want to start with a base query that includes everything, then if a parameter is not null add that predicate.
If the author was empty in the above example we wouldn't have a hasAuthor to start the Specification.

How can I conditionally combine the predicates in this way?

Upvotes: 1

Views: 802

Answers (1)

Hopey One
Hopey One

Reputation: 1816

You can build your Specification this way.

Specification<Book> spec = Specification.where(null);
if (byAuthor) {
    spec = spec.and(hasAuthor("Robert Ludlum"));
}
if (byTitle) {
    spec = spec.and(hasTitle("The Bourne Identity"));
}
...
repository.findAll(where(spec));

Upvotes: 1

Related Questions