Querydsl for JPA/Hibernate is an alternative to both JPQL and JPA 2 Criteria queries. It combines the dynamic nature of Criteria queries with the expressiveness of JPQL and all that in a fully typesafe manner.
If you are unsure if switching to Querydsl from JPA 2 Criteria is worth a try, see this comparison.
Maven integration
To get started with Querydsl for Hibernate using a Maven 2 based build environment, follow the following steps.
Add the following dependencies to your Maven project and make sure that the Maven 2 repo of Mysema Source is accessible from your POM :
<dependency> <groupId>com.mysema.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>1.8.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.mysema.querydsl</groupId> <artifactId>querydsl-hql</artifactId> <version>1.8.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.2</version> </dependency>
Querydsl uses the Annotation Processing Tool of Java 6 for code generation which needs to be configured as well :
<project> <build> <plugins> ... <plugin> <groupId>com.mysema.maven</groupId> <artifactId>maven-apt-plugin</artifactId> <version>0.3.2</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> ... </plugins> </build> </project>
The JPAAnnotationProcessor finds domain types annotated with the javax.persistence.Entity annotation and generates query types for them.
If you use Hibernate annotations in your domain types you should use the APT processor com.mysema.query.apt.hibernate.HibernateAnnotationProcessor instead.
Run clean install and you will get your Query types generated into target/generated-sources/java.
If you use Eclipse, run mvn eclipse:eclipse to update your Eclipse project to include target/generated-sources/java as a source folder.
Now you are able to construct Querydsl query instances and instances of the query domain model.
Ant integration
If you have an Ant based build environment you can use one of the packaged releases from the Downloads page.
Place the jar files from the full-deps bundle on your classpath and use the following tasks for Querydsl code generation :
<!-- APT based code generation --> <javac srcdir="${src}" classpathref="cp"> <compilerarg value="-proc:only"/> <compilerarg value="-processor"/> <compilerarg value="com.mysema.query.apt.jpa.JPAAnnotationProcessor"/> <compilerarg value="-s"/> <compilerarg value="${generated}"/> </javac> <!-- compilation --> <javac classpathref="cp" destdir="${build}"> <src path="${src}"/> <src path="${generated}"/> </javac>
Replace src with your main source folder, generated with your folder for generated sources and build with your target folder.
Using query types
To create queries with Querydsl you need to instantiate variables and Query implementations. We will start with the variables.
Let's assume that your project has the following domain type :
@Entity public class Customer { private String firstName; private String lastName; public String getFirstName(){ return firstName; } public String getLastName(){ return lastName; } public void setFirstName(String fn){ firstName = fn; } public void setLastName(String ln)[ lastName = ln; } }
Querydsl will generate a query type with the simple name QCustomer into the same package as Customer. QCustomer can be used as a statically typed variable in Querydsl queries as a representative for the Customer type.
QCustomer has a default instance variable which can be accessed as a static field :
QCustomer customer = QCustomer.customer;
Alternatively you can define your own Customer variables like this :
QCustomer customer = new QCustomer("myCustomer");
Querying
For the HQL-module HibernateQuery is the main Query implementation. It is instantiated like this :
// where session is a Hibernate session HQLQuery query = new HibernateQuery (session);
To use the JPA API instead of the Hibernate API, you can instantiate a JPAQuery like this :
// where entityManager is a JPA EntityManager HQLQuery query = new JPAQuery (entityManager);
To retrieve the customer with the first name Bob you would construct a query like this :
QCustomer customer = QCustomer.customer; HQLQuery query = new HibernateQuery (session); Customer bob = query.from(customer) .where(customer.firstName.eq("Bob")) .uniqueResult(customer);
The from call defines the query source, the where part defines the filter and uniqueResult defines the projection and tells Querydsl to return a single element. Easy, right?
To create a query with multiple sources you just use the HQLQuery interface like this :
query.from(customer, company);
And to use multiple filters use it like this
query.from(customer) .where(customer.firstName.eq("Bob"), customer.lastName.eq("Wilson"));
Or like this
query.from(customer) .where(customer.firstName.eq("Bob").and(customer.lastName.eq("Wilson")));
In native JPQL form the query would be written like this :
from Customer as customer where customer.firstName = "Bob" and customer.lastName = "Wilson"
Using joins
Querydsl supports the following join variants in JPQL : inner join, join, left join and full join. Join usage is typesafe, and follows the following pattern :
query.from(cat) .innerJoin(cat.mate, mate) .leftJoin(cat.kittens, kitten) .list(cat);
The native JPQL version of the query would be
from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten
Another example
query.from(cat) .leftJoin(cat.kittens, kitten) .on(kitten.bodyWeight.gt(10.0)) .list(cat);
With the following JPQL version
from Cat as cat left join cat.kittens as kitten with kitten.bodyWeight > 10.0
General usage
Use the the cascading methods of the HQLQuery interface like this
- from : Define the query sources here.
- innerJoin, join, leftJoin, fullJoin, on : Define join elements using these constructs. For the join methods the first argument is the join source and the second the target (alias).
- where : Define the query filters, either in varargs form separated via commas or cascaded via the and-operator.
- groupBy : Define the group by arguments in varargs form.
- having : Define the having filter of the "group by" grouping as an varags array of EBoolean expressions.
- orderBy : Define the ordering of the result as an varargs array of order expressions. Use asc() and desc() on numeric, string and other comparable expression to access the OrderSpecifier instances.
- limit, offset, restrict : Define the paging of the result. Limit for max results, offset for skipping rows and restrict for defining both in one call.
Ordering
The syntax for declaring ordering is
query.from(customer) .orderBy(customer.lastName.asc(), customer.firstName.desc()) .list(customer);
which is equivalent to the following native JPQL
from Customer as customer order by customer.lastName asc, customer.firstName desc
Grouping
Grouping can be done in the following form
query.from(customer) .groupBy(customer.lastName) .list(customer.lastName);
which is equivalent to the following native JPQL
select customer.lastName from Customer as customer group by customer.lastName
Delete clauses
Delete clauses in Querydsl follow a simple delete-where-execute form. Here are some examples :
QCat cat = QCat.cat; // delete all cats new HibernateDeleteClause(session, cat).execute(); // delete all cats with kittens new HibernateDeleteClause(session, cat).where(cat.kittens.isNotEmpty()).execute();
The second parameter of the HibernateDeleteClause constructor is the entity to be deleted. The where call is optional and the execute call performs the deletion and returns the amount of deleted entities.
For JPA based Delete usage, use the JPADeleteClause instead.
Update clauses
Update clauses in Querydsl follow a simple update-set/where-execute form. Here are some examples :
QCat cat = QCat.cat; // rename cats named Bob to Bobby new HibernateUpdateClause(session, cat).where(cat.name.eq("Bob")) .set(cat.name, "Bobby") .execute();
The second parameter of the HibernateUpdateClause constructor is the entity to be updated. The set invocations define the property updates in SQL-Update-style and the execute call performs the Update and returns the amount of updated entities.
For JPA based Update usage, use the JPAUpdateClause instead.
Subqueries
To create a subquery you create a HQLSubQuery instance, define the query parameters via from, where etc and use unique or list to create a subquery, which is just a type-safe Querydsl expression for the query. unique is used for a unique (single) result and list for a list result.
query.from(department) .where(department.employees.size().eq( new HQLSubQuery().from(d).unique(d.employees.size().max()) )).list(department);
Another example
query.from(employee) .where(employee.weeklyhours.gt( new HQLSubQuery().from(employee.department.employees, e) .where(e.manager.eq(employee.manager)) .unique(e.weeklyhours.avg()) )).list(employee);
Exposing the original query
If you need to do tune the original Query before the execution of the query you can expose it like this :
HibernateQuery query = new HibernateQuery(session); org.hibernate.Query hibQuery = query.from(employee).createQuery(employee); hibQuery.setResultTransformer(someTransformer); List results = hibQuery.list();
Using Native SQL in Hibernate queries
Querydsl supports Native SQL in Hibernate via the HibernateSQLQuery class.
To use it, you must generate Querydsl query types for your SQL schema. This can be done for example with the following Maven configuration :
<plugin> <groupId>com.mysema.querydsl</groupId> <artifactId>querydsl-maven-plugin</artifactId> <version>${project.version}</version> <executions> <execution> <goals> <goal>export</goal> </goals> </execution> </executions> <configuration> <jdbcDriver>org.apache.derby.jdbc.EmbeddedDriver</jdbcDriver> <jdbcUrl>jdbc:derby:target/demoDB;create=true</jdbcUrl> <packageName>com.mycompany.mydomain</packageName> <targetFolder>target/generated-sources/java</targetFolder> </configuration> <dependencies> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derby</artifactId> <version>${derby.version}</version> </dependency> </dependencies> </plugin>
When the query types have successfully been generated into the location of your choice, you can use them in your queries.
Single column query :
// serialization templates SQLTemplates templates = new DerbyTemplates(); // query types (S* for SQL, Q* for domain types) SAnimal cat = new SAnimal("cat"); SAnimal mate = new SAnimal("mate"); QCat catEntity = QCat.cat; HibernateSQLQuery query = new HibernateSQLQuery(session, templates); List<String> names = query.from(cat).list(cat.name);
Query multiple columns :
query = new HibernateSQLQuery(session, templates); List<Object[]> rows = query.from(cat).list(cat.id, cat.name);
Query all columns :
List<Object[]> rows = query.from(cat).list(cat.all());
Query in SQL, but project as entity :
query = new HibernateSQLQuery(session, templates); List<Cat> cats = query.from(cat).orderBy(cat.name.asc()).list(catEntity);
Query with joins :
query = new HibernateSQLQuery(session, templates); cats = query.from(cat) .innerJoin(mate).on(cat.mateId.eq(mate.id)) .where(cat.dtype.eq("Cat"), mate.dtype.eq("Cat")) .list(catEntity);
Query and project into DTO :
query = new HibernateSQLQuery(session, templates); List<CatDTO> catDTOs = query.from(cat) .orderBy(cat.name.asc()) .list(EConstructor.create(CatDTO.class, cat.id, cat.name));
If you are using the JPA API instead of the Hibernate API, then use JPASQLQuery instead of HibernateSQLQuery.
Where to go next
For an up-to-date guide on how to use Querydsl with Hibernate, see the latest Reference Documentation.
And if you have any issues, questions or ideas for enhancement post a thread in the Mysema Source Forum.
Happy querying!
you can do that in SQLAchemy since two years ... and you don't need all this xml shit
ReplyDeleteActually you can do that in Querydsl since two years as well. I just didn't describe it before in a blog post.
ReplyDeleteThe XML overhead for Java builds is regrettable, but Maven and Ant are the standard build tools for Java, so I added integration guides for them.
Querydsl itself doesn't require any XML configuration. All it needs is the APT configuration in the build tool of choice, which can be XML-based or not, and the Querydsl library dependencies.
For a non-XML-based build tool for Java, consider using Apache Buildr. It uses a Ruby DSL for declaring builds.
SQLAlchemy is for Python. Who cares?
ReplyDeleteGreat work, Timo!
QueryDSL is just awesome and an decent piece of quality software. Thanks Timo!!! :-)
ReplyDeleteLooks perfect. Playing with it for an hour and it is the best query solution I've ever tried.
ReplyDeleteThanks a lot.
DeleteGood!!!
ReplyDeletePlease add tutorial for Netbeans and Querydsl
Hi,
ReplyDeletehow do I write condition type:
WHERE (Expr1 AND Expr2) OR (Expr3 AND Expr4)
using query dsl?
btw great tool :)
expr1.and(expr2).or(expr3.and(expr4))
DeleteLooks really great. I've been doing the same sort of thing with Joist (http://www.joist.ws), but generating the query object with non-APT/regular code generation (since I'm already generating the domain objects). Your query DSL looks very polished--nice work!
ReplyDeleteThanks, Joist looks nice as well, but does your entity generation approach support inheritance properly? Keeping property change tracking in sessions might make your domain objects cleaner.
DeleteGreat work, Timo!
ReplyDeleteI discovered Querydsl reading a post at Overflow where you say it combines "the expressivity and conciseness of HQL and the dynamic nature of Criteria". Your post was "biased" but you didn't lie.
Besides, reading the documentation I could start using it in 5 minutes. I added the pom.xml dependencies and plugin, run mvn clean install, created a query and saw it worked as expected.
I will try it a little bit more and see if it meets my needs.
Thank you very much for this awesome tool!
Thanks a lot, May!
DeleteLet me know if you find any rough edges.
can i get generate Q classes without the mvn install? mvn apt:process doesn't work for. i'm looking from something faster than mvn install.
ReplyDeleteIt won't work if you put the configuration under the execution, try putting the configuration directly under the plugin, if you want to use mvn apt:process directly.
DeleteHello,
ReplyDeleteI have the following Situation using queryDsl with Maven.
My Project is using a 3rd Party lib containing all Entity Classes for my Prject. Lets call this 3rd party lib MyDomainsPrj.
In my App lets call it MyAppPrj I am referring to MyDomainsPrj and here I want to use the generated Q..
How can I configure apt-maven-plugin in MyAppPrj to proccess Entity files in MyDomainsPrj.jar and not in MyAppPrj?
Best Regards!
Excellent Tool ... TY
ReplyDeleteI love querydsl
ReplyDeleteIs QueryDSL open source?
ReplyDeleteYes.
DeleteHi using HQL we can get the result set as POJO class(not the domain object) for eg a transfer object. How can we acheive the same with Querydsl. I can't see any reference for returning the query results as POJO classes.
ReplyDeleteQuerydsl supports for example constructor and bean population. Those options are available via the Projections class.
DeleteI think that it would be nice to have projections that guaranties types-safety. Currently both constructor and bean population uses reflection that don't notices about problem in the compile time. Something like RowMapper that exists in Spring JDBC would be helpful.
ReplyDeleteYou can use MappingProjection for typesafe projections: http://www.querydsl.com/static/querydsl/3.3.1/apidocs/index.html?com/mysema/query/types/MappingProjection.html
DeleteIs it possible to left join the entities with no relation using 'on' condtion alone in JPAQuery?
ReplyDeleteNo, that's unfortunately not possible.
Delete