使⽤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.ity</packages>
</configuration>
</execution>
</executions>
</plugin>
<targetFolder>target/generated-sources/java</targetFolder>
这是⽣成Q版实体的⽬标⽂件夹
<packages>com.ity</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.ums.OrderStatusEnums;
import com.ums.PayStatusEnums;
import lombok.Data;
import t.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 ;
llect.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;
enum怎么用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;
}
public QueryResults(List<T> results, QueryModifiers mod, long total) {
this(results, Limit(), Offset(), total);
}
public List<T> getResults() {
sults;
}
public long getTotal() {
al;
}
public boolean isEmpty() {
sults.isEmpty();
}
public long getLimit() {
return this.limit;
}
public long getOffset() {
return this.offset;
}
}
@Override
public QueryResults<OrderMaster> dymamicQuery(SearchOrderDTO searchOrderDTO) {
QOrderMaster o = derMaster;
JPAQuery<OrderMaster> query = jpaQueryFactory.select(o).from(o);
if (!StringUtils.OrderId())){
query.derId.OrderId()));
}
if (!StringUtils.BuyerName())){
query.where(o.buyerName.like("%"+BuyerName()+"%"));
}
if (!StringUtils.BuyerPhone())){
query.where(o.buyerPhone.BuyerPhone()));
}
if (MaxAmount()!=null && MinAmount()!=null){
query.(MinAmount()));
}
if (MaxAmount()!=null && MinAmount()!=null){
query.derAmount.MaxAmount()));
}
if (OrderStatus()!=null){
query.derStatus.OrderStatus()));
}
if (PayStatus()!=null){
query.where(o.payStatus.PayStatus()));
}
ateTime.desc())
.offset((PageNum()-1)*PageSize())
.PageSize())
.fetchResults();
}
这些查询中包含了模糊查询,动态查询和分页
最后是Controller中的引⽤
@PostMapping("/searchorder")
public QueryResults<OrderMaster> findByCase(@RequestBody SearchOrderDTO searchOrderDTO){
QueryResults<OrderMaster> queryResults=orderService.dymamicQuery(searchOrderDTO);
System.out.println(searchOrderDTO);
System.out.Results());
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⽐较直观
以上为个⼈经验,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。