DEV Community

Jonathan Tavares
Jonathan Tavares

Posted on

How to use JPA API Specification?

Hey, this is my first article here. I hope that I'll be able to post content about technology more consistently from now on.

I've chosen to bring you an advanced JPA 2 topic: the Specification API.

Specification was a concept that Eric Evans brought up in his book Domain Driven Design(DDD). Based on this, JPA brings this concept as a set of interfaces that facilitate the reuse of predicates, using the Criteria API. That's it, simples as that.

Let's look an example:

We have a book model with id, author, title, isbn and publication year.

@Entity
@Data
public class Book {
@Id
private Integer id;
private String author;
private String title;
private String isbn;
private Integer publicationYear;
}
view raw Book.java hosted with ❤ by GitHub

and We have a book repository interface that extends JpaSpecificationExecutor

public interface BookRepository extends JpaRepository<Book, Integer>, JpaSpecificationExecutor<Book> {
}

The JpaSpecificationExecutor provides two methods:

List<T> findAll(Specification<T> specification);
Page<T> findAll(Specification<T> specification, Pageable pageable);

Now to build our query, We can define the class that implements the Specification interface.

public class BookSpecifications {
public static Specification<Book> booksByAuthor(String authorName) {
return (root, query, cb) -> cb.like(cb.lower(root.get(Book_.author)), "%" + authorName.toLowerCase() + "%");
}
}

With this static method booksByAuthor, we can use this specification to filter for all books that have an author in common. Let's see how simple it is below:

bookRepository.findAll(booksByAuthor("George R. R. Martin"));

One aspect of the specification that makes it more interesting is the fact that we can combine the queries. Let's look an example:

bookRepository.findAll(where(booksByAuthor("George R. R. Martin")).and(booksByPublicationYear(1996)));

And the specification class would look like this:

public class BookSpecifications {
public static Specification<Book> booksByAuthor(String authorName) {
return (root, query, cb) -> cb.like(cb.lower(root.get(Book_.author)), "%" + authorName.toLowerCase() + "%");
}
public static Specification<Book> booksByPublicationYear(int publicationYear) {
return (root, query, cb) -> cb.equal(root.get(Book_.publicationYear), publicationYear);
}
}

That's it. Thank you very much for reading this content. I usually bring content with a more hands-on approach and if something wasn't so clear, feel free to comment and suggest improvements.

If you want to know more about this content, I recommend the official documentation:
JPA Specifications

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (1)

Collapse
 
saladlam profile image
Salad Lam

Hello Jonathan,

Thank you for your sharing. I also wrote something on Specification interface and may inspire you on how to use it in new ways.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more