Shekhar Gulati is a Java Consultant with over 5 years experience.He is currently working with Xebia India an Agile Software Development company.The postings on this site and on his blog are his own and do not necessarily represent opinion of his employer. His own blog is at http://whyjava.wordpress.com/ and you can follow him on twitter here. Shekhar has posted 25 posts at DZone. View Full User Profile

DDD Specification Support in Hades

10.21.2010
| 6091 views |
  • submit to reddit

In the first part of this series on Hades I talked about queries and finder support.  In this article, I will talk about another useful feature of Hades called Specification. Specification is a Domain Driven Design concept wherein you define a Predicate to check whether an object matches or does not match a criteria. Hades provides Specification support in the form Specification interface which is a wrapper over JPA Criteria API. The Specification interface provides a callback method that gets CriteriaBuilderCriteriaQuery<T> and Root<T> as its arguments and returns a Predicate.

public interface Specification<T> {

Predicate toPredicate(Root<T> root, CriteriaQuery<T> query,
CriteriaBuilder builder);
}

Hades GenericDao interface provides two methods with Specification support.

List<T> readAll(final Specification<T> spec); // return list of entities matching spec
Page<T> readAll(final Specification<T> spec, final Pageable pageable); // returns a Page of entities matching the spec

 

Specification offers :

  1. You can easily plug-in new matching strategies: Specification is a strategy interface so it is easy to plug-in new criteria without changing the API. So, you can add strategies like bookByAuthorName or bookWithPriceLessThan or bookByAuthorWithPriceInBetween very easily.
    public static Specification<Book> bookByAuthorName(final String value) {
    return new Specification<Book>() {

    public Predicate toPredicate(Root<Book> root, CriteriaQuery<Book> query, CriteriaBuilder builder) {
    return builder.equal(root.get("author"), value);
    }
    };
    }

    public static Specification<Book> withPriceBetween(final double start, final double end) {
    return new Specification<Book>() {

    public Predicate toPredicate(Root<Book> root, CriteriaQuery<Book> query, CriteriaBuilder builder) {
    return builder.between(root.<Double> get("price"), start, end);
    }
    };
    }
    and the client code
    @Test
    public void shouldFindBookByAuthorNameSpecification() throws Exception {
    List<Book> allBooks = bookDao.readAll(BookSpecifications.bookByAuthorName("shekhar"));
    assertThat(allBooks.size(), is(equalTo(1)));
    }
  2. You can avoid intermingling different entities: suppose that we want to find all the books written by a particular author and Author is an entity of our application. So, if we don't use Specification, we will have to add a method findAllBooksByAuthor(Author author). So, our Book domain dao is now tightly coupled with Author and as we keep adding different entities it all gets dirty. So, with the help of Specification we can avoid this mess.
  3. You can reduce boiler plate code : with Specification you don't have to worry about writing the boiler plate code of creating the CriteriaBuilderCriteriaQuery<T> and Root<T>. You get all this via callback.
  4. You can combine Specifications : One of the coolest thing about Specifications is that you can build complex specifications by joining different specifications together. Hades provides a utility class called Specifications which provides utility methods to combine Specification instances. For example
     public static Specification<Book> bookByAuthorAndPriceBetween(final String authorName, final double priceStart, final double priceEnd) {
    return where(bookByAuthorName(authorName)).and(withPriceBetween(priceStart, priceEnd));
    }


Published at DZone with permission of its author, Shekhar Gulati.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Oliver Gierke replied on Sat, 2010/10/23 - 5:41am

Hi, yet another cool on on the new specifications feature. I just wanted to ping all readers about the availabilty of 2.0.0.RELEASE that includes the feature Shekhar describes: http://redmine.synyx.org/news/24. A small not on your last point: It even gets a little bit simple if you don't create a Specification for the combined one. Simply chain them together at the repository call: readAll(where(bookByAuthorName(authorName)).and(withPriceBetween(priceStart, priceEnd))); Admittedly, implementing the snippets is quite verbose due to the Java inner classes concept. But you can create a nice API for clients using that way. In fact, the verbosity should be reduced when we get the SAM stuff in Java 8 ;). Cheers, Ollie

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.