Advanced Spring Data JPA – Specifications and Querydsl |
|

In my last blog post I introduced the basic feature set of Spring Data JPA. In this post I'd like to dive into some more features and how they can help you simplify data access layer implementation even further. The Spring Data repository abstraction consists of an interface based programming model, some factory classes and a Spring namespace to easily configure the infrastructure. A typical repository interface looks something like this:
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Customer findByEmailAddress(String emailAddress);
List<Customer> findByLastname(String lastname, Sort sort);
Page<Customer> findByFirstname(String firstname, Pageable pageable);
}
The first method simply expects to find a single customer with a given email address, the second method returns all customers with a given lastname and applies the given Sort to the result whereas the third method returns a Page of customers. For details have a look at the former blog post.
Although this approach is really convenient (you don't even have to write a single line of implementation code to get the queries executed) it has two drawbacks: first, the number of query methods might grow for larger applications because of – and that's the second point – the queries define a fixed set of criterias. To avoid these two drawbacks, wouldn't it be cool if you could come up with a set of atomic predicates that you could combine dynamically to build your query?
If you are a long time JPA user you might answer: isn't that what the Criteria API is for? Right, so let's have a look what a sample business requirement implementation could look like using the JPA Criteria API. Here's the use case: on their birthday's we want to send a voucher to all long term customers. How do we retrieve the ones that match?
We pretty much have two parts to the predicate: the birthday as well as what we call long-term-customer. Let's assume the latter means that the customer account was created at least two years ago. Here's how it would look like implemented using the JPA 2.0 Criteria API.
LocalDate today = new LocalDate(); CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Customer> query = builder.createQuery(Customer.class); Root<Customer> root = query.from(Customer.class); Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today); Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2); query.where(builder.and(hasBirthday, isLongTermCustomer)); em.createQuery(query.select(root)).getResultList();
What do we have here? We create a new LocalDate for convenience and go on with three lines of boilerplate to set up the necessary JPA infrastructure instances. Then we have two lines building the predicates, one to concatenate both and a last one to execute the actual query. We're using the meta-model classes introduced with JPA 2.0 and generated by the Annotation Processing API. The main problem with this code is that the predicates are not easy to externalize and reuse because you need to set up the CriteriaBuilder, CriteriaQuery and Root first. Also, readability of the code is poor as it is hard to quickly infer the intent of the code upon first glance.
Specifications
To be able to define reusable Predicates we introduced the Specification interface that is derived from concepts introduced in Eric Evans' Domain Driven Design book. It defines a specification as a predicate over an entity which is exactly what our Specification interface represents. The actually only consists of a single method:
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}
so we can now easily use a helper class like this:
public CustomerSpecifications {
public static Specification<Customer> customerHasBirthday() {
return new Specification<Customer> {
public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
return cb.equal(root.get(Customer_.birthday), today);
}
};
}
public static Specification<Customer> isLongTermCustomer() {
return new Specification<Customer> {
public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
}
};
}
}
Admittedly, not the most beautiful code in the world but it serves our initial requirement quite nicely: we can refer to a set of atomic specifications. The next question is: how will we execute these specifications? To do so, you simply extend JpaSpecificationExecutor in your repository interface and thus "pull in" an API to execute Specifications:
public interface CustomerRepository extends JpaRepository<Customer>, JpaSpecificationExecutor {
// Your query methods here
}
A client can now do:
customerRepository.findAll(hasBirthday()); customerRepository.findAll(isLongTermCustomer());
The basic repository implementation will prepare the CriteriaQuery, Root and CriteriaBuilder for you, apply the Predicate created by the given Specification and execute the query. But couldn't we just have created simple query methods to achieve that? Correct, but remember our second initial requirement. We wanted to be able to freely combine atomic Specifications to create new ones one the fly. To do so we have a helper class Specifications that provides and(…) and or(…) methods to concatenate atomic Specifications. There's also a where(…) that provides some syntactic sugar to make the expression more readable. The use case sample I came up with in the beginning looks like this:
customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));
This reads fluently, improving readability as well as providing additional flexibility as compared to the use of the JPA Criteria API alone. The only caveat here is that coming up with the Specification implementation requires quite some coding effort.
Querydsl
To cure that pain an open-source project called Querydsl has come up with a quite similar but also different approach. Just like the JPA Criteria API it uses a Java 6 annotation processor to generate meta-model objects but produces a much more approachable API. Another cool thing about the project is that it has not only has support for JPA but also allows querying Hibernate, JDO, Lucene, JDBC and even plain collections.
So to get that up and running you add Querydsl to your pom.xml and configure the APT plugin accordingly.
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>maven-apt-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources</outputDirectory>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
This will cause your build to create special query classes – QCustomer inside the very same package in our case.
QCustomer customer = QCustomer.customer; LocalDate today = new LocalDate(); BooleanExpression customerHasBirthday = customer.birthday.eq(today); BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2));
This is not only almost fluent English out of the box, the BooleanExpressions are even reusable without further wrapping which lets us get rid off the additional (and a bit ugly to implement) Specification wrapper. A further plus is that you get IDE code completion at every dot on the right hand side of the assignments, so customer. + CTRL + SPACE would list all properties. customer.birthday. + CTRL + SPACE would list all available keywords and so on. To execute Querydsl predicates you simply let your repository extend QueryDslPredicateExecutor:
public interface CustomerRepository extends JpaRepository<Customer>, QueryDslPredicateExecutor {
// Your query methods here
}
Clients can then simply do:
BooleanExpression customerHasBirthday = customer.birthday.eq(today); BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2)); customerRepository.findAll(customerHasBirthday.and(isLongTermCustomer));
Summary
Spring Data JPA repository abstraction allows executing predicates either via JPA Criteria API predicates wrapped into a Specification object or via Querydsl predicates. To enable this functionality you simply let your repository extend JpaSpecificationExecutor or QueryDslPredicateExecutor (you could even use both side by side if you liked). Note that you need the Querydsl JARs in the class in case you decide for the Querydsl approach.
One more thing
One more cool thing about the Querydsl approach is that it is not only available for our JPA repositories but for our MongoDB support as well. The functionality is included in the just released M2 release of Spring Data MongoDB already. Beyond that both the Mongo and JPA module of Spring Data are supported on the CloudFoundry platform. See the cloudfoundry-samples wiki for getting started with Spring Data and CloudFoundry.
Similar Posts
- Fine-tuning Spring Data repositories
- Getting started with Spring Data JPA
- Spring Data Arora SR1 released
- Countdown to Grails 2.0: Persistence
- Yet Another Flavour of GORM: MongoDB





