mybatis-plus框架功能很強大,把很多功能都集成了,比如自動生成代碼結構,mybatis crud封裝,分頁,動態數據源等等,附上官網鏈接https://mp.baomidou.com/,github上有代碼例子,國內小伙伴推薦碼云https://gitee.com/baomidou/mybatis-plus。
但是,其中還是有些小坑,文檔也沒有涉及的很全面,碰到問題,百度或者發issue,能力強的還是直接看源碼好,一切答案都在源碼中。
版本推薦用3.1.0,3.1.1及以上版本有bug,訪問mapper接口的時候,會把數據庫date類型轉換為localDateTime,報錯java.sql.SQLFeatureNotSupportedException
解決方案可以參考 http://www.ythuaji.com.cn/article/97440.html
1
2
3
4
5
|
< 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哪些字段等等。該類的具體用法,網上教程很多。
這里有個需求,通過前端提交查詢條件,后臺動態拼接成where的sql語句,用于查詢。常規做法是前端提交一堆查詢參數,controller層用一個對象接收,然后在mybatis的xml里對該對象里的各種屬性做判斷
1
2
3
4
5
6
7
8
9
|
< select id = "test" > select * from test < where > < if test = " name != null and name != '' " > and name=#{name} </ if > ... </ where > </ select > |
這有個問題是具體字段連接類型就有很多,like,=,>,<等等。當然要實現功能有很多種方式,mybatis-plus的QueryWrapper很強大,可以通過對象的方式進行查詢操作,但是不同的頁面都自己管自己,效率低下,會存在大量重復代碼。所以我就想自己封裝一套,從前端的查詢條件傳固定格式的參數,到后臺進行轉換,自動拼接成對應的where sql語句,再傳到mybatis xml里進行動態查詢。這樣所有頁面就可以統一,便于操作。下面進入正題:
前端
前端用的技術是html+jquery,jquery操作dom做各種操作。html就僅僅是樣式展現,不涉及任何的邏輯代碼,沒有使用vue之類的mvvm框架,也沒有使用thymeleaf之類的模板引擎,其實這些都會在html嵌入污染代碼,導致美工修改頁面樣式的時候一臉蒙蔽。html就是純的html+css,通過jquery來完成剩余的工作。
index.html
1
2
3
4
5
6
|
< form id = "myform" > < input name = "name" /> < input name = "age" /> < input name = "startdate" /> < input name = "enddate" /> </ form > |
jquery發起post請求,拼接的參數如下:
1
2
3
4
5
6
|
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值 為數據表的字段名;type值為sql字段拼接的方式,規則可自己定制;value就是字段值了;目標拼接成的sql語句如下:
1
|
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發起post請求:
1
2
3
4
5
6
|
$.ajax({ url: "list" , type: "post" , data: {pageNum:1,pageSize:20,condition:JSON.stringify(searchParam),...(根據需要自己加請求參數)} success: function (result){...} }); |
說明:請求參數condition為什么要傳json字符串后續有介紹。
后端
Controller
controller接收前端發過來的參數,碰到一個問題,在有多個請求參數的情況下,如何接收 集合對象 請求參數?使用了很多方法都不行,后續有空再研究下,目前使用的方法簡單粗暴,就上傳json字符串,后端轉換成對象。
Controller:
1
2
3
4
5
6
7
8
9
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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; } |
該類是重點,根據type不同的值進行組合,queryWrapper包含了很多拼接方法,可以看文檔。這里只寫了一些常用的拼接方法。
ConditionVo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Data public class ConditionVo implements Serializable { private static final long serialVersionUID = -5099378457111419832L; /** * 數據庫字段名 */ private String column; /** * 字段值 */ private String value; /** * 連接類型,如llike,equals,gt,ge,lt,le */ private String type; } |
拿到queryWrapper對象就是重點了,之后就是sql操作了。
Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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自帶了強大的翻頁功能,只需往mapper方法里傳入一個Page類,該類實現了IPage接口。
這里有個坑,通過看源碼發現,mapper方法的參數有順序要求:page對象一定要放在第一個參數,否則翻頁查詢會報錯。源碼在com.baomidou.mybatisplus.core.override.MybatisMapperMethod中public Object execute(SqlSession sqlSession, Object[] args)方法的case SELECT判斷里,因反編譯后的行數可能不同,所以貼上具體是哪個方法,我這邊反編譯后在68行。
ListVo是自己的業務對象。
Mapper
1
2
|
import com.baomidou.mybatisplus.core.toolkit.Constants; IPage<ListVo> getPageTestList(Page<ListVo> page, @Param (Constants.WRAPPER) Wrapper query); |
大家可以進Constans這個接口看下源碼都有哪些值,后續xml要用到。
XML
1
2
3
4
5
6
|
< select id = "getPageTestList" resultType = "xx.xx.xx.ListVo" > select * from test < if test = "ew.emptyOfWhere == false" > ${ew.customSqlSegment} </ if > </ select > |
這里先強調一點,${ew.customSqlSegment}是用美元符號$,而不是#。
這里的ew是啥?其實就是mapper方法里的@Param(Constants.WRAPPER) Wrapper query對象,Constants.WRAPPER的值就是ew。
首先判斷ew.emptyOfWhere是否存在where條件,有的話再拼接上去。ew里還有個屬性nonEmptyOfWhere,看單詞應該跟emptyOfWhere的值相反,但是在xml中使用卻提示不存在,不知道為什么,又是一個地雷?
ew.customSqlSegment又是啥,該值是WHERE + sql語句,還有個ew.sqlSegment是不包括WHERE字符串。大家可以在service層輸出queryWrapper里面的相關方法:
1
2
3
4
|
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框架用的熟練的小伙伴們應該就懂了吧,這樣就可以避免sql注入的問題。
這里又有 一個小坑,就是order by排序。傳入了page參數,mybatis-plus底層就會幫你翻頁查詢,會查詢總數量。通過輸出的sql日志可以發現,其實框架是在你的sql基礎上外面再套一層select count(1) from。這里會有個問題,本人用的數據庫是sqlserver,如果在count查詢語句里用了order by就會出錯,解決方法是調用queryWrapper對象中的排序方法,如:queryWrapper.orderByDesc("CREATE_DATE"),xml中就不要用order by。所以我在controller層用了這個方法,這樣mybatis-plus底層會合理地進行查詢。
總結
通過上面這種封裝方式,就不需要在xml里面做一大堆的where條件if判斷來拼接sql。
mybatis-plus框架功能很強大,且還在維護中,有空可以仔細閱讀下文檔、官方例子,能力強的可以直接看源碼,一切答案都在源碼中。
到此這篇關于mybatis-plus QueryWrapper自定義查詢條件的實現的文章就介紹到這了,更多相關mybatis-plus QueryWrapper查詢條件內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/l1006841574/article/details/99670600