mybatis-plus QueryWrapper自定義查詢條件的實現(xiàn)
mybatis-plus框架功能很強(qiáng)大,把很多功能都集成了,比如自動生成代碼結(jié)構(gòu),mybatis crud封裝,分頁,動態(tài)數(shù)據(jù)源等等,附上官網(wǎng)鏈接https://mp.baomidou.com/,github上有代碼例子,國內(nèi)小伙伴推薦碼云https://gitee.com/baomidou/mybatis-plus。
但是,其中還是有些小坑,文檔也沒有涉及的很全面,碰到問題,百度或者發(fā)issue,能力強(qiáng)的還是直接看源碼好,一切答案都在源碼中。
版本推薦用3.1.0,3.1.1及以上版本有bug,訪問mapper接口的時候,會把數(shù)據(jù)庫date類型轉(zhuǎn)換為localDateTime,報錯java.sql.SQLFeatureNotSupportedException
解決方案可以參考 http://www.dbjr.com.cn/article/193995.htm
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version> </dependency>
mybatis-plus里有個類QueryWrapper,封裝sql對象,包括where條件,order by排序,select哪些字段等等。該類的具體用法,網(wǎng)上教程很多。
這里有個需求,通過前端提交查詢條件,后臺動態(tài)拼接成where的sql語句,用于查詢。常規(guī)做法是前端提交一堆查詢參數(shù),controller層用一個對象接收,然后在mybatis的xml里對該對象里的各種屬性做判斷
<select id="test"> select * from test <where> <if test=" name != null and name != '' "> and name=#{name} </if> ... </where> </select>
這有個問題是具體字段連接類型就有很多,like,=,>,<等等。當(dāng)然要實現(xiàn)功能有很多種方式,mybatis-plus的QueryWrapper很強(qiáng)大,可以通過對象的方式進(jìn)行查詢操作,但是不同的頁面都自己管自己,效率低下,會存在大量重復(fù)代碼。所以我就想自己封裝一套,從前端的查詢條件傳固定格式的參數(shù),到后臺進(jìn)行轉(zhuǎn)換,自動拼接成對應(yīng)的where sql語句,再傳到mybatis xml里進(jìn)行動態(tài)查詢。這樣所有頁面就可以統(tǒng)一,便于操作。下面進(jìn)入正題:
前端
前端用的技術(shù)是html+jquery,jquery操作dom做各種操作。html就僅僅是樣式展現(xiàn),不涉及任何的邏輯代碼,沒有使用vue之類的mvvm框架,也沒有使用thymeleaf之類的模板引擎,其實這些都會在html嵌入污染代碼,導(dǎo)致美工修改頁面樣式的時候一臉蒙蔽。html就是純的html+css,通過jquery來完成剩余的工作。
index.html
<form id="myform"> <input name="name"/> <input name="age"/> <input name="startdate"/> <input name="enddate"/> </form>
jquery發(fā)起post請求,拼接的參數(shù)如下:
var searchParam = [ {column: "COLUMN_NAME",type: "like", value: "tim"}, {column: "COLUMN_AGE",type: "eq", value: "22"}, {column: "COLUMN_DATE",type: "ge", value: "2019-08-16 00:00:00"}, {column: "COLUMN_DATE",type: "le", value: "2019-08-16 23:59:59"} ];
其中column值 為數(shù)據(jù)表的字段名;type值為sql字段拼接的方式,規(guī)則可自己定制;value就是字段值了;目標(biāo)拼接成的sql語句如下:
COLUMN_NAME like '%tim%' and COLUMN_AGE=22 and COLUMN_DATE>='2019-08-16 00:00:00' and COLUMN_DATE<='2019-08-16 23:59:59'
jquery發(fā)起post請求:
$.ajax({ url: "list", type: "post", data: {pageNum:1,pageSize:20,condition:JSON.stringify(searchParam),...(根據(jù)需要自己加請求參數(shù))} success: function(result){...} });
說明:請求參數(shù)condition為什么要傳json字符串后續(xù)有介紹。
后端
Controller
controller接收前端發(fā)過來的參數(shù),碰到一個問題,在有多個請求參數(shù)的情況下,如何接收 集合對象 請求參數(shù)?使用了很多方法都不行,后續(xù)有空再研究下,目前使用的方法簡單粗暴,就上傳json字符串,后端轉(zhuǎn)換成對象。
Controller:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @RequestMapping(value = "/list", method = RequestMethod.POST) public Object getTestList(@RequestParam(name = "pageNum", required = false, defaultValue = "1") int pageNum, @RequestParam(name = "pageSize", required = false, defaultValue = "15") int pageSize, @RequestParam(name = "condition",required = false) String conditionJson) { QueryWrapper queryWrapper = SearchUtil.parseWhereSql(conditionJson); queryWrapper.orderByDesc("CREATE_DATE"); return service.getPageTestList(queryWrapper,pageNum,pageSize); }
SearchUtil:
public static QueryWrapper parseWhereSql(String conditionJson){ QueryWrapper queryWrapper = new QueryWrapper(); if(StrUtil.isNotEmpty(conditionJson)){ List<ConditionVo> conditionList = JSON.parseArray(conditionJson,ConditionVo.class); if(CollUtil.isNotEmpty(conditionList)){ for(ConditionVo conditionVo : conditionList){ switch (conditionVo.getType()){ case "eq": queryWrapper.eq(conditionVo.getColumn(),conditionVo.getValue());break; case "ne": queryWrapper.ne(conditionVo.getColumn(),conditionVo.getValue());break; case "like": queryWrapper.like(conditionVo.getColumn(),conditionVo.getValue());break; case "leftlike": queryWrapper.likeLeft(conditionVo.getColumn(),conditionVo.getValue());break; case "rightlike": queryWrapper.likeRight(conditionVo.getColumn(),conditionVo.getValue());break; case "notlike": queryWrapper.notLike(conditionVo.getColumn(),conditionVo.getValue());break; case "gt": queryWrapper.gt(conditionVo.getColumn(),conditionVo.getValue());break; case "lt": queryWrapper.lt(conditionVo.getColumn(),conditionVo.getValue());break; case "ge": queryWrapper.ge(conditionVo.getColumn(),conditionVo.getValue());break; case "le": queryWrapper.le(conditionVo.getColumn(),conditionVo.getValue());break; } } } } return queryWrapper; }
該類是重點,根據(jù)type不同的值進(jìn)行組合,queryWrapper包含了很多拼接方法,可以看文檔。這里只寫了一些常用的拼接方法。
ConditionVo:
@Data public class ConditionVo implements Serializable { private static final long serialVersionUID = -5099378457111419832L; /** * 數(shù)據(jù)庫字段名 */ private String column; /** * 字段值 */ private String value; /** * 連接類型,如llike,equals,gt,ge,lt,le */ private String type; }
拿到queryWrapper對象就是重點了,之后就是sql操作了。
Service
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @Override public IPage<ListVo> getPageEntityList(QueryWrapper queryWrapper, int pageNum, int pageSize) { Page<ListVo> page = new Page<>(pageNum,pageSize); IPage<ListVo> list = page(page,queryWrapper); return list; } @Override public IPage<ListVo> getPageTestList(QueryWrapper queryWrapper, int pageNum, int pageSize) { Page<ListVo> page = new Page<>(pageNum,pageSize); IPage<ListVo> list = mapper.getPageTestList(page,queryWrapper); return list; }
上面的第一個方法getPageEntityList使用的是mybatis-plus自帶的page(page,queryWrapper)方法,具體使用方法可以查看官方文檔。使用該方法就不需要自己寫sql語句了。本篇文章重點是下面那個方法getPageTestList。
mybatis-plus自帶了強(qiáng)大的翻頁功能,只需往mapper方法里傳入一個Page類,該類實現(xiàn)了IPage接口。
這里有個坑,通過看源碼發(fā)現(xiàn),mapper方法的參數(shù)有順序要求:page對象一定要放在第一個參數(shù),否則翻頁查詢會報錯。源碼在com.baomidou.mybatisplus.core.override.MybatisMapperMethod中public Object execute(SqlSession sqlSession, Object[] args)方法的case SELECT判斷里,因反編譯后的行數(shù)可能不同,所以貼上具體是哪個方法,我這邊反編譯后在68行。
ListVo是自己的業(yè)務(wù)對象。
Mapper
import com.baomidou.mybatisplus.core.toolkit.Constants; IPage<ListVo> getPageTestList(Page<ListVo> page,@Param(Constants.WRAPPER) Wrapper query);
大家可以進(jìn)Constans這個接口看下源碼都有哪些值,后續(xù)xml要用到。
XML
<select id="getPageTestList" resultType="xx.xx.xx.ListVo"> select * from test <if test="ew.emptyOfWhere == false"> ${ew.customSqlSegment} </if> </select>
這里先強(qiáng)調(diào)一點,${ew.customSqlSegment}是用美元符號$,而不是#。
這里的ew是啥?其實就是mapper方法里的@Param(Constants.WRAPPER) Wrapper query對象,Constants.WRAPPER的值就是ew。
首先判斷ew.emptyOfWhere是否存在where條件,有的話再拼接上去。ew里還有個屬性nonEmptyOfWhere,看單詞應(yīng)該跟emptyOfWhere的值相反,但是在xml中使用卻提示不存在,不知道為什么,又是一個地雷?
ew.customSqlSegment又是啥,該值是WHERE + sql語句,還有個ew.sqlSegment是不包括WHERE字符串。大家可以在service層輸出queryWrapper里面的相關(guān)方法:
log.info(queryWrapper.isEmptyOfWhere()+""); log.info(queryWrapper.getCustomSqlSegment()); log.info(queryWrapper.getSqlSegment()); log.info(queryWrapper.getParamNameValuePairs().toString());
輸出如下:
getCustomSqlSegment()
WHERE COLUMN_NAME LIKE #{ew.paramNameValuePairs.MPGENVAL1}
getSqlSegment()
COLUMN_NAME LIKE #{ew.paramNameValuePairs.MPGENVAL1}
getParamNameValuePairs()
{MPGENVAL1=%tim%}
看到這,mybatis框架用的熟練的小伙伴們應(yīng)該就懂了吧,這樣就可以避免sql注入的問題。
這里又有 一個小坑,就是order by排序。傳入了page參數(shù),mybatis-plus底層就會幫你翻頁查詢,會查詢總數(shù)量。通過輸出的sql日志可以發(fā)現(xiàn),其實框架是在你的sql基礎(chǔ)上外面再套一層select count(1) from。這里會有個問題,本人用的數(shù)據(jù)庫是sqlserver,如果在count查詢語句里用了order by就會出錯,解決方法是調(diào)用queryWrapper對象中的排序方法,如:queryWrapper.orderByDesc("CREATE_DATE"),xml中就不要用order by。所以我在controller層用了這個方法,這樣mybatis-plus底層會合理地進(jìn)行查詢。
總結(jié)
通過上面這種封裝方式,就不需要在xml里面做一大堆的where條件if判斷來拼接sql。
mybatis-plus框架功能很強(qiáng)大,且還在維護(hù)中,有空可以仔細(xì)閱讀下文檔、官方例子,能力強(qiáng)的可以直接看源碼,一切答案都在源碼中。
到此這篇關(guān)于mybatis-plus QueryWrapper自定義查詢條件的實現(xiàn)的文章就介紹到這了,更多相關(guān)mybatis-plus QueryWrapper查詢條件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MybatisPlus使用Wrapper實現(xiàn)條件查詢功能
- mybatis-plus自帶QueryWrapper自定義sql實現(xiàn)復(fù)雜查詢實例詳解
- Mybatis-plus動態(tài)條件查詢QueryWrapper的使用案例
- MyBatisPlus-QueryWrapper多條件查詢及修改方式
- mybatisplus where QueryWrapper加括號嵌套查詢方式
- mybatisplus如何在xml的連表查詢中使用queryWrapper
- MybatisPlus使用queryWrapper如何實現(xiàn)復(fù)雜查詢
- MyBatis-Plus實現(xiàn)2種分頁方法(QueryWrapper查詢分頁和SQL查詢分頁)
- 詳解MyBatis-Plus Wrapper條件構(gòu)造器查詢大全
- Mybatis Plus Wrapper查詢某幾列的方法實現(xiàn)
相關(guān)文章
Java排序之Comparable和Comparator比較器詳解
這篇文章主要介紹了Java排序之Comparable和Comparator比較器詳解,Comparable<T>是內(nèi)部比較器,Comparator<T>是外部比較器,最推薦使用Comparator<T>接口排序,Comparator提供靜態(tài)方法很方便,推薦使用,需要的朋友可以參考下2024-01-01idea快速找到項目中對應(yīng)的類圖文詳解(包括源碼)
用IDEA開發(fā)Java項目時經(jīng)常會使用到各種快捷鍵,其中搜索是最常用的之一,下面這篇文章主要給大家介紹了關(guān)于idea如何快速找到項目中對應(yīng)的類(包括源碼)的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06將Swagger2文檔導(dǎo)出為HTML或markdown等格式離線閱讀解析
這篇文章主要介紹了將Swagger2文檔導(dǎo)出為HTML或markdown等格式離線閱讀,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11基于Java實現(xiàn)一個高效可伸縮的計算結(jié)果緩存
這篇文章將通過對一個計算結(jié)果緩存的設(shè)計迭代介紹,分析每個版本的并發(fā)缺陷,并分析如何修復(fù)這些缺陷,最終完成一個高效可伸縮的計算結(jié)果緩存,感興趣的小伙伴可以了解一下2023-06-06解讀Jvm的內(nèi)存結(jié)構(gòu)與GC及jvm參數(shù)調(diào)優(yōu)
這篇文章主要介紹了解讀Jvm的內(nèi)存結(jié)構(gòu)與GC及jvm參數(shù)調(diào)優(yōu)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05詳細(xì)談?wù)凧ava中l(wèi)ong和double的原子性
原子性是指一個操作或多個操作要么全部執(zhí)行,且執(zhí)行的過程不會被任何因素打斷,要么就都不執(zhí)行,下面這篇文章主要給大家介紹了關(guān)于Java中l(wèi)ong和double原子性的相關(guān)資料,需要的朋友可以參考下2021-08-08詳解Java List的擴(kuò)容機(jī)制原理及應(yīng)用
在Java中,List是一種非常常用的數(shù)據(jù)結(jié)構(gòu),用于存儲有序的元素集合,本文將分析Java List的擴(kuò)容機(jī)制原理,并通過示例代碼和測試代碼來加強(qiáng)闡述內(nèi)容,具有一定的參考價值,感興趣的可以了解一下2023-08-08