Angus Mezick says:
Added on April 26th, 2011 at 7:45 amGreat post! I look forward to experimenting with this when the project is more mature. Will this project allow me to easily connect to two datasources? For example, I have two databases. One is for page content (CMS/products) and the other is for shopping (orders and stuff). Currently using straight hibernate was as easy as setting up two session factories and letting spring auto wire them by name. When I tried to do this with entity managers I started having to just through a lot of hoops and creating many extra config files. I would love to see a blog post about how to configure JPA Spring for multiple data sources.
Thanks!
Oliver Gierke (blog author) says:
Added on April 26th, 2011 at 8:09 amThe jpa:repositories namespace element will automatically pick up single EntityManagerFactory you defined in one datasource scenarios. In a scenario with two ones you simply define to EntityManagerFactoryBeans and wire them to the repositories element via the entity-manager-factory-ref atttribute.
Ted Pennings says:
Added on April 26th, 2011 at 8:10 amThis is great! Do you consider it production ready? If so, when is the target for that and a 1.0 release?
Jeremy Bogatirsky says:
Added on April 26th, 2011 at 10:13 amHi,
nice post !
In the first code listing, shouldn't we have a "Customer" as return type for interface's methods ?
Oliver Gierke (blog author) says:
Added on April 26th, 2011 at 10:24 amThx for reporting this… fixed!
Bas Huisman says:
Added on April 27th, 2011 at 1:06 pmI think you mistakenly wrote QueryDslSpecificationExecutor (twice), I think you meant QueryDslPredicateExecutor
Chuck S says:
Added on April 27th, 2011 at 3:54 pmThis is great stuff! Also would like to know when this will go GA?
Bruce says:
Added on April 27th, 2011 at 4:09 pmFor anyone considering plagiarizing this code, keep in mind customers with a birthday on leap day.
Oliver Gierke (blog author) says:
Added on April 28th, 2011 at 2:00 am@Bas – thx, fixed!
@Chuck – we will have an RC very soon followed by the GA release very quickly. The majority of the codebase was a port from the Hades project I lead before it actually became Spring Data JPA. Hades is in version 2.0 already so we consider the codebase quite mature already.
Abhishek Chavan says:
Added on April 29th, 2011 at 4:18 am@Oliver Gierke – Great Article. Am thinking about using Spring Data in my current project. However, hoping for the GA release. I loved the QueryDSL part best. Will tell you how it goes.
Lukas Eder says:
Added on May 6th, 2011 at 8:53 am@Oliver: Since you do not seem to really love the verboseness of the criteria code you've been writing in this example, you might be interested in having a look at jOOQ (www.jooq.org). While JPA 2.0 is clearly the "standard", it seems that most developers keep struggling and constructing workaround over workaround, just to get their SQL running… Unlike QueryDSL, which is an excellent extension for JPA (and a true alternative to criteria query), I see jOOQ as the way to go if you don't really want to have an OR-mapper, but typesafe, plain SQL access to your database.
I'd be curious what you think about jOOQ.
Cheers
Lukas
cheap G-Star pants & shorts says:
Added on May 12th, 2011 at 3:41 amdo well
cheap nike air max shoes says:
Added on May 23rd, 2011 at 9:19 pm50% OFF! Cheap Nike Air Max Shoes,Air Max 90,Air max 95, air max 2009,Air max 2011,Air max 24-7,air max 87,air max TN,BW,air max 2010,air max zenyth,air max skyline, air max structure triax 91,air maxtrainer 1 low,air max Taiwind 2009,air max 180,Air Max Griffey 2011,Air Pegasus 89, Air Waffle Trainer,Air Max Terra Ninety,2012 shoes. asics onitsuka tiger shoes, choose the one best of Nike Air Max Shoes for you and save.Free Shipping Paypal payment wholesale and retail.
Web site: http://www.pickmaxshoes.com
We are a professional supplier of nike air max shoes, now welcome to the world of air max! A heavy discount is going on in our store. Nike Air Max 2011 is our main product now including Nike Air Max 2010 , Nike Air Max 2009 , Nike Air Max 90 ,Nike Air Max 95 ,Nike Air Max 24-7 ,Nike Air Structure Triax 91 , Air Max Griffey 2011, Air Pegasus 89 , Air Waffle Trainer , Air Max Terra Ninety and so on. For example, nike air The Air Max 91 is a favorite among people from all walks of life from yuppies to people of the hip-hop community – it is one of many shoes that sneakerheads units. Probably the most popular Air Max runners, the Nike Air Max 2011 brought an aircraft in the air before. So get fabulous shoes with a lower price, what are you waiting for?
0ur site: http://www.pickmaxshoes.com
FAQ
Payment
For customers’safety, we take Western Union/Money Gram and Paypal the payment method.
Shipment
All the items in our store are free shipping.
Item will be shipped out as soon as payment cleared and tracking number will be provided for you within 12 business hours after shipping
Delivery will take approx. 5-7 business days.
Return Policy
If you don't satisfy with the quality, you are allowed to return the item within 7 days after receipt, then we will issue you a full refund or exchange accordingly.
If any questions, please feel free to contact us!
0ur site: http://www.pickmaxshoes.com
cheap nike air max shoes says:
Added on May 23rd, 2011 at 9:21 pm[url=http://www.pickmaxshoes.com/Nike-Air-Max-90--s233/]Nike Air Max 90[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-90--s233/]Cheap nike Air Max 90[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-90--s233/]Nike Air Max 90 sale[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-95--s234/]Cheap Nike Air Max 95[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-95--s234/]Nike Air Max 95 sale[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-95--s234/]Nike Air Max 95[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-97-s235/]Nike Air Max 97[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-97-s235/]Nike Air Max 97 sale[/url]
[url=http://www.pickmaxshoes.com/Nike-Air-Max-97-s235/]cheap Nike Air Max 97[/url]
cheap air max shoes says:
Added on May 23rd, 2011 at 9:23 pm50% OFF! Cheap Nike Air Max Shoes,Air Max 90,Air max 95, air max 2009,Air max 2011,Air max 24-7,air max 87,air max TN,BW,air max 2010,air max zenyth,air max skyline, air max structure triax 91,air maxtrainer 1 low,air max Taiwind 2009,air max 180,Air Max Griffey 2011,Air Pegasus 89, Air Waffle Trainer,Air Max Terra Ninety,2012 shoes. asics onitsuka tiger shoes, choose the one best of Nike Air Max Shoes for you and save.Free Shipping Paypal payment wholesale and retail.
Abhishek Chavan says:
Added on June 15th, 2011 at 7:55 amI am using Spring Data JPA with a RDBMS in my project. I have a requirement where I have to fetch a single record from the Database from a table which has the latest date. For this I need to use a limit and order by function OR using sub queries. However, I wished to know if i wish for not to use NamedQuery is there a way I can achieve this using Spring Data JPA and QueryDSL. I am using the 2nd part which you mentioned using QueryDSL to retrieve data. is there a way i can specify somewhere the maximum records to fetch.
Dave Macpherson says:
Added on July 19th, 2011 at 11:24 am@Oliver Gierke (blog author) said:
"The jpa:repositories namespace element will automatically pick up single EntityManagerFactory you defined in one datasource scenarios. In a scenario with two ones you simply define to EntityManagerFactoryBeans and wire them to the repositories element via the entity-manager-factory-ref atttribute."
Oliver:
First of all, I'm a fan of Spring Data's goals and love the simplicity that it can bring to a project. However, I'm not sure how your suggested approach regarding mulitple persistent units works when you also have to manage separate transaction managers for each of your EntityManagers. Unless I'm mistaken, Spring's configuration supports only a single transaction manager declaration in the application context which you can't use if you have multiple persistent units/entity managers each with their own transaction managers. You have to bypass this and set up AOP advisors to do transaction management instead. How does Spring Data's default transaction management cope with this, and are we breaking any assumptions in Spring Data's transaction management if we should try this approach?
I've very recently had to configure Spring to use multiple JPA persistence units and it was very unclear how to configure all the EMF's, AOP advices, etc within the application context to get this to all wire up correctly. I feel the Spring documentation doesn't offer a clear example of how one should go about this, and your documentation doesn't address this either.
Rather than just saying "simply define two EntityManagerFactoryBeans…" (which I don't believe is all that you need to do) can I request that you explicitly document an example of using two persistent units within a Spring Data project so that it's clear what "hoops" you have to jump through to get this to work properly?
Kind regards,
Dave
Oliver Gierke says:
Added on July 19th, 2011 at 11:49 amHi Dave,
thanks for the feedback. I didn't want to go into the bloody details of working with two separate datasources (could be I shouldn't have mentioned it at all) as it's not really related to the main topic of this blog post. But of course I can elaborate a bit more on the scenario.
Actually it's not that hard at all. Things improved quite a lot with Spring 3.0 which introduced the capability of defining the name of the transaction manager to be used inside the @Transactional annotation (see the reference documentation [0] for detail). Regarding Spring data, the story is actually a two fold one. First, as we're using @Transactional inside the repository base implementation we don't know the transaction manager you declare upfront. So we let you define the transaction-manager-ref attribute inside the repositories namespace element and fallback on the known default if not configured. This essential defines the basis for how you work with multiple datasources.
1. You define datasource, EntityManagerFactory and a transaction manager per datasource.
2. You define jpa:repositories elements and wire entity-manager-factory-ref and transaction-manager-ref to the beans of the datasource that's actually backing a given set of repositories (you might want/need to use exlude filters for fine-grained control over what repository interfaces get picked up)
You can actually find an example of such a configuration inside this [1] test case configuration file. The according test case is EntityManagerFactoryRefTests.
The second part of the story is that you probably want to annotate the concrete repository interfaces and custom implementations with @Transactional as well. As this interface is entirely under your control you can (and actually have to) reference the transaction manager to be used explicitly here. There's a JIRA ticket [2] that requests the config from the XML namespace actually would actually define the transaction manager to be used for those repositories as well. I am a bit torn on this one as the configuration of the annotation would then not necessarily reflect what's actually done at runtime.
All of this deals with the case of two separate datasources that don't need to get their transactions synchronized at all. If that was the case you'd need to stick to JTA and the JtaTransactionManager infrastructure but then you'd actually deal with a single transaction manager again.
Hope that helps,
Ollie
[0] http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#tx-multiple-tx-mgrs-with-attransactional
[1] https://github.com/SpringSource/spring-data-jpa/blob/master/src/test/resources/multiple-entity-manager-integration-context.xml
[2] https://jira.springsource.org/browse/DATAJPA-18
Air Max pas cher says:
Added on August 17th, 2011 at 1:09 amThanks for providing such a great article,Thank you for sharing the information.I like it very much.it is of vital importance for me.
laseelan says:
Added on October 4th, 2011 at 6:59 amHow can i use hibernate filters with spring data jpa.
Ken Griffey Jr Shoes says:
Added on January 7th, 2012 at 3:57 amDo not climb mountains, I do not know day high also; not a deep river, I do not know in the thick also.
Gal L. says:
Added on January 22nd, 2012 at 2:44 pmHi Oliver,
Thanks for the post.
After using Spring Data 3.1, I'm missing one method in the CRUD/JPA/DSL interfaces – QBE pattern.
Although one can use implementation specific solution for QBE, it is not JPA 2.0 complaint.
It would be great if one of the Spring data interfaces would have a QBE method. a reference interface could be found here:
http://catchylink.com/OpenJPAQBEInterface
and a reference implementation in the OpenJPA source in class:
http://catchylink.com/OpenJPAQBEImpl
Sid says:
Added on February 21st, 2012 at 10:50 amHi,
I need to wrap all Spring Data Access exceptions from each repository into our my own custom exception. Since all the repositories are created using proxy and have no concrete implementation, I was wondering how do I catch them or write aspects for them?
/**
*
* @author Sid Sharma
*
*/
public class ActivityStreamServiceImpl implements ActivityStreamService {
@Autowired
private ActivityEntryRepository activityEntryRepository;
public void addActivityEntry(ActivityEntryDetail activityEntry) throws SabaException
{
this.activityEntryRepository.save(activityEntry);
}
}
public interface ActivityEntryRepository extends PagingAndSortingRepository
{
}
Here ActivityEntryRepository has no concrete implementation. How do I ensure every exception raised from ActivityEntryRepository is caught and re thrown?
Thanks,
Sid
Willis Shawler says:
Added on May 2nd, 2012 at 5:21 pmErica,
Your mom used to bellydance?! That's so awesome! Even better that she still dances, and in the kitchen, too. I do some of my best work there.
Glen Maughn says:
Added on May 3rd, 2012 at 8:53 amCould the Larsons get any cuter?!? I think you guys could be in a magazine, actually I know you could. I can't believe how big William is either he just gets cuter everyday!
Nike Requin says:
Added on May 16th, 2012 at 2:42 amo awesome! Even better that she still dances, and in the kitchen, too. I do some of my best work there.
Nike Requin says:
Added on May 16th, 2012 at 2:43 amhat's so awesome! Even better that she still dances, and in the kitchen, too. I do some of my best work there.
ew says:
Added on May 30th, 2012 at 1:28 pmProblem – although our project is Java primarily, we have recently began coding new entities and other code in Groovy.
however the mysema maven plugin does not seem to be abe to generate sources from my Groovy entites. – anyone know a way around this ?
Was says:
Added on June 20th, 2012 at 3:19 amHi Oliver,
Thanks again for this great post. I followed your recommendation about multidatabases configuration and everything worked as expected.
Actually, we decided to use Atomikos and org.springframework.transaction.jta.JtaTransactionManager. After having reconfigured my applicationContext, all the find methods still work but the save() method of the repository don't seem to work anymore. I activated the Hibernate Log and I see no insert request, only select ones.
I one of your replies you were talking about the fact that we have to deal with a single transaction manager again. Can you be a little bit more explicit about that?
Thanks
cmdr says:
Added on June 29th, 2012 at 8:40 amHi
I am currently using spring data jpa and query dsl.
I was wondering why does there are limitation on the repository return type.
We can query for entities (great, good job), but we also what to query for date, string (I mean make a projection on entities properties).
The querydsl ConstructorExpression is very rich and seems to do the job.
Have a look at http://www.querydsl.com/static/querydsl/2.7.0/reference/html/ch03.html
At least wee will need a builder for JPQLQuery in the QueryDslPredicateExecutor with something like this:
public JPQLQuery getJPQLQuery()
{
return new JPAQuery(entitymanager);//for hibernate
}
Regards
銉兂銈儸銉笺儷 銈点偆銈?4 says:
Added on November 30th, 2012 at 2:03 amvery nice work , thank you
銉兂銈儸銉笺儷 銈点偆銈?3 says:
Added on December 3rd, 2012 at 2:09 pmHi there I just downloaded the iframe app for my Facebook page it's installed, but there says there is a problem . . . can you help with this?Thanks,Jared
Sarfaraz Khan says:
Added on January 28th, 2013 at 12:34 amHi
Just curious to know about "Customer_" that has been used in code like
"Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);"
Regards
Sarfaraz Khan
itpudge says:
Added on March 15th, 2013 at 6:55 amHi,how could i write this sql to jpql,
"delete c from comment c left join article a on c.article_id=a.id where a.user_id=?1"
I try @Query("delete from Comment as c where c.article.user.id=?1"),but it query as "delete from comment cross join article article1_ where user_id=6 "no join and mistake article.user_id for comment.user_id.
But when i try @Query("select c from Comment c where c.article.user.id = ?1"),it is ok~~~
That make me confused.
xiaolong says:
Added on March 19th, 2013 at 6:51 pmcheap Jordan Shoes and Nike Air Jordan Shoes from http://www.jumptopshoesclothes.com . We are
the professional Jordan shoes shop for you to buy Jordan Shoes, cheap
Jordan Shoes, and we also supply Nike Air Force One,Nike Dunk,Nike
Shoes.
Our goal is to provide you with the latest products and the lowest
prices. As a kicks fan ourselves, we believe that you shouldn't have
to pay a lot for what you love, which is exclusive sneaker in a
variety of different colors and styles!