SpringDataJPA + QueryDSL 实现复杂查询

技术分享 ·

简介 JPA 全称 Java Persistence API,中文意为Java持久层API。JPA并不是一个框架,而是一类框架的统称,比如早期的Hibernate,都是ORM框架,用于在关系型数据库和业务实体对象之间作映射,我们可以通过 JPA 所提供的默认的方法来快速实现CRUD操作。 而QueryDSL 则是在 …

简介

JPA 全称 Java Persistence API,中文意为Java持久层API。JPA并不是一个框架,而是一类框架的统称,比如早期的Hibernate,都是ORM框架,用于在关系型数据库和业务实体对象之间作映射,我们可以通过 JPA 所提供的默认的方法来快速实现CRUD操作。
而QueryDSL 则是在 SpringDataJPA 基础上更进一步,更面向对象,可以说是为 JPA 插上了翅膀。
本文将介绍如何通过 SpringDataJPA + QueryDSL 实现一些诸如子查询、动态 SQL 的复杂操作。

1.手动嵌套 Mysql 函数

一般情况下,如果想嵌套 MySQL 函数或者执行更复杂的查询逻辑,可以利用 Querydsl 的表达式语言来构建动态查询。QueryDSL 提供了一个强大的 API 来编写类型安全的查询,同时还可以灵活地组合 SQL 函数。
例如:要查询表 test01 中 name 字段在 “张三,李四,王五” 中的数据。

select * from test01 t where FIND_IN_SET(t.name, "张三,李四,王五") > 0;

这里可以使用 Expressions.stringTemplate 来创建布尔表达式代表 MySQL 的 FIND_IN_SET 函数。

public List<Tuple> findRecordsByNameList(String nameList) {
        JPAQuery<Tuple> jpaQuery = jpaQueryFactory.select(
                qdo.id,qdo.name
        ).from(qdo);
        StringTemplate stringTemplate = Expressions.stringTemplate("FIND_IN_SET({0},{1})", qdo.name,nameList);
        return jpaQuery.where(stringTemplate.stringValue().gt("")).fetch();
}

2.动态 SQL

在 MyBatis 中,我们可以通过使用 <if><when><foreach> 等标签来轻松实现动态 SQL。然而,在 Spring Data JPA 结合 Querydsl 的场景下,实现类似的动态 SQL 逻辑则相对较为复杂。由于目前使用这种组合的人数较少,因此在网上找到相关的教程和资源也比较困难。 在 QueryDSL 中我们可以通过StreamAPI来实现类似于 mybatis 中的 <foreach> 标签。

public boolean isNotDoingTask(Map<Long, Long> instanceProcessMap) {
    JPAQuery<Long> jpaQuery = jpaQueryFactory.select(qdo.id)
            .from(qdo)
            .where(qdo.operationType.ne("DOING")
                    .and(qdo.operationType.isNotNull())
                    .and(qdo.operationType.ne("RECALL"))
                    .and(instanceProcessMap.keySet().stream()
                            .map(key -> qdo.processId.eq(key)
                                    .and(qdo.instanceDetailId.eq(instanceProcessMap.get(key))))
                            .reduce(BooleanExpression::or)
                            .orElse(null) // 如果有任何条件,则使用or连接,否则为null
                    )
            );
    return jpaQuery.fetch().isEmpty();
public List<PrdHomepageDocConfigVO> findAuthList(Long userId, Long orgId, List<Long> roleIds) {
    JPAQuery<PrdHomepageDocConfigVO> jpaQuerySelect = getJpaQuerySelect().where(
            qdo.deleteFlag.eq(0)
                    .and( qdo.publicScope.eq(DocConfigEnum.PublicScope.PUBLIC.getCode()))
                    .or(qdo.publicScope.eq(DocConfigEnum.PublicScope.APPOINT_RES.getCode())
                            .and(qdo.personnelObject.prepend(",").concat(",").like("%" + userId + "%")))
                    .or(qdo.publicScope.eq(DocConfigEnum.PublicScope.APPOINT_ORG.getCode())
                            .and(qdo.personnelObject.prepend(",").concat(",").like("%" + orgId + "%")))
                    .or(qdo.publicScope.eq(DocConfigEnum.PublicScope.APPOINT_ROLE.getCode())
                            .and(roleIds.stream()
                                    .map(roleId -> qdo.personnelObject.prepend(",").concat(",").like("%" + roleId + "%"))
                            .reduce(BooleanExpression::or).orElse(null))));
    return jpaQuerySelect.fetch();
}

3.子查询

JPAQuery<PrdDocInspectVO> jpaQuery = jpaQueryFactory.select(Projections.bean(PrdDocInspectVO.class,
                qdo
        ))
        .from(qdo)
        .leftJoin(qdoProject).on(qdoProject.id.eq(qdo.projectId)
                .and(qdoProject.deleteFlag.eq(0)))
        .leftJoin(qdoInspectRecord)
        .on(qdoInspectRecord.docId.eq(qdo.id)
                .and(qdoInspectRecord.createTime.eq(
                        JPAExpressions.select(qdoInspectRecord.createTime.max())
                                .from(qdoInspectRecord)
                                .where(qdoInspectRecord.docId.eq(qdo.id)
                                        .and(qdoInspectRecord.whetherRelease.eq(true))
                                        .and(qdoInspectRecord.docType.eq(DocConfigEnum.ReasonType.OPPO.getCode()))
                                ))))
        .where(qdo.deleteFlag.eq(0));
  • ORM
  • SpringDataJPA
  • QueryDSL