使用JPA+querydsl如何实现多条件动态查询

目录
  • JPAquerydsl多条件动态查询
    • 介绍一下querydsl
    • 看源码
  • springdataJPA和querydsl
    • 什么是SpringDataJPA?什么是QueryDSL?
    • @Mapper实体-模型映射
    • 项目整体流程
    • 疑问
    • JpaRepository
    • SimpleJpaRepository
    • 写一个Repository继承JpaRepository之后
    • spring-data-jpa的相关语法

JPA querydsl多条件动态查询

相信很多人在做订单管理的时候会用到多条件的检索,比如说查询订单状态是已支付的,金额在100-200之间的商铺a的已完结的订单,这样的多条件。

实现方式有多种,核心就一个if和判空。今天学习了querydsl,来具体回顾一下。

首先是我做的效果图,我们主要看查询怎么实现的。

介绍一下querydsl

首先QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查询。

其次Querydsl可以通过一组通用的查询API为用户构建出适合不同类型ORM框架或者是SQL的查询语句,也就是说QueryDSL是基于各种ORM框架以及SQL之上的一个通用的查询框架。

再然后借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。

开始开发 ,首先是pom依赖

这里要加两个关于querydsl的依赖,jpa和apt,版本是一致的

<dependency>
			<groupId>com.querydsl</groupId>
			<artifactId>querydsl-jpa</artifactId>
			<version>4.2.1</version>
		</dependency>
		<dependency>
			<groupId>com.querydsl</groupId>
			<artifactId>querydsl-apt</artifactId>
			<version>4.2.1</version>
		</dependency>

然后是要配置一个插件来生成Q版的实体类,只有Q版的实体类才能参与querydsl的查询

<plugin>
				<groupId>com.querydsl</groupId>
				<artifactId>querydsl-maven-plugin</artifactId>
				<executions>
					<execution>
						<phase>generate-sources</phase>
						<goals>
							<goal>jpa-export</goal>
						</goals>
						<configuration>

							<targetFolder>target/generated-sources/java</targetFolder>
							<packages>com.jerry.gamemarket.entity</packages>
						</configuration>
					</execution>
				</executions>
			</plugin>
<targetFolder>target/generated-sources/java</targetFolder>

这是生成Q版实体的目标文件夹

<packages>com.jerry.gamemarket.entity</packages>

这是把那些包下的实体生成Q版。

执行mvn compile之后,就能看到生成Q版实体类。

在编写具体的查询方法之前我们需要实例化EntityManager对象以及JPAQueryFactory对象,并且通过实例化控制器时就去实例化JPAQueryFactory对象,所以在启动类中引入一个Bean叫JPAQueryFactory加一个EntityManager参数,可以全局使用。

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

搜索条件实体类

package com.jerry.gamemarket.dto;
import com.jerry.gamemarket.enums.OrderStatusEnums;
import com.jerry.gamemarket.enums.PayStatusEnums;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import java.math.BigDecimal;
/**
 * author by 李兆杰
 * Date 2018/11/28
 */
@Data
public class SearchOrderDTO {
    private  String orderId;
    //   private String id;
    private  String buyerName;
    private  String buyerPhone;
    private  String buyerAddress;
    private  String canteenName;
    private BigDecimal maxAmount;
    private BigDecimal minAmount;
    private Integer orderStatus;
    //    默认未支付
    private Integer payStatus;
    private Integer pageNum = 1;
    private Integer pageSize=10;
}

动态搜索实现类中的方法,这里返回类型是 QueryResults,我们了解一下这个特殊的返回类型

