mybatis-plusQueryWrapper⾃定义查询条件
mybatis-plus框架功能很强⼤,把很多功能都集成了,⽐如⾃动⽣成代码结构,mybatis crud封装,分页,动态数据源等等,附上官⽹链接mp.baomidou/,github上有代码例⼦,国内⼩伙伴推荐码云gitee/baomidou/mybatis-plus。
但是,其中还是有些⼩坑,⽂档也没有涉及的很全⾯,碰到问题,百度或者发issue,能⼒强的还是直接看源码好,⼀切答案都在源码中。
版本推荐⽤3.1.0,3.1.1及以上版本有bug,访问mapper接⼝的时候,会把数据库date类型转换为localDateTime,报
错java.sql.SQLFeatureNotSupportedException
解决⽅案可以参考 blog.csdn/lkh1992/article/details/90024829
<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⾥对该对象⾥的各种属性做判断
<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之类的模板引擎,其实这些都会在htm
l嵌⼊污染代码,导致美⼯修改页⾯样式的时候⼀脸蒙蔽。html就是纯的html+css,通过jquery来完成剩余的⼯作。
index.html
<form id="myform">
<input name="name"/>
<input name="age"/>
<input name="startdate"/>
<input name="enddate"/>
</form>
jquery发起post请求,拼接的参数如下:
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语句如下: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请求:
$.ajax({
url:"list",
type:"post",
data:{pageNum:1,pageSize:20,condition:JSON.stringify(searchParam),...(根据需要⾃⼰加请求参数)}
success:function(result){...}
});
说明:请求参数condition为什么要传json字符串后续有介绍。
后端
Controller
controller接收前端发过来的参数,碰到⼀个问题,在有多个请求参数的情况下,如何接收 集合对象 请求参数?使⽤了很多⽅法都不⾏,后续有空再研究下,⽬前使⽤的⽅法简单粗暴,就上传json字符串,后端转换成对象。
Controller:
import ditions.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);
PageTestList(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){
Type()){
case"eq": queryWrapper.Column(),Value());break;
case"ne": (Column(),Value());break;
case"like": queryWrapper.Column(),Value());break;
case"leftlike": queryWrapper.Column(),Value());break;
case"rightlike": queryWrapper.Column(),Value());break;
case"notlike": Column(),Value());break;
case"gt": (Column(),Value());break;
case"lt": queryWrapper.Column(),Value());break;
case"ge": (Column(),Value());break;
case"le": queryWrapper.Column(),Value());break;
}
}
}
}
return queryWrapper;
}
该类是重点,根据type不同的值进⾏组合,queryWrapper包含了很多拼接⽅法,可以看⽂档。这⾥只写了⼀些常⽤的拼接⽅法。ConditionVo:
@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
import adata.IPage;
import sion.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 = PageTestList(page,queryWrapper);
return list;
}
上⾯的第⼀个⽅法getPageEntityList使⽤的是mybatis-plus⾃带的page(page,queryWrapper)⽅法,具体使⽤⽅法可以查看官⽅⽂档。使⽤该⽅法就不需要⾃⼰写sql语句了。本篇⽂章重点是下⾯那个⽅法getPageTestList。
mybatis-plus⾃带了强⼤的翻页功能,只需往mapper⽅法⾥传⼊⼀个Page类,该类实现了IPage接⼝。
这⾥有个坑,通过看源码发现,mapper⽅法的参数有顺序要求:page对象⼀定要放在第⼀个参数,否则翻页查询会报错。源码
在verride.MybatisMapperMethod中public Object execute(SqlSession sqlSession, Object[] args)⽅法的case SELECT判断⾥,因反编译后的⾏数可能不同,所以贴上具体是哪个⽅法,我这边反编译后在68⾏。
ListVo是⾃⼰的业务对象。
thymeMapper
import lkit.Constants;
IPage<ListVo>getPageTestList(Page<ListVo> page,@Param(Constants.WRAPPER) Wrapper query);
⼤家可以进Constans这个接⼝看下源码都有哪些值,后续xml要⽤到。
XML
<select id="getPageTestList"resultType="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⾥⾯的相关⽅法:
log.info(queryWrapper.isEmptyOfWhere()+"");
log.CustomSqlSegment());
log.SqlSegment());
log.ParamNameValuePairs().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对象中的排序⽅法,如:derByDesc("CREATE_DATE"),xml中就不要⽤order by。所以我在controller层⽤了这个⽅法,这样mybatis-plus底层会合理地进⾏查询。
总结
通过上⾯这种封装⽅式,就不需要在xml⾥⾯做⼀⼤堆的where条件if判断来拼接sql。
mybatis-plus框架功能很强⼤,且还在维护中,有空可以仔细阅读下⽂档、官⽅例⼦,能⼒强的可以直接看源码,⼀切答案都在源码中。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论