SpringBoot环境下QueryDSL-JPA的入门及进阶

转载自:https://www.jianshu.com/p/69dcb1b85bbb

一、环境配置

1. 引入maven依赖

        <!-- querydsl -->
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>
                <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <scope>provided</scope>
        </dependency>

2. 添加maven插件

添加这个插件是为了让程序自动生成query type(查询实体,命名方式为:"Q"+对应实体名)。
上文引入的依赖中querydsl-apt即是为此插件服务的。

注:在使用过程中,如果遇到query type无法自动生成的情况,用maven更新一下项目即可解决(右键项目->Maven->Update Project)。

           <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/generatedsources/java
                           </outputDirectory>
                           <processor>
                               com.querydsl.apt.jpa.JPAAnnotationProcessor
                           </processor>
                       </configuration>
                   </execution>
               </executions>
            </plugin>

二、使用

在Spring环境下,我们可以通过两种风格来使用QueryDSL。

一种是使用JPAQueryFactory的原生QueryDSL风格,
另一种是基于Spring Data提供的QueryDslPredicateExecutor<T>的Spring-data风格。

使用QueryDslPredicateExecutor<T>可以简化一些代码,使得查询更加优雅。
JPAQueryFactory的优势则体现在其功能的强大,支持更复杂的查询业务。甚至可以用来进行更新和删除操作。

下面分别介绍两种风格的使用方式。

1. JPAQueryFactory

JPAQueryFactory使用逻辑类似于HQL/SQL语法,不再额外说明。
QueryDSL在支持JPA的同时,也提供了对Hibernate的支持。可以通过HibernateQueryFactory来使用。

装配

    @Bean
    public JPAQueryFactory jpaQuery(EntityManager entityManager) {
        return new JPAQueryFactory(entityManager);
    }

注入

    @Autowired
    JPAQueryFactory queryFactory;

1.1 更新/删除

Update

QMemberDomain qm = QMemberDomain.memberDomain;
queryFactory.update(qm).set(qm.status, "0012").where(qm.status.eq("0011")).execute();

Delete

QMemberDomain qm = QMemberDomain.memberDomain;
queryFactory.delete(qm).where(qm.status.eq("0012")).execute();

1.2 查询

查询简直可以玩出花来。

1.2.1 select()和fetch()的几种常用写法

QMemberDomain qm = QMemberDomain.memberDomain;
//查询字段-select()
List<String> nameList = queryFactory.select(qm.name).from(qm).fetch();
//查询实体-selectFrom()
List<MemberDomain> memberList = queryFactory.selectFrom(qm).fetch();
//查询并将结果封装至dto中
List<MemberFavoriteDto> dtoList = queryFactory.select(Projections.constructor(MemberFavoriteDto.class,qm.name,qf.favoriteStoreCode)).from(qm).leftJoin(qm.favoriteInfoDomains,qf).fetch();
//去重查询-selectDistinct()
List<String> distinctNameList = queryFactory.selectDistinct(qm.name).from(qm).fetch();
//获取首个查询结果-fetchFirst()
MemberDomain firstMember = queryFactory.selectFrom(qm).fetchFirst();
//获取唯一查询结果-fetchOne()
//当fetchOne()根据查询条件从数据库中查询到多条匹配数据时,会抛`NonUniqueResultException`。
MemberDomain anotherFirstMember = queryFactory.selectFrom(qm).fetchOne();

1.2.2 where子句查询条件的几种常用写法

        //查询条件示例
        List<MemberDomain> memberConditionList = queryFactory.selectFrom(qm)
                //like示例
                .where(qm.name.like('%'+"Jack"+'%')
                        //contain示例
                        .and(qm.address.contains("厦门"))
                        //equal示例
                        .and(qm.status.eq("0013"))
                        //between
                        .and(qm.age.between(20, 30)))               
                .fetch();

1.2.3 多表查询

//以左关联为例-left join
QMemberDomain qm = QMemberDomain.memberDomain;
QFavoriteInfoDomain qf= QFavoriteInfoDomain.favoriteInfoDomain;
List<MemberDomain> leftJoinList = queryFactory.selectFrom(qm).leftJoin(qm.favoriteInfoDomains,qf).where(qf.favoriteStoreCode.eq("0721")).fetch();

