博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis Plus使用技巧和实践(Mybatis-plus官方文档的补充)
阅读量:2144 次
发布时间:2019-04-30

本文共 7801 字,大约阅读时间需要 26 分钟。

概述

上一篇介绍了Mybatis-Plus(简称MP)的使用介绍,包括了代码生成器、CRUD接口,分页插件、逻辑删除、乐观锁等。此篇是上一篇的衔接,如果没有看过请先看。今天不讲MP的API、CRUD接口,主要介绍下实践技巧官方文档的扩展说明,相信熟悉了这篇文章后,你将对MP的使用将会更熟练。理解有限,有错误请指正。

一、注解的使用

@TableName-数据库表名称和实体关系配置注解 -- 放在类上

属性 类型 必须指定 默认值 描述
value String "" 数据库的表名
schema String "" schema(@since 3.1.1)数据库库名
keepGlobalPrefix boolean false 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)(@since 3.1.1)-一般不用
resultMap String "" xml 中 resultMap 的 id
autoResultMap boolean false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)(@since 3.1.2)

@TableId-数据库主键字段 -- 放在成员变量上,使用在数据库主键值不是"id"的情况,比如主键是“user_id”,可以@TableId("user_id")

属性 类型 必须指定 默认值 描述
value String "" 主键字段名
type Enum IdType.NONE 主键类型

 

@TableField -(此注解不能和@TableId同时使用)- 处理数据库字段和实体不对应情况,及如果字段是数据库关键字如‘level’,‘status’等,可以改成这样@TableField(value = "`level`")

属性 类型 必须指定 默认值 描述
value String "" 字段名
el String "" 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... }部分
exist boolean true 是否为数据库表字段,如果为false则为:排除非表字段,实体向数据库插入或更新时不会拼接此字段。
condition String "" 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},默认条件是根据参数做“等值”查询,一般都是等值查询,如果不是请
update String "" 字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性)
strategy Enum FieldStrategy.DEFAULT 字段验证策略 3.1.2+使用下面3个替代
insertStrategy Enum N DEFAULT(因为DEFAULE是默认参考全局配置,全局配置默认是NOT_NULl),说明:就是在插入时是否对null字段添加到insert to table 的字段中去。 举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) (since v_3.1.2)
updateStrategy Enum N DEFAULT 举例:IGNORED: update table_a set column=#{columnProperty} (since v_3.1.2)
whereStrategy Enum N DEFAULT 举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> (since v_3.1.2)
fill Enum FieldFill.DEFAULT 字段自动填充策略
select boolean true 是否进行 select 查询
keepGlobalFormat boolean false 是否保持使用全局的 format 进行处理(@since 3.1.1)

这里着重说明一下fill属性的使用(自动填充-默认不填充) 。自动填充就是对字段在insert或update中不用自己设置值,MP自动设置其默认值,不过默认值需要自己设置,常常用在createTimeupdateTime的字段上,createTime一般使用INSERT策略,updateTime一般使用INSERT_UPDATE策略。使用分两步。

第一步:在实体字段上添加@TableFiedl(fill = FiedlFill.填充策略),填充策略有4种如下:

描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段
@TableField(fill = FieldFill.INSERT) // 创建时间只在添加是填充private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE) // 更新时间在添加和更新时都填充private Date updateTime;

第二步:自定义类实现MetaObjectHandler类即可,重写填充策略的两个方法用于insertupdate,关键的是setFieldValByName()方法,第一个参数是实体的属性,第二个参数是设置的默认值

@Componentpublic class MetaObjectHandlerConfig implements MetaObjectHandler {    @Override    public void insertFill(MetaObject metaObject) {        setFieldValByName("createTime", new Date(), metaObject);        setFieldValByName("updateTime", new Date(), metaObject);    }    @Override    public void updateFill(MetaObject metaObject) {        this.setFieldValByName("updateTime",new Date(), metaObject);    }}

这样在实体插入或更新的时候就不用每次都设置像createTime这种字段的值,委托给MP来自动填充了,是不是很厉害。

