使用JPA+querydsl如何實(shí)現(xiàn)多條件動(dòng)態(tài)查詢
JPA querydsl多條件動(dòng)態(tài)查詢
相信很多人在做訂單管理的時(shí)候會(huì)用到多條件的檢索,比如說查詢訂單狀態(tài)是已支付的,金額在100-200之間的商鋪a的已完結(jié)的訂單,這樣的多條件。
實(shí)現(xiàn)方式有多種,核心就一個(gè)if和判空。今天學(xué)習(xí)了querydsl,來具體回顧一下。
首先是我做的效果圖,我們主要看查詢?cè)趺磳?shí)現(xiàn)的。
介紹一下querydsl
首先QueryDSL僅僅是一個(gè)通用的查詢框架,專注于通過Java API構(gòu)建類型安全的SQL查詢。
其次Querydsl可以通過一組通用的查詢API為用戶構(gòu)建出適合不同類型ORM框架或者是SQL的查詢語句,也就是說QueryDSL是基于各種ORM框架以及SQL之上的一個(gè)通用的查詢框架。
再然后借助QueryDSL可以在任何支持的ORM框架或者SQL平臺(tái)上以一種通用的API方式來構(gòu)建查詢。目前QueryDSL支持的平臺(tái)包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
開始開發(fā) ,首先是pom依賴
這里要加兩個(gè)關(guān)于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>
然后是要配置一個(gè)插件來生成Q版的實(shí)體類,只有Q版的實(shí)體類才能參與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版實(shí)體的目標(biāo)文件夾
<packages>com.jerry.gamemarket.entity</packages>
這是把那些包下的實(shí)體生成Q版。
執(zhí)行mvn compile之后,就能看到生成Q版實(shí)體類。
在編寫具體的查詢方法之前我們需要實(shí)例化EntityManager對(duì)象以及JPAQueryFactory對(duì)象,并且通過實(shí)例化控制器時(shí)就去實(shí)例化JPAQueryFactory對(duì)象,所以在啟動(dòng)類中引入一個(gè)Bean叫JPAQueryFactory加一個(gè)EntityManager參數(shù),可以全局使用。
@Bean public JPAQueryFactory queryFactory(EntityManager entityManager){ return new JPAQueryFactory(entityManager); }
搜索條件實(shí)體類
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; // 默認(rèn)未支付 private Integer payStatus; private Integer pageNum = 1; private Integer pageSize=10; }
動(dòng)態(tài)搜索實(shí)現(xiàn)類中的方法,這里返回類型是 QueryResults,我們了解一下這個(gè)特殊的返回類型
看源碼
results
是返回的list數(shù)據(jù)數(shù)組total
是總數(shù)offset
是從哪開始limit
是限制條數(shù)
// // 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(); }
這些查詢中包含了模糊查詢,動(dòng)態(tài)查詢和分頁
最后是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; }
整個(gè)查詢就完成了,怎么去渲染數(shù)據(jù)就看大家喜好了。
springdataJPA和querydsl
什么是SpringDataJPA?什么是QueryDSL?
SpringDataJPA是對(duì)JPA使用的封裝(JPA是java持久層api)
QueryDSL是基于各種ORM(對(duì)象關(guān)系映射)上的一個(gè)通用框架。使用其API類庫,可以寫出java代碼的sql
@Mapper 實(shí)體-模型映射
在mapper上使用注解 @Mapper(componentModel = "spring", uses = {})
用于映射dto和entity 自動(dòng)生成mapper實(shí)現(xiàn) 完成相互轉(zhuǎn)化
如果dto和entity中的屬性名不匹配,需要增加注解
@Mappings({ @Mapping(source = "entity.name", target = "dto屬性名") })
項(xiàng)目整體流程
服務(wù)后臺(tái)中rest包下對(duì)外暴露提供restful接口,具體類中引入代理層,該代理層實(shí)現(xiàn)dto以及dao層的處理(注入service以及自動(dòng)生成的mapper映射),由mapper處理dto與entity之間的相互轉(zhuǎn)化,service層操作db(操作方式為jpa)
疑問
代理類中,為什么要通過在構(gòu)造方法上增加@Autowired注解對(duì)mapper和service進(jìn)行初始化,而不是對(duì)要注入的成員變量上增加@Autowired注解,采用構(gòu)造方法的方式有何優(yōu)點(diǎn)?
通過相關(guān)資料找到其答案:java變量初始化的順序?yàn)椋红o態(tài)變量或靜態(tài)語句塊–>實(shí)例變量或初始化語句塊–>構(gòu)造方法–>@Autowired 如果該類中有增加構(gòu)造方法時(shí),執(zhí)行構(gòu)造方法時(shí),成員變量還沒有初始化,此時(shí)會(huì)報(bào)錯(cuò),如果沒有構(gòu)造方法可以在成員變量上增加@Autowired注解來初始化變量。為了避免構(gòu)造方法初始化的時(shí)候,成員變量還沒有初始化,所以建議在構(gòu)造方法上增加@Autowired注解。
項(xiàng)目中QueryDSL僅用于生成q類,并沒有用java代碼格式的sql呀?為什么只用了spring-data-jpa?
JpaRepository
spring-data-jpa簡(jiǎn)介,spring整合各種第三方框架,命名格式為spring-data-*,spring整合jpa造就了spring-data-jpa。
repository就是持久層,相當(dāng)于dao、mapper等,項(xiàng)目中定義各種repository繼承JpaRepository就可以使用基本的CRUD。
CrudRepository該接口是spring整合jpa的二級(jí)接口,此接口提供了普通的CRUD操作,后續(xù)新增PagingAndSortingRepository接口,提供findAll方法的重載方法(支持分頁),QueryByExampleExecutor優(yōu)雅的解決了空指針問題,后續(xù)優(yōu)化為JpaRepository接口,該接口對(duì)上個(gè)接口方法進(jìn)行優(yōu)化,返回值更廣泛。
SimpleJpaRepository
該類是JpaRepository接口的具體實(shí)現(xiàn),CRUD操作就是由該類提供的。包括四個(gè)成員變量JpaEntityInformation、PersistenceProvider、CrudMethodMetadata、EntityManager。 前三個(gè)成員變量是為了獲取拼接sql,EntityManager執(zhí)行該sql。相當(dāng)于session、sqlSession等
寫一個(gè)Repository繼承JpaRepository之后
可以寫其實(shí)現(xiàn)類,但是不需要implements關(guān)鍵字去實(shí)現(xiàn),spring-data-jpa會(huì)自動(dòng)識(shí)別其關(guān)系,也可以不寫實(shí)現(xiàn)類,在運(yùn)行時(shí)期,SimpleJpaRepository該類就是其實(shí)現(xiàn)類,如果寫了自定義實(shí)現(xiàn)類,就會(huì)執(zhí)行實(shí)現(xiàn)類中的邏輯
spring-data-jpa的相關(guān)語法
對(duì)于多表查詢 需要用到Specification匿名內(nèi)部類,重寫其方法。感覺遇到多表查詢還是寫sql比較直觀
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
用Java實(shí)現(xiàn)全國(guó)天氣預(yù)報(bào)的api接口調(diào)用示例
查詢天氣預(yù)報(bào)在APP中常用的一個(gè)常用功能,本文實(shí)例講述了java調(diào)用中國(guó)天氣網(wǎng)api獲得天氣預(yù)報(bào)信息的方法。分享給大家供大家參考。2016-10-10java基于jdbc連接mysql數(shù)據(jù)庫功能實(shí)例詳解
這篇文章主要介紹了java基于jdbc連接mysql數(shù)據(jù)庫功能,結(jié)合實(shí)例形式詳細(xì)分析了jdbc連接mysql數(shù)據(jù)庫的原理、步驟、實(shí)現(xiàn)方法及相關(guān)操作技巧,需要的朋友可以參考下2017-10-10SpringBoot設(shè)置接口超時(shí)的方法小結(jié)
這篇文章主要介紹了SpringBoot設(shè)置接口超時(shí)的方法小結(jié),包括配置文件,config配置類及相關(guān)示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09Jmeter對(duì)接口測(cè)試入?yún)?shí)現(xiàn)MD5加密
這篇文章主要介紹了Jmeter對(duì)接口測(cè)試入?yún)?shí)現(xiàn)MD5加密,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Java實(shí)現(xiàn)多叉樹和二叉樹之間的互轉(zhuǎn)
本文主要介紹了Java實(shí)現(xiàn)多叉樹和二叉樹之間的互轉(zhuǎn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05SpringBoot自動(dòng)配置的實(shí)現(xiàn)原理
這篇文章主要介紹了詳解SpringBoot自動(dòng)配置原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01Java httpClient連接池支持多線程高并發(fā)的實(shí)現(xiàn)
本文主要介紹了Java httpClient連接池支持多線程高并發(fā)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08springboot集成JWT實(shí)現(xiàn)身份認(rèn)證(權(quán)鑒)的方法步驟
本文主要介紹了springboot集成JWT實(shí)現(xiàn)身份認(rèn)證(權(quán)鑒)的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04JavaWeb實(shí)現(xiàn)mysql數(shù)據(jù)庫數(shù)據(jù)的添加和刪除
這篇文章主要介紹了如何利用JavaWeb實(shí)現(xiàn)mysql數(shù)據(jù)庫數(shù)據(jù)的添加和刪除功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03