看源码

  • results是返回的list数据数组
  • total是总数
  • offset是从哪开始
  • limit是限制条数
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.querydsl.core;
import com.google.common.collect.ImmutableList;
import java.io.Serializable;
import java.util.List;
import javax.annotation.Nullable;
public final class QueryResults<T> implements Serializable {
    private static final long serialVersionUID = -4591506147471300909L;
    private static final QueryResults<Object> EMPTY = new QueryResults(ImmutableList.of(), 9223372036854775807L, 0L, 0L);
    private final long limit;
    private final long offset;
    private final long total;
    private final List<T> results;
    public static <T> QueryResults<T> emptyResults() {
        return EMPTY;
    }
    public QueryResults(List<T> results, @Nullable Long limit, @Nullable Long offset, long total) {
        this.limit = limit != null ? limit.longValue() : 9223372036854775807L;
        this.offset = offset != null ? offset.longValue() : 0L;
        this.total = total;
        this.results = results;
    }
    public QueryResults(List<T> results, QueryModifiers mod, long total) {
        this(results, mod.getLimit(), mod.getOffset(), total);
    }
    public List<T> getResults() {
        return this.results;
    }
    public long getTotal() {
        return this.total;
    }
    public boolean isEmpty() {
        return this.results.isEmpty();
    }
    public long getLimit() {
        return this.limit;
    }
    public long getOffset() {
        return this.offset;
    }
}
 @Override
    public QueryResults<OrderMaster> dymamicQuery(SearchOrderDTO searchOrderDTO) {
        QOrderMaster o = QOrderMaster.orderMaster;
        JPAQuery<OrderMaster> query = jpaQueryFactory.select(o).from(o);
        if (!StringUtils.isEmpty(searchOrderDTO.getOrderId())){
            query.where(o.orderId.like(searchOrderDTO.getOrderId()));
        }
        if (!StringUtils.isEmpty(searchOrderDTO.getBuyerName())){
            query.where(o.buyerName.like("%"+searchOrderDTO.getBuyerName()+"%"));
        }
        if (!StringUtils.isEmpty(searchOrderDTO.getBuyerPhone())){
            query.where(o.buyerPhone.eq(searchOrderDTO.getBuyerPhone()));
        }
        if (searchOrderDTO.getMaxAmount()!=null && searchOrderDTO.getMinAmount()!=null){
            query.where(o.orderAmount.goe(searchOrderDTO.getMinAmount()));
        }
        if (searchOrderDTO.getMaxAmount()!=null && searchOrderDTO.getMinAmount()!=null){
            query.where(o.orderAmount.loe(searchOrderDTO.getMaxAmount()));
        }
        if (searchOrderDTO.getOrderStatus()!=null){
            query.where(o.orderStatus.eq(searchOrderDTO.getOrderStatus()));
        }
        if (searchOrderDTO.getPayStatus()!=null){
            query.where(o.payStatus.eq(searchOrderDTO.getPayStatus()));
        }
        return query.orderBy(o.createTime.desc())
                .offset((searchOrderDTO.getPageNum()-1)*searchOrderDTO.getPageSize())
                .limit(searchOrderDTO.getPageSize())
                .fetchResults();
    }

这些查询中包含了模糊查询,动态查询和分页

最后是Controller中的引用

@PostMapping("/searchorder")
    public QueryResults<OrderMaster> findByCase(@RequestBody SearchOrderDTO searchOrderDTO){
        QueryResults<OrderMaster> queryResults=orderService.dymamicQuery(searchOrderDTO);
        System.out.println(searchOrderDTO);
        System.out.println(queryResults.getResults());
        return queryResults;
    }

整个查询就完成了,怎么去渲染数据就看大家喜好了。

springdataJPA和querydsl

什么是SpringDataJPA?什么是QueryDSL?

SpringDataJPA是对JPA使用的封装(JPA是java持久层api)

QueryDSL是基于各种ORM(对象关系映射)上的一个通用框架。使用其API类库,可以写出java代码的sql

@Mapper 实体-模型映射

在mapper上使用注解 @Mapper(componentModel = "spring", uses = {})

用于映射dto和entity 自动生成mapper实现 完成相互转化

如果dto和entity中的属性名不匹配,需要增加注解

@Mappings({
@Mapping(source = "entity.name", target = "dto属性名")
})

项目整体流程

服务后台中rest包下对外暴露提供restful接口,具体类中引入代理层,该代理层实现dto以及dao层的处理(注入service以及自动生成的mapper映射),由mapper处理dto与entity之间的相互转化,service层操作db(操作方式为jpa)

疑问

代理类中,为什么要通过在构造方法上增加@Autowired注解对mapper和service进行初始化,而不是对要注入的成员变量上增加@Autowired注解,采用构造方法的方式有何优点?

通过相关资料找到其答案:java变量初始化的顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造方法–>@Autowired 如果该类中有增加构造方法时,执行构造方法时,成员变量还没有初始化,此时会报错,如果没有构造方法可以在成员变量上增加@Autowired注解来初始化变量。为了避免构造方法初始化的时候,成员变量还没有初始化,所以建议在构造方法上增加@Autowired注解。

项目中QueryDSL仅用于生成q类,并没有用java代码格式的sql呀?为什么只用了spring-data-jpa?

JpaRepository

