Michael Scharhag

About Michael Scharhag

Michael Scharhag is a Java Developer, Blogger and technology enthusiast. Particularly interested in Java related technologies including Java EE, Spring, Groovy and Grails.

Java: Using the specification pattern with JPA

This article is an introduction to using the specification pattern in Java. We also will see how we can combine classic specifications with JPA Criteria queries to retrieve objects from a relational database.

Within this post we will use the following Poll class as an example entity for creating specifications. It represents a poll that has a start and end date. In the time between those two dates users can vote among different choices. A poll can also be locked by an administrator before the end date has been reached. In this case, a lock date will be set.
 
 

@Entity
public class Poll { 

  @Id
  @GeneratedValue
  private long id;

  private DateTime startDate; 
  private DateTime endDate;
  private DateTime lockDate;

  @OneToMany(cascade = CascadeType.ALL)
  private List<Vote> votes = new ArrayList<>();

}

For better readability I skipped getters, setters, JPA annotations for mapping Joda DateTime instances and fields that aren’t needed in this example (like the question being asked in the poll).

Now assume we have two constraints we want to implement:

  • A poll is currently running if it is not locked and if startDate < now < endDate
  • A poll is popular if it contains more than 100 votes and is not locked

We could start by adding appropriate methods to Poll like: poll.isCurrentlyRunning(). Alternatively we could use a service method like pollService.isCurrentlyRunning(poll). However, we also want to be able to query the database to get all currently running polls. So we might add a DAO or repository method like pollRepository.findAllCurrentlyRunningPolls().

If we follow this way we implement the isCurrentlyRunning constraint two times in two different locations. Things become worse if we want to combine constraints. What if we want to query the database for a list of all popular polls that are currently running?

This is where the specification pattern come in handy. When using the specification pattern we move business rules into extra classes called specifications.

To get started with specifications we create a simple interface and an abstract class:

public interface Specification<T> {  
  boolean isSatisfiedBy(T t);  
  Predicate toPredicate(Root<T> root, CriteriaBuilder cb);
  Class<T> getType();
}
abstract public class AbstractSpecification<T> implements Specification<T> {
  @Override
  public boolean isSatisfiedBy(T t) {
    throw new NotImplementedException();
  }  

  @Override
  public Predicate toPredicate(Root<T> poll, CriteriaBuilder cb) {
    throw new NotImplementedException();
  }

  @Override
  public Class<T> getType() {
    ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
    return (Class<T>) type.getActualTypeArguments()[0];
  }
}

Please ignore the AbstractSpecification<T> class with the mysterious getType() method for a moment (we come back to it later).

The central part of a specification is the isSatisfiedBy() method, which is used to check if an object satisfies the specification. toPredicate() is an additional method we use in this example to return the constraint as javax.persistence.criteria.Predicate instance which can be used to query a database.

For each constraint we create a new specification class that extends AbstractSpecification<T> and implements isSatisfiedBy() and toPredicate().

The specification implementation to check if a poll is currently running looks like this:

public class IsCurrentlyRunning extends AbstractSpecification<Poll> {

  @Override
  public boolean isSatisfiedBy(Poll poll) {
    return poll.getStartDate().isBeforeNow() 
        && poll.getEndDate().isAfterNow() 
        && poll.getLockDate() == null;
  }

  @Override
  public Predicate toPredicate(Root<Poll> poll, CriteriaBuilder cb) {
    DateTime now = new DateTime();
    return cb.and(
      cb.lessThan(poll.get(Poll_.startDate), now),
      cb.greaterThan(poll.get(Poll_.endDate), now),
      cb.isNull(poll.get(Poll_.lockDate))
    );
  }
}

Within isSatisfiedBy() we check if the passed object matches the constraint. In toPredicate() we construct a Predicate using JPA’s CriteriaBuilder. We will use the resulting Predicate instance later to build a CriteriaQuery for querying the database.

The specification for checking if a poll is popular looks similar:

public class IsPopular extends AbstractSpecification<Poll> {

  @Override
  public boolean isSatisfiedBy(Poll poll) {
    return poll.getLockDate() == null && poll.getVotes().size() > 100;
  }  

  @Override
  public Predicate toPredicate(Root<Poll> poll, CriteriaBuilder cb) {
    return cb.and(
      cb.isNull(poll.get(Poll_.lockDate)),
      cb.greaterThan(cb.size(poll.get(Poll_.votes)), 5)
    );
  }
}