1.2.4 使用Mysql聚合函数

//聚合函数-avg()
Double averageAge = queryFactory.select(qm.age.avg()).from(qm).fetchOne();

//聚合函数-concat()
String concat = queryFactory.select(qm.name.concat(qm.address)).from(qm).fetchOne();

//聚合函数-date_format()
String date = queryFactory.select(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", qm.registerDate)).from(qm).fetchOne();

当用到DATE_FORMAT这类QueryDSL似乎没有提供支持的Mysql函数时,我们可以手动拼一个String表达式。这样就可以无缝使用Mysql中的函数了。

1.2.5 使用子查询

下面的用法中子查询没有什么实际意义,只是作为一个写法示例。

//子查询
List<MemberDomain> subList = queryFactory.selectFrom(qm).where(qm.status.in(JPAExpressions.select(qm.status).from(qm))).fetch();

1.2.6 排序

//排序
List<MemberDomain> orderList = queryFactory.selectFrom(qm).orderBy(qm.name.asc()).fetch();

1.2.7 分页的两种写法

        QMemberDomain qm = QMemberDomain.memberDomain;
        //写法一
        JPAQuery<MemberDomain> query = queryFactory.selectFrom(qm).orderBy(qm.age.asc());
        long total = query.fetchCount();//hfetchCount的时候上面的orderBy不会被执行
        List<MemberDomain> list0= query.offset(2).limit(5).fetch();
        //写法二
        QueryResults<MemberDomain> results =                   
        queryFactory.selectFrom(qm).orderBy(qm.age.asc()).offset(2).limit(5)
        .fetchResults();
        List<MemberDomain> list = results.getResults();
        logger.debug("total:"+results.getTotal());
        logger.debug("limit:"+results.getLimit());
        logger.debug("offset:"+results.getOffset());

写法一和二都会发出两条sql进行查询,一条查询count,一条查询具体数据。
写法二的getTotal()等价于写法一的fetchCount
无论是哪种写法,在查询count的时候,orderBy、limit、offset这三个都不会被执行。可以大胆使用。

1.2.8 使用Template实现QueryDSL未支持的语法

其实Template我们在1.2.4 使用Mysql聚合函数中已经使用过了。QueryDSL并没有对Mysql的所有函数提供支持,好在它给我们提供了Template特性。我们可以使用Template来实现各种QueryDSL未直接支持的语法。
示例如下。

QMemberDomain qm = QMemberDomain.memberDomain;
        //使用booleanTemplate充当where子句或where子句的一部分
        List<MemberDomain> list = queryFactory.selectFrom(qm).where(Expressions.booleanTemplate("{} = \"tofu\"", qm.name)).fetch();
        //上面的写法,当booleanTemplate中需要用到多个占位时
        List<MemberDomain> list1 = queryFactory.selectFrom(qm).where(Expressions.booleanTemplate("{0} = \"tofu\" and {1} = \"Amoy\"", qm.name,qm.address)).fetch();
        
        //使用stringTemplate充当查询语句的某一部分
        String date = queryFactory.select(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", qm.registerDate)).from(qm).fetchFirst();
        //在where子句中使用stringTemplate
        String id = queryFactory.select(qm.id).from(qm).where(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", qm.registerDate).eq("2018-03-19")).fetchFirst();

不过Template好用归好用,但也有其局限性。
例如当我们需要用到复杂的正则表达式匹配的时候,就有些捉襟见肘了。这是由于Template中使用了{}来作为占位符,而正则表达式中也可能使用了{},因而会产生冲突。

三、使用心得

1. 查询条件中字段为String时关于null,empty,blank的表达

(如果你还不了解null,empty,blank的区别,请先自行搜索了解)
QueryDSL为String类型的字段提供了.isEmpty(),isNull(),.isNotEmpty(),isNotNull()这四个函数支持,唯独没有对blank提供支持。经过测试,我发现可以通过这种方式来实现对blank的使用:.eq(""),.ne("")

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值