QueryDSL-JPA

QueryDSL-JPA

QueryDSL簡介

官網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

第一步:Maven引入依賴

<!--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標示爲源代碼目錄便可.
imagesql

實體類

城市類:數組

@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是不一樣的對象.

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息