zoukankan      html  css  js  c++  java
  • QueryDSL-JPA

    QueryDSL-JPA

    QueryDSL简介

    官网

    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系列,这里使用的是后者.

    第一步: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标示为源代码目录即可.
    image

    实体类

    城市类:

    @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对多表查询提供了一个很好地封装,看下面代码:

        /**
         * 关联查询示例,查询出城市和对应的旅店
         * @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,简单清晰了很多.
    那么该怎么调用这个方法呢?

        @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语句:

    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是不同的对象.

  • 相关阅读:
    【BZOJ2749】【HAOI2012】外星人[欧拉函数]
    【BZOJ3675】【APIO2014】序列分割 [斜率优化DP]
    【BZOJ2326】【HNOI2011】数学作业 [矩阵乘法][DP]
    【BZOJ1996】【HNOI2010】合唱队 [区间DP]
    【BZOJ1857】【SCOI2010】传送带 [三分]
    【BZOJ2338】【HNOI2011】数矩形 [计算几何]
    【BZOJ2330】【SCOI2011】糖果 [差分约束]
    【BZOJ1095】【ZJOI2007】捉迷藏 [动态点分治]
    【BZOJ4031】【HEOI2015】小Z的房间 [Matrix-Tree][行列式]
    【FJWC2017】交错和查询 [线段树]
  • 原文地址:https://www.cnblogs.com/chenglc/p/11230755.html
Copyright © 2011-2022 走看看