If we now want to test if a Poll instance matches one of these constraints we can use our newly created specifications:

boolean isPopular = new IsPopular().isSatisfiedBy(poll);
boolean isCurrentlyRunning = new IsCurrentlyRunning().isSatisfiedBy(poll);

For querying the database we need to extend our DAO / repository to support specifications. This can look like the following:

public class PollRepository {

  private EntityManager entityManager = ...

  public <T> List<T> findAllBySpecification(Specification<T> specification) {
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

    // use specification.getType() to create a Root<T> instance
    CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(specification.getType());
    Root<T> root = criteriaQuery.from(specification.getType());

    // get predicate from specification
    Predicate predicate = specification.toPredicate(root, criteriaBuilder);

    // set predicate and execute query
    criteriaQuery.where(predicate);
    return entityManager.createQuery(criteriaQuery).getResultList();
  }
}

Here we finally use the getType() method implemented in AbstractSpecification<T> to create CriteriaQuery<T> and Root<T> instances. getType() returns the generic type of the AbstractSpecification<T> instance defined by the subclass. For IsPopular and IsCurrentlyRunning it returns the Poll class. Without getType() we would have to create the CriteriaQuery<T> and Root<T> instances inside toPredicate() of every specification we create. So it is just a small helper to reduce boiler plate code inside specifications. Feel free to replace this with your own implementation if you come up with better approaches.

Now we can use our repository to query the database for polls that match a certain specification:

List<Poll> popularPolls = pollRepository.findAllBySpecification(new IsPopular());
List<Poll> currentlyRunningPolls = pollRepository.findAllBySpecification(new IsCurrentlyRunning());

At this point the specifications are the only components that contain the constraint definitions. We can use it to query the database or to check if an object fulfills the required rules.

However one question remains: How do we combine two or more constraints? For example we would like to query the database for all popular polls that are still running.

The answer to this is a variation of the composite design pattern called composite specifications. Using a composite specification we can combine specifications in different ways.

To query the database for all running and popular pools we need to combine the isCurrentlyRunning with the isPopular specification using the logical and operation. Let’s create another specification for this. We name it AndSpecification:

public class AndSpecification<T> extends AbstractSpecification<T> {

  private Specification<T> first;
  private Specification<T> second;

  public AndSpecification(Specification<T> first, Specification<T> second) {
    this.first = first;
    this.second = second;
  }

  @Override
  public boolean isSatisfiedBy(T t) {
    return first.isSatisfiedBy(t) && second.isSatisfiedBy(t);
  }

  @Override
  public Predicate toPredicate(Root<T> root, CriteriaBuilder cb) {
    return cb.and(
      first.toPredicate(root, cb), 
      second.toPredicate(root, cb)
    );
  }

  @Override
  public Class<T> getType() {
    return first.getType();
  }
}

An AndSpecification is created out of two other specifications. In isSatisfiedBy() and toPredicate() we return the result of both specifications combined by a logical and operation.

We can use our new specification like this:

Specification<Poll> popularAndRunning = new AndSpecification<>(new IsPopular(), new IsCurrentlyRunning());
List<Poll> polls = myRepository.findAllBySpecification(popularAndRunning);

To improve readability we can add an and() method to the Specification interface:

public interface Specification<T> {

  Specification<T> and(Specification<T> other);

  // other methods
}

and implement it within our abstract implementation:

abstract public class AbstractSpecification<T> implements Specification<T> {

  @Override
  public Specification<T> and(Specification<T> other) {
    return new AndSpecification<>(this, other);
  }

  // other methods
}

Now we can chain multiple specification by using the and() method:

Specification<Poll> popularAndRunning = new IsPopular().and(new IsCurrentlyRunning());
boolean isPopularAndRunning = popularAndRunning.isSatisfiedBy(poll);
List<Poll> polls = myRepository.findAllBySpecification(popularAndRunning);

When needed we can easily extend this further with other composite specifications (for example OrSpecification or NotSpecification).

Conclusion

When using the specification pattern we move business rules in separate specification classes. These specification classes can be easily combined by using composite specifications. In general, specification improve reusability and maintainability. Additionally specifications can easily be unit tested. For more detailed information about the specification pattern I recommend this article by Eric Evans and Martin Fowler.

  • You can find the source of this example project on GitHub.

 

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


six + = 10



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books