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; | |
} |
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
Top comments (1)
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.