二、使用实践

2.1 强大的条件构造器

2.1.1 条件构造器中condition的使用

条件构造器所有条件有个重载的方法,第一个入参boolean condition(表示该条件是否加入最后生成的sql中,默认true)。

场景:Controller中由前端传递过来的查询条件,比如查询所有列表信息,如果前端传递过来个name值,表示通过name进行筛选,当name传非空值时才添加查询条件,正常情况下我们可能会这样使用:

@GetMapping("/list")public Object index(@RequestParam(value = "name", required = false) String name){    QueryWrapper
qw = new QueryWrapper<>(); if (StringUtils.isNoneBlank(name)) { qw.eq("name", name); } return customerService.list(qw);}

如果筛选的条件比较多,那代码就不好看了。MP提供了查询时根据条件来判断,决定是否加入查询条件语句的方法,就说是condition为true则加入后面的条件,false则不会加入条件

public Object index(@RequestParam(value = "name", required = false) String name){    QueryWrapper
qw = new QueryWrapper<>(); qw.eq(StringUtils.isNoneBlank(name), "name", name); return customerService.list(qw);}

2.1.2 仅查询数据库部分字段

当我们使用条件构造器查询数据库时,默认查询所有实体字段,如果只需要id和name两个字段的值,使用select(String column...)方法可以完成,执行查询的sql如(select id, name from user where xxxx)。

注意:返回的值依然是实体对象,只是除了id和name其他都是null

QueryWrapper
qw = new QueryWrapper<>();qw.select("id", "name");List
list = customerService.list(qw);

一般条件构造器添加了select字段后,使用普通的list()方法查询出实体就没有必要了,因为带着一堆null的值很不爽,实践是查询方法使用listMap()来查询,这样会根据查询语句中select的具体字段来赋值给map,如下示例中map只有id和name两个key了。

QueryWrapper
qw = new QueryWrapper<>();qw.select("id", "name");List
> list = customerService.listMaps(qw);return list;

2.1.3 告别“条件构造器”使用"Lambda条件构造器"

使用LambdaQueryWrapper条件构造器和QueryWrapper构造器相比方法几乎相同,唯一不同的就是传递条件的表字段不是通过手敲的方式而是使用lambda的形式来完成获取表字段。这样就不用担心将 “name”写成“nema”了,例子中使用方法引用来获取name的字段。

三种创建LambdaQueryWrapper方式,个人推荐第二种,只在普通QueryWrapper构造器前面加上Lambda即可完成构造:

LambdaQueryWrapper
lambda = new QueryWrapper
().lambda();LambdaQueryWrapper
lambda2 = new LambdaQueryWrapper<>();LambdaQueryWrapper
lambda3 = Wrappers.lambdaQuery();
LambdaQueryWrapper
lqw = new LambdaQueryWrapper<>();lqw.eq(Customer::getName, "Tom"); // 表字段使用lambda的方法引用来获取List
> list = customerService.listMaps(lqw);

当然LambdaQueryWrapper的select()中的查询字段也需要使用lambda来获取字段名称

2.2 万剑归宗的查询LambdaQueryChainWrapper(链式条件构造器查询)

前面的查询都是创建一个条件构造器,再通过mapper或service以条件构造器为参数进行查询。LambdaQueryChainWrapper将前面的两句合二为一,一句来完成带条件的查询。使用起来就是:MP简直想起飞。

@Autowiredprivate CustomerMapper customerMapper;@Testpublic void contextLoads() {    List
list = new LambdaQueryChainWrapper
(customerMapper) .select(Customer::getId, Customer::getName) .like(Customer::getName, "王").list(); System.out.println(list);}

创建LambdaQueryChainWrapper对象需要传递mapper对象,虽然酒好不能贪杯,请酌情使用链式查询。还有个缺点链式查询只有list()和one()两个方法获取数据,返回的都是实体对象或实体对象的集合,没有对应的Map()查询方法。

2.3 自定义SQL

项目中自己写sql也是在所难免的,可这么强大的条件构造器我们也想使用怎么办呢,别着急MP也帮我们想到了。两处注意事项:

第一处:在自己的mapper接口文件中添加自己的查询方法,只需要传递@Param(Constants.WRAPPER) Wrapper wrapper条件构造器参数即可,MP会自动赋值给wrapper。

List
getMyself(@Param(Constants.WRAPPER) Wrapper wrapper);

第二处:在xml文件中,使用(固定格式)${ew.customSqlSegment}作为参数放在表名的后面就可以了。

三、两款用于开发环境的sql打印、分析功能的插件

3.1 执行 SQL 分析打印插件(第三方插件)

该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长,对于开发时研究sql语句很有用途。

3.1.1 添加依赖

p6spy
p6spy
3.8.1

3.1.2 修改数据库连接配置为p6spy的配置

只需要改动driverurl即可,如下配置

spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriverspring.datasource.url=jdbc:p6spy:mysql://127.0.0.1:3306/database?useUnicode=true&characterEncoding=utf-8

3.1.3 添加p6spy的配置文件spy.properties

module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory# 自定义日志打印logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger#日志输出到控制台appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger# 使用日志系统记录 sql#appender=com.p6spy.engine.spy.appender.Slf4JLogger# 设置 p6spy driver 代理deregisterdrivers=true# 取消JDBC URL前缀useprefix=true# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.excludecategories=info,debug,result,batch,resultset# 日期格式dateformat=yyyy-MM-dd HH:mm:ss# 实际驱动可多个#driverlist=org.h2.Driver# 是否开启慢SQL记录outagedetection=true# 慢SQL记录标准 2 秒outagedetectioninterval=2

3.1.4 运行sql语句,控制台查看,则会有sql运行语句消耗时间信息,如下图:sql消耗了37ms

3.2 MP自带sql性能分析插件

MP自带了sql性能分析拦截器,用于输出每条 SQL 语句及其执行时间,只需要定义一个bean就可以用来做性能分析。如果对 SQL 的打印效果要求较高,请使用上面提到的第三方扩展 执行 SQL 分析打印 功能。

3.2.1 配置性能分析

  • spring boot方式配置(推荐)
  • @Bean@Profile({"dev","test"})// 设置 dev test 环境开启public PerformanceInterceptor performanceInterceptor() {   return new PerformanceInterceptor();}
  • spring mvc方式配置

3.2.1 运行sql语句,控制台查看,则会有sql运行语句消耗时间信息,如下图:sql消耗了82ms

四、总结

通过这两篇文章,已经将MP的大部分功能都说明的差不多了,真的感谢MP的开发者们,写出这么好的东西,核心功能和各种插件都将DAO层的东西写尽了,再结合Spring Boot的强大,真的是开箱就开枪,让我们开发者可以开开心心的写业务。学会了MP后,感觉对后台业务操作的简化程度有了质的提高。

花了一天的时间边测试,边写文章,如果有错误的地方,请指正。希望对你理解Mybatis-Plus有更深的认识。

(完) 

转载地址:http://ahhgf.baihongyu.com/

你可能感兴趣的文章
linux放音乐cd
查看>>
GridView+存储过程实现'真分页'
查看>>
flask_migrate
查看>>
解决activemq多消费者并发处理
查看>>
UDP连接和TCP连接的异同
查看>>
hibernate 时间段查询
查看>>
java操作cookie 实现两周内自动登录
查看>>
Tomcat 7优化前及优化后的性能对比
查看>>
Java Guava中的函数式编程讲解
查看>>
Eclipse Memory Analyzer 使用技巧
查看>>
tomcat连接超时
查看>>
谈谈编程思想
查看>>
iOS MapKit导航及地理转码辅助类
查看>>
检测iOS的网络可用性并打开网络设置
查看>>
简单封装FMDB操作sqlite的模板
查看>>
iOS开发中Instruments的用法
查看>>
iOS常用宏定义
查看>>
什么是ActiveRecord
查看>>
有道词典for mac在Mac OS X 10.9不能取词
查看>>
关于“团队建设”的反思
查看>>