官网html
1 QueryDSL仅仅是一个通用的查询框架,专一于经过Java API构建类型安全的SQL查询。 2 Querydsl能够经过一组通用的查询API为用户构建出适合不一样类型ORM框架或者是SQL的查询语句,也就是说QueryDSL是基于各类ORM框架以及SQL之上的一个通用的查询框架。 3 借助QueryDSL能够在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
首先对于queryDSL有两个版本,com.mysema.querydsl和com.querydsl,前者是3.X系列后者是4.X系列,这里使用的是后者.java
<!--query dsl--> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>${querydsl.version}</version> <scope>provided</scope> </dependency> <!--query dsl end-->
<!--该插件能够生成querysdl须要的查询对象,执行mvn compile便可--> <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin>
执行==mvn compile==以后,能够找到该target/generated-sources/java,而后IDEA标示为源代码目录便可.
sql
城市类:数组
@Entity @Table(name = "t_city", schema = "test", catalog = "") public class TCity { //省略JPA注解标识 private int id; private String name; private String state; private String country; private String map; }
旅馆类:安全
@Entity @Table(name = "t_hotel", schema = "test", catalog = "") public class THotel { //省略JPA注解标识 private int id; private String name; private String address; private Integer city;//保存着城市的id主键 }
Spring Data JPA中提供了QueryDslPredicateExecutor接口,用于支持QueryDSL的查询操做,这样的话单表动态查询就能够参考以下代码:框架
//查找出Id小于3,而且名称带有`shanghai`的记录. //动态条件 QTCity qtCity = QTCity.tCity; //该Predicate为querydsl下的类,支持嵌套组装复杂查询条件 Predicate predicate = qtCity.id.longValue().lt(3) .and(qtCity.name.like("shanghai")); //分页排序 Sort sort = new Sort(new Sort.Order(Sort.Direction.ASC,"id")); PageRequest pageRequest = new PageRequest(0,10,sort); //查找结果 Page<TCity> tCityPage = tCityRepository.findAll(predicate,pageRequest);
QueryDSL对多表查询提供了一个很好地封装,看下面代码:maven
/** * 关联查询示例,查询出城市和对应的旅店 * @param predicate 查询条件 * @return 查询实体 */ @Override public List<Tuple> findCityAndHotel(Predicate predicate) { JPAQueryFactory queryFactory = new JPAQueryFactory(em); JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity,QTHotel.tHotel) .from(QTCity.tCity) .leftJoin(QTHotel.tHotel) .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue())); //添加查询条件 jpaQuery.where(predicate); //拿到结果 return jpaQuery.fetch(); }
城市表左链接旅店表,当该旅店属于这个城市时查询出二者的详细字段,存放到一个Tuple的多元组中.相比原生sql,简单清晰了不少.
那么该怎么调用这个方法呢?ide
@Test public void findByLeftJoin(){ QTCity qtCity = QTCity.tCity; QTHotel qtHotel = QTHotel.tHotel; //查询条件 Predicate predicate = qtCity.name.like("shanghai"); //调用 List<Tuple> result = tCityRepository.findCityAndHotel(predicate); //对多元组取出数据,这个和select时的数据相匹配 for (Tuple row : result) { System.out.println("qtCity:"+row.get(qtCity)); System.out.println("qtHotel:"+row.get(qtHotel)); System.out.println("--------------------"); } System.out.println(result); }
这样作的话避免了返回Object[]数组,下面是自动生成的sql语句:fetch
select tcity0_.id as id1_0_0_, thotel1_.id as id1_1_1_, tcity0_.country as country2_0_0_, tcity0_.map as map3_0_0_, tcity0_.name as name4_0_0_, tcity0_.state as state5_0_0_, thotel1_.address as address2_1_1_, thotel1_.city as city3_1_1_, thotel1_.name as name4_1_1_ from t_city tcity0_ left outer join t_hotel thotel1_ on ( cast(thotel1_.city as signed)=cast(tcity0_.id as signed) ) where tcity0_.name like ? escape '!'
分页查询对于queryDSL不管什么样的sql只须要写一遍,会自动转换为相应的count查询,也就避免了文章开始的问题4,下面代码是对上面的查询加上分页功能:插件
@Override public QueryResults<Tuple> findCityAndHotelPage(Predicate predicate,Pageable pageable) { JPAQueryFactory queryFactory = new JPAQueryFactory(em); JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity.id,QTHotel.tHotel) .from(QTCity.tCity) .leftJoin(QTHotel.tHotel) .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue())) .where(predicate) .offset(pageable.getOffset()) .limit(pageable.getPageSize()); //拿到分页结果 return jpaQuery.fetchResults(); }
和上面不一样之处在于这里使用了offset和limit限制查询结果.而且返回一个QueryResults,该类会自动实现count查询和结果查询,并进行封装.
调用形式以下:
@Test public void findByLeftJoinPage(){ QTCity qtCity = QTCity.tCity; QTHotel qtHotel = QTHotel.tHotel; //条件 Predicate predicate = qtCity.name.like("shanghai"); //分页 PageRequest pageRequest = new PageRequest(0,10); //调用查询 QueryResults<Tuple> result = tCityRepository.findCityAndHotelPage(predicate,pageRequest); //结果取出 for (Tuple row : result.getResults()) { System.out.println("qtCity:"+row.get(qtCity)); System.out.println("qtHotel:"+row.get(qtHotel)); System.out.println("--------------------"); } //取出count查询总数 System.out.println(result.getTotal()); }
生成的原生count查询sql,当该count查询结果为0的话,则直接返回,并不会再进行具体数据查询:
select count(tcity0_.id) as col_0_0_ from t_city tcity0_ left outer join t_hotel thotel1_ on ( cast(thotel1_.city as signed)=cast(tcity0_.id as signed) ) where tcity0_.name like ? escape '!'
生成的原生查询sql:
select tcity0_.id as id1_0_0_, thotel1_.id as id1_1_1_, tcity0_.country as country2_0_0_, tcity0_.map as map3_0_0_, tcity0_.name as name4_0_0_, tcity0_.state as state5_0_0_, thotel1_.address as address2_1_1_, thotel1_.city as city3_1_1_, thotel1_.name as name4_1_1_ from t_city tcity0_ left outer join t_hotel thotel1_ on ( cast(thotel1_.city as signed)=cast(tcity0_.id as signed) ) where tcity0_.name like ? escape '!' limit ?
查看打印,能够发现对应的city也都是同一个对象,hotel是不一样的对象.