spring-data-jpa简介,spring整合各种第三方框架,命名格式为spring-data-*,spring整合jpa造就了spring-data-jpa。

repository就是持久层,相当于dao、mapper等,项目中定义各种repository继承JpaRepository就可以使用基本的CRUD。

CrudRepository该接口是spring整合jpa的二级接口,此接口提供了普通的CRUD操作,后续新增PagingAndSortingRepository接口,提供findAll方法的重载方法(支持分页),QueryByExampleExecutor优雅的解决了空指针问题,后续优化为JpaRepository接口,该接口对上个接口方法进行优化,返回值更广泛。

SimpleJpaRepository

该类是JpaRepository接口的具体实现,CRUD操作就是由该类提供的。包括四个成员变量JpaEntityInformation、PersistenceProvider、CrudMethodMetadata、EntityManager。 前三个成员变量是为了获取拼接sql,EntityManager执行该sql。相当于session、sqlSession等

写一个Repository继承JpaRepository之后

可以写其实现类,但是不需要implements关键字去实现,spring-data-jpa会自动识别其关系,也可以不写实现类,在运行时期,SimpleJpaRepository该类就是其实现类,如果写了自定义实现类,就会执行实现类中的逻辑

spring-data-jpa的相关语法

对于多表查询 需要用到Specification匿名内部类,重写其方法。感觉遇到多表查询还是写sql比较直观

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • JPA多条件复杂SQL动态分页查询功能

    概述 ORM映射为我们带来便利的同时,也失去了较大灵活性,如果SQL较复杂,要进行动态查询,那必定是一件头疼的事情(也可能是lz还没发现好的方法),记录下自己用的三种复杂查询方式. 环境 springBoot IDEA2017.3.4 JDK8 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0&q

  • 详解spring boot jpa整合QueryDSL来简化复杂操作

    前言 使用过spring data jpa的同学,都很清楚,对于复杂的sql查询,处理起来还是比较复杂的,而本文中的QueryDSL就是用来简化JPA操作的. Querydsl定义了一种常用的静态类型语法,用于在持久域模型数据之上进行查询.JDO和JPA是Querydsl的主要集成技术.本文旨在介绍如何使用Querydsl与JPA组合使用.JPA的Querydsl是JPQL和Criteria查询的替代方法.QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查

  • Spring Data JPA 如何使用QueryDsl查询并分页

    目录 Spring Data JPA 使用QueryDsl查询并分页 使用QueryDSL Spring Data JPA 使用QueryDsl查询并分页 QProblemPoint qProblemPoint = QProblemPoint.problemPoint; Map<String,String> map = getWhere(param); JPAQuery<ProblemPoint> query = jpaQueryFactory .selectFrom(qProbl

  • 在Spring Data JPA中引入Querydsl的实现方式

    一.环境说明 基础框架采用Spring Boot.Spring Data JPA.Hibernate.在动态查询中,有一种方式是采用Querydsl的方式. 二.具体配置 1.在pom.xml中,引入相关包和配置插件. (1)引入包(注:不需要版本号,Spring Boot 会自动匹配合适的版本) <!-- Querydsl相关包 --> <dependency> <groupId>com.querydsl</groupId> <artifactId&

  • 使用JPA+querydsl如何实现多条件动态查询

    目录 JPAquerydsl多条件动态查询 介绍一下querydsl 看源码 springdataJPA和querydsl 什么是SpringDataJPA?什么是QueryDSL? @Mapper实体-模型映射 项目整体流程 疑问 JpaRepository SimpleJpaRepository 写一个Repository继承JpaRepository之后 spring-data-jpa的相关语法 JPA querydsl多条件动态查询 相信很多人在做订单管理的时候会用到多条件的检索,比如说

  • Spring Data JPA实现动态查询的两种方法

    前言 一般在写业务接口的过程中,很有可能需要实现可以动态组合各种查询条件的接口.如果我们根据一种查询条件组合一个方法的做法来写,那么将会有大量方法存在,繁琐,维护起来相当困难.想要实现动态查询,其实就是要实现拼接SQL语句.无论实现如何复杂,基本都是包括select的字段,from或者join的表,where或者having的条件.在Spring Data JPA有两种方法可以实现查询条件的动态查询,两种方法都用到了Criteria API. Criteria API 这套API可用于构建对数据

  • Spring Data JPA中的动态查询实例

    spring Data JPA大大的简化了我们持久层的开发,但是实际应用中,我们还是需要动态查询的. 比如,前端有多个条件,这些条件很多都是可选的,那么后端的SQL,就应该是可以定制的,在使用hibernate的时候,可以通过判断条件来拼接SQL(HQL),当然,Spring Data JPA在简化我们开发的同时,也是提供了支持的. 通过实现Criteria二实现的动态查询,需要我们的Repo接口继承JpaSpecificationExecutor接口,这是个泛型接口. 然后查询的时候,传入动态

  • Spring data jpa的使用与详解(复杂动态查询及分页,排序)

    一. 使用Specification实现复杂查询 (1) 什么是Specification Specification是springDateJpa中的一个接口,他是用于当jpa的一些基本CRUD操作的扩展,可以把他理解成一个spring jpa的复杂查询接口.其次我们需要了解Criteria 查询,这是是一种类型安全和更面向对象的查询.而Spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor. 而JpaSpecifica

  • springdata jpa使用Example快速实现动态查询功能

    目录 Example官方介绍 Example api的组成 限制 使用 测试查询 自定匹配器规则 补充 官方创建ExampleMatcher例子(1.8 lambda) StringMatcher 参数 总结 Example官方介绍 Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not r

  • 基于Vue+elementUI实现动态表单的校验功能(根据条件动态切换校验格式)

    前言 开发过程中遇到了一个需求,根据用户选择的联系方式,动态改变输入框的检验条件,并且整个表单是可以增加的 在线访问:动态表单校验 github(欢迎star): https://github.com/Mrblackant. .. 思考几个问题 1.整个表单是可新增的,所以要遍历生成: 2.联系方式(手机/座机)的切换,是要切换后边不同类型输入框还是只改变校验规则(本篇是动态改变校验规则) 实现 1.elementui的form表单实现校验的时候要给当前el-form-item加上prop属性,

  • 使用LambdaQueryWrapper动态加过滤条件 动态Lambda

    目录 LambdaQueryWrapper动态加过滤条件 动态Lambda mybatis-plus QueryWrapper LambdaQueryWrapper LambdaQueryWrapper动态加过滤条件 动态Lambda 1.遇到这样的需求,在baseservice类中处理数据权限,子类可能使用QueryWrapper或者LambdaQueryWrapper调用base类的方法进行查询. 2.可以拿到的:PO的类,数据权限属性的属性名(是固定的) 直接上代码: /** * 可序列化

  • JPA如何使用nativequery多表关联查询返回自定义实体类

    目录 JPA nativequery多表关联查询返回自定义实体类 JPA多表关联的实现方式 优缺点对比 使用sql并返回自定义实体类 JPA多表关联动态查询(自定义sql语句) 实体类 注解解释 测试类 打印结果 TestVo实体接收类 JPA nativequery多表关联查询返回自定义实体类 JPA官方推荐的多表关联查询使用不便,接触的有些项目可能会使用JPA 做简单查询,Mybaits做复杂查询.所以想要寻找一种好用的解决方案. JPA多表关联的实现方式 1.使用Specification

  • XML卷之实战锦囊(2):动态查询

    动机: 查询功能是我们在网站上见过的最普遍也是最常用的一个功能模块了.以往的信息查询都是连接到数据库的,每一次点击都必须要后台数据库的支持.然而很多情况下用户往往只针对某一部分的数据进行操作,这样不但服务器的负担加重,而且严重的影响用户浏览的速度. 针对这种情况我们需要将用户需要的某一部分数据以XML的方式传递到客户端,用户对这些数据可以很方便的进行操作.既方便了用户,又减轻了服务器数据库的负担.何乐而不为呢!而且这项功能可以通用到其他众多模块,因此添加了这个动态查询功能. 材料: XML卷之动

  • SqlServer使用 case when 解决多条件模糊查询问题

    我们在进行项目开发中,经常会遇到多条件模糊查询的需求.对此,我们常见的解决方案有两种:一是在程序端拼接SQL字符串,根据是否选择了某个条件,构造相应的SQL字符串:二是在数据库的存储过程中使用动态的SQL语句.其本质也是拼接SQL字符串,不过是从程序端转移到数据库端而已. 这两种方式的缺点是显而易见的:一是当多个条件每个都可为空时,要使用多个if语句进行判断:二是拼接的SQL语句容易产生SQL注入漏洞. 最近写数据库存储过程的时候经常使用case when 语句,正好可以用这个语句解决一下以上问

随机推荐