博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis Plus的使用(Mybatis增强工具)- Mybatis从学习到忘记
阅读量:2144 次
发布时间:2019-04-30

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

概述

如果你是刚刚学习完Mybatis那么恭喜你,你竟然在起步阶段,就发现了一款可以让Mybatis起飞的东西;如果你是Mybatis熟客,或者是会使用Mybatis-generator、Mybatis-PageHelper、Mybatis通用mapper,那么也提前恭喜你,你可以放弃这些“散件”,只需要掌握今天这个东西,上面这些过客可以统统说“拜拜”了。Mybatis Plus看似有着一统Mybatis所有工具(插件)集的架势,那么等什么呢,开始吧!

 一、Mybatis-Plus(MP)简介

  • MP官网: 
  • GigHub源码地址:
  • MP宗旨:为简化开发而生
  • MP愿景:成为 MyBatis 最好的搭档,就像  中的 1P、2P,基友搭配,效率翻倍。
  • MP特性:
  1. 润物无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
  2. 效率至上:只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。
  3. 丰富功能:热加载、代码生成、分页、性能分析等功能一应俱全。
  4. 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  5. 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  6. 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  7. 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  8. 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  9. 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  10. 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  11. 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  12. 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  13. 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
  14. 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  15. 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
  • MP经典主图(就想魂斗罗一样并肩作战):

核心配图

二、安装

Spring Boot项目添加Mybatis-plus的依赖如下:

com.baomidou
mybatis-plus-boot-starter
3.1.2

Spring MVC项目添加Mybatis-plus的依赖如下:

com.baomidou
mybatis-plus
3.1.2

三、配置

3.1 Spring Boot 工程:配置 MapperScan 注解

@SpringBootApplication@MapperScan("com.xxx.xx.mapper") // 自己mapper所在的报名public class Application {    public static void main(String[] args) {        SpringApplication.run(QuickStartApplication.class, args);    }}

配置mapper的xml映射文件

mybatis-plus.mapper-locations=classpath*:mapper/*.xml

3.2 Spring MVC 工程:

3.2.1 配置 MapperScan

3.2.2 调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory

MyBatis-Plus 提供了大量的个性化配置来满足不同复杂度的工程,大家可根据自己的项目按需取用,详细配置请参考官方一文,详细的注解请参考官方一文。

3.3 使用MP

使用其实很简单,只是让自己的Mapper继承MP的BaseMapper<T>,那么自己的Mapper就可以愉快的使用BaseMapper的各种CRUD方法了

public class UserMapper extends BaseMapper
{ // 已经可以使用BaseMapper中的大量CRUD方法}

怎么样除了在Mapper中继承了一个类,xml中没有任何修改,哪怕没有xml都可以对数据库进行操作了,简单使用就是这样,接下来看看MP有哪些有用的功能吧。 

四、核心功能

4.1 代码生成器(很好用的-选择使用)

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

4.1.1 添加 代码生成器 依赖

MyBatis-Plus 从3.0.3之后移除了代码生成器与模板引擎的默认依赖,需要手动添加代码生成器的依赖:

com.baomidou
mybatis-plus-generator
latest-version

4.1.2 添加 模板引擎 依赖

MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。

  • Velocity(默认):
  • org.apache.velocity
    velocity-engine-core
    latest-velocity-version
  • Freemarker:
  • org.freemarker
    freemarker
    latest-freemarker-version

 注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

AutoGenerator generator = new AutoGenerator();// set freemarker enginegenerator.setTemplateEngine(new FreemarkerTemplateEngine());

代码生成器 代码演示:

public class MPGenerator {	// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中	public static String scanner(String tip) {		Scanner scanner = new Scanner(System.in);		StringBuilder help = new StringBuilder();		help.append("请输入" + tip + ":");		if (scanner.hasNext()) {			String ipt = scanner.next();			if (StringUtils.isNotEmpty(ipt)) {				return ipt;			}		}		throw new MybatisPlusException("请输入正确的" + tip + "!");	}	public static void main(String[] args) {		// 代码生成器		AutoGenerator mpg = new AutoGenerator();		// 全局配置		GlobalConfig gc = new GlobalConfig();		String projectPath = System.getProperty("user.dir");		gc.setOutputDir(projectPath + "/src/main/java");		gc.setFileOverride(false);		gc.setAuthor("wtao");		gc.setOpen(false);		gc.setBaseColumnList(true);		gc.setBaseResultMap(true);		gc.setIdType(IdType.AUTO);		// gc.setSwagger2(true); 实体属性 Swagger2 注解		mpg.setGlobalConfig(gc);		// 数据源配置		DataSourceConfig dsc = new DataSourceConfig();		dsc.setUrl("jdbc:mysql://127.0.0.1/mp?useUnicode=true&characterEncoding=utf-8");		// dsc.setSchemaName("public");		dsc.setDriverName("com.mysql.cj.jdbc.Driver");		dsc.setUsername("root");		dsc.setPassword("123456");		mpg.setDataSource(dsc);		// 包配置		PackageConfig pc = new PackageConfig();		pc.setModuleName(scanner("模块名"));		pc.setParent("com.eyaoshun.crm");		mpg.setPackageInfo(pc);		// 自定义配置		InjectionConfig cfg = new InjectionConfig() {			@Override			public void initMap() {				// to do nothing			}		};		// 如果模板引擎是 freemarker		//String templatePath = "/templates/mapper.xml.ftl";		// 如果模板引擎是 velocity		String templatePath = "/templates/mapper.xml.vm";		// 自定义输出配置		List
focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); /* cfg.setFileCreate(new IFileCreate() { @Override public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) { // 判断自定义文件夹是否需要创建 checkDir("调用默认方法创建的目录"); return false; } }); */ cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); // 配置自定义输出模板 //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 // templateConfig.setEntity("templates/entity2.java"); // templateConfig.setService(); // templateConfig.setController(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setRestControllerStyle(true); strategy.setInclude("t_.*"); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix("t_"); mpg.setStrategy(strategy); // 模板引擎 mpg.setTemplateEngine(new VelocityTemplateEngine()); // 执行生成器 mpg.execute(); }}

更多详细的代码生成器配置,请参考一文。

运行main->控制台输入"demo"后将生成的结构如下(resources下还有xml文件):

4.2 CRUD 接口介绍(MP的核心功能)

4.2.1 Mapper CRUD 接口

  • 通用 CRUD 封装接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
  • 泛型 T 为任意实体对象
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
  • 对象 Wrapper

其实我们真正在使用Mybatis时候核心就是使用接口编程,MP帮助我们创建了大量的公共接口和实现,我们只需要再在自己的Mapper接口中继承BaseMapper接口即可使用其内部的各CRUD方法了。提供的方法如下:

插入一条记录int insert(T entity);// 根据 ID 删除int deleteById(Serializable id);// 根据 columnMap 条件,删除记录,  参数为表字段 map 对象int deleteByMap(@Param(Constants.COLUMN_MAP) Map
columnMap);// 根据 entity 条件,删除记录int delete(@Param(Constants.WRAPPER) Wrapper
wrapper);// 删除(根据ID 批量删除)int deleteBatchIds(@Param(Constants.COLLECTION) Collection
idList);// 根据 ID 修改int updateById(@Param(Constants.ENTITY) T entity);// 根据 whereEntity 条件,更新记录int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper
updateWrapper);// 根据 ID 查询T selectById(Serializable id);// 查询(根据ID 批量查询)List
selectBatchIds(@Param(Constants.COLLECTION) Collection
idList);// 查询(根据 columnMap 条件)List
selectByMap(@Param(Constants.COLUMN_MAP) Map
columnMap);// 根据 entity 条件,查询一条记录T selectOne(@Param(Constants.WRAPPER) Wrapper
queryWrapper);// 根据 Wrapper 条件,查询总记录数Integer selectCount(@Param(Constants.WRAPPER) Wrapper
queryWrapper);// 根据 entity 条件,查询全部记录List
selectList(@Param(Constants.WRAPPER) Wrapper
queryWrapper);// 根据 Wrapper 条件,查询全部记录List
> selectMaps(@Param(Constants.WRAPPER) Wrapper
queryWrapper);// 根据 Wrapper 条件,查询全部记录List
selectObjs(@Param(Constants.WRAPPER) Wrapper
queryWrapper);// 根据 entity 条件,查询全部记录(并翻页)IPage
selectPage(IPage
page, @Param(Constants.WRAPPER) Wrapper
queryWrapper);// 根据 Wrapper 条件,查询全部记录(并翻页)IPage
> selectMapsPage(IPage
page, @Param(Constants.WRAPPER) Wrapper
queryWrapper);

4.2.2 Service CRUD 接口

  • 通用 Service CRUD 封装接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  • 对象 Wrapper

(划重点)Service层还有两个牛逼的方法,是通过链式调用查询和更新,使用lambdaQuerylambdaUpate两个方法可以不用构造Wrapper也可以进行查询和更新,多使用如下的方法,让你自己嗨到站不起来。

List
userList = userService.lambdaQuery.eq(User::getName, "老王").eq(User:getAge).list();boolean b = userService.lambdaUpdate.eq(User::getName, "老王").set(User::getAge, 18).update();

MP对service层也做了大量公共方法的定义,我们只需要在自己的Service接口中继承MP的IService,即可拥有比mapper更多的方法操作数据库。Service层提供的方法如下:

// 插入一条记录(选择字段,策略插入)boolean save(T entity);// 插入(批量)boolean saveBatch(Collection
entityList);// 插入(批量) @param batchSize 插入批次数量boolean saveBatch(Collection
entityList, int batchSize);// 批量修改插入boolean saveOrUpdateBatch(Collection
entityList);// 批量修改插入boolean saveOrUpdateBatch(Collection
entityList, int batchSize);// 根据 ID 删除boolean removeById(Serializable id);// 根据 columnMap 条件,删除记录boolean removeByMap(Map
columnMap);// 根据 entity 条件,删除记录boolean remove(Wrapper
queryWrapper);// 删除(根据ID 批量删除)boolean removeByIds(Collection
idList);// 根据 ID 选择修改boolean updateById(T entity);// 根据 whereEntity 条件,更新记录boolean update(T entity, Wrapper
updateWrapper);// 根据ID 批量更新boolean updateBatchById(Collection
entityList, int batchSize);// TableId 注解存在更新记录,否插入一条记录boolean saveOrUpdate(T entity);// 根据 ID 查询T getById(Serializable id);// 查询(根据ID 批量查询)Collection
listByIds(Collection
idList);// 查询(根据 columnMap 条件)Collection
listByMap(Map
columnMap);// 根据 Wrapper,查询一条记录T getOne(Wrapper
queryWrapper, boolean throwEx);// 根据 Wrapper,查询一条记录Map
getMap(Wrapper
queryWrapper);// 根据 Wrapper,查询一条记录Object getObj(Wrapper
queryWrapper);// 根据 Wrapper 条件,查询总记录数int count(Wrapper
queryWrapper);// 查询列表List
list(Wrapper
queryWrapper);// 翻页查询IPage
page(IPage
page, Wrapper
queryWrapper);// 查询列表List
> listMaps(Wrapper
queryWrapper);// 根据 Wrapper 条件,查询全部记录List
listObjs(Wrapper
queryWrapper);// 翻页查询IPage
> pageMaps(IPage
page, Wrapper
queryWrapper);

4.2.3 条件构造器

Mapper层和Service层的接口中看到了许多Wrapper(条件构造器),它可以想象成它一个创建where后面所有语句的对象,它通过各种方法可以创建出各种where语句中的“条件”,所以叫他条件构造器。

  • 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中
  • 以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true
  • 以下出现的泛型Param均为Wrapper的子类实例(均具有AbstractWrapper的所有方法)
  • 以下方法在入参中出现的R为泛型,在普通wrapper中是String,在LambdaWrapper中是函数(例:Entity::getId,Entity为实体类,getId为字段idgetMethod)
  • 以下方法入参中的R column均表示数据库字段,当R具体类型为String时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)!而不是实体类数据字段名!!!,另当R具体类型为SFunction时项目runtime不支持eclipse自家的编译器!!!
  • 以下举例均为使用普通wrapper,入参为MapList的均以json形式表现!
  • 使用中如果入参的Map或者List,则不会加入最后生成的sql中!!!
  • 有任何疑问就点开源码看,看不懂函数

警告:

不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输

  1. wrapper 很重
  2. 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
  3. 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
  4. 我们拒绝接受任何关于 RPC 传输 Wrapper 报错相关的 issue 甚至 pr

4.2.3.1 AbstractWrapper

说明:QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类

用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

allEq

allEq(Map
params)allEq(Map
params, boolean null2IsNull)allEq(boolean condition, Map
params, boolean null2IsNull)
  • 全部(或个别)

个别参数说明:

params : key为数据库字段名,value为字段值

null2IsNull : 为true则在mapvaluenull时调用 方法,为false时则忽略valuenull

  • 例1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null
  • 例2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'
allEq(BiPredicate
filter, Map
params)allEq(BiPredicate
filter, Map
params, boolean null2IsNull)allEq(boolean condition, BiPredicate
filter, Map
params, boolean null2IsNull)

 个别参数说明:

filter : 过滤函数,是否允许字段传入比对条件中

paramsnull2IsNull : 同上

  • 例1: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"老王",age:null})--->name = '老王' and age is null
  • 例2: allEq((k,v) -> k.indexOf("a") > 0, {id:1,name:"老王",age:null}, false)--->name = '老王'

eq 等于 =

eq(R column, Object val)eq(boolean condition, R column, Object val)

以下方法和上面eq方法的参数几乎相同,仅写出方法名称参考,(between方法参数会多一个值)

  • ne 不等于<>
  • gt 大于 >
  • ge 大于等于 >=
  • lt 小于 <
  • le 小于大于 <=
  • between   BETWEEN 值1 AND 值2
  • notBetween   NOT BETWEEN 值1 AND 值2
  • like  LIKE '%值%'
  • notLike  NOT LIKE '%值%'
  • likeLeft  LIKE '%值'
  • likeRight  LIKE '值%'
  • isNull   字段 IS NULL
  • isNotNull   字段 IS NOT NULL
  • between BETWEEN   值1 AND 值2
  • notBetween NOT BETWEEN   值1 AND 值2
  • like  LIKE '%值%'
  • notLike  NOT LIKE '%值%'
  • likeLeft   LIKE '%值'
  • likeRight  LIKE '值%'

isNull、isNotNul  字段是否为NULL

isNull(R column)isNull(boolean condition, R column)

in、notIn

notIn(R column, Collection
value)notIn(boolean condition, R column, Collection
value)

inSql、notInSql

inSql(R column, String inValue)inSql(boolean condition, R column, String inValue)
  • 字段 IN ( sql语句 )
  • 例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
  • 例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)

groupBy

groupBy(R... columns)groupBy(boolean condition, R... columns)
  • 分组:GROUP BY 字段, ...
  • 例: groupBy("id", "name")--->group by id,name

orderByAsc、orderByDesc

orderByAsc(R... columns)orderByAsc(boolean condition, R... columns)orderByDesc(R... columns)orderByDesc(boolean condition, R... columns)
  • 排序:ORDER BY 字段, ... ASC/DESC
  • 例: orderByAsc("id", "name")--->order by id ASC,name ASC

orderBy

orderBy(boolean condition, boolean isAsc, R... columns)
  • 排序:ORDER BY 字段, ...
  • 例: orderBy(true, true, "id", "name")--->order by id ASC,name ASC

having

having(String sqlHaving, Object... params)having(boolean condition, String sqlHaving, Object... params)
  • HAVING ( sql语句 )
  • 例: having("sum(age) > 10")--->having sum(age) > 10
  • 例: having("sum(age) > {0}", 11)--->having sum(age) > 11

or

or()or(boolean condition)
  • 例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'

注意事项:主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

以下的三个方法or(Function<Param, Param> func),and(Function<Param, Param> func),nested(Function<Param, Param> func)是嵌套方法,参数是通过lambda表达式写出一串条件,查询的时候这些条件会被使用括起来再去数据库查询。比如我们手写sql必须在条件时加入括号才可以,下面三个方法是干这个用的,如下面sql对应3个方法的例子:

SELECT * FROM user WHERE name = '老王' or (age < 30 and money > 100000000)SELECT * FROM user WHERE age < 30 and (money > 10000000 or name='李现')SELECT * FROM user WHERE (money > 1000000 or age < 30) and age < 30
or(Function
func)or(boolean condition, Function
func)
  • OR 嵌套
  • 例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')

and

and(Function
func)and(boolean condition, Function
func)
  • AND 嵌套
  • 例: and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')

 nested

nested(Function
func)nested(boolean condition, Function
func)
  • 正常嵌套 不带 AND 或者 OR
  • 例: nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')

apply : 手写sql,动态传值(防sql注入)

apply(String applySql, Object... params)apply(boolean condition, String applySql, Object... params)
  • 拼接 sql

注意事项:该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!

  • 例: apply("id = 1")--->id = 1
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

last 

last(String lastSql)last(boolean condition, String lastSql)
  • 无视优化规则直接拼接到 sql 的最后

注意事项:

只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

  • 例: last("limit 1")

exists

exists(String existsSql)exists(boolean condition, String existsSql)
  • 拼接 EXISTS ( sql语句 )
  • 例: exists("select id from table where age = 1")--->exists (select id from table where age = 1)

notExists

notExists(String notExistsSql)notExists(boolean condition, String notExistsSql)
  • 拼接 NOT EXISTS ( sql语句 )
  • 例: notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)

4.2.3.2 QueryWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件

LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取

select

select(String... sqlSelect)select(Predicate
predicate)select(Class
entityClass, Predicate
predicate)
  • 设置查询字段

说明:

以上方分法为两类.

第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准

  • 例: select("id", "name", "age")
  • 例: select(i -> i.getProperty().startsWith("test"))

UpdateWrapper

说明:

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件

LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!

set

set(String column, Object val)set(boolean condition, String column, Object val)
  • SQL SET 字段
  • 例: set("name", "老李头")
  • 例: set("name", "")--->数据库字段值变为空字符串
  • 例: set("name", null)--->数据库字段值变为null

setSql

setSql(String sql)
  • 设置 SET 部分 SQL
  • 例: setSql("name = '老李头')

lambda

  • 获取 LambdaWrapper
    QueryWrapper中是获取LambdaQueryWrapper
    UpdateWrapper中是获取LambdaUpdateWrapper

4.2.3.3 使用 Wrapper 自定义SQL

需求来源:

在使用了mybatis-plus之后, 自定义SQL的同时也想使用Wrapper的便利应该怎么办? 在mybatis-plus版本3.0.7得到了完美解决 版本需要大于或等于3.0.7, 以下两种方案取其一即可

Service.java

mysqlMapper.getAll(Wrappers.
lambdaQuery().eq(MysqlData::getGroup, 1));

方案一 注解方式 Mapper.java

@Select("select * from mysql_data ${ew.customSqlSegment}")List
getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

方案二 XML形式 Mapper.xml

4.3 分页插件

4.3.1 引入分页插件

spring xml方法

 spring boot方式

//Spring boot方式@EnableTransactionManagement@Configuration@MapperScan("com.baomidou.cloud.service.*.mapper*")public class MybatisPlusConfig {    /**     * 分页插件     */    @Bean    public PaginationInterceptor paginationInterceptor() {        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();        // paginationInterceptor.setLimit(你的最大单页限制数量,默认 500 条,小于 0 如 -1 不受限制);        return paginationInterceptor;    }}

4.3.2 使用分页方法

已经配置了分页插件后,直接使用mapper中的分页方法就可以了。

// 先构造分页实体对象Page
page = new Page<>(1, 2);// 查询出来对象包含查询结果集合和分页相关属性Ipage
ipage = userMapper.selectPage(page, queryWrapper);

如果分页查询时不需要使用总条数的信息(total)可以这样构造Page对象:

Page
page = new Page<>(1, 2, false);

因为查询分页时会查询数据库两次,一次是查询总记录,一次是查询分页数据。构造器加了第3个参数false后,仅去数据库查询一次,就不会查询总记录数的sql语句了,性能肯定有优化,不过一般分页都会使用总记录数的。

4.3.2 XML 自定义分页

MP中查询分页只有2个方法,如果想自己定义新方法(如连表查询等) 也需要分页的话,则需要这样:

  • UserMapper.java 方法内容
public interface UserMapper{//可以继承或者不继承BaseMapper    /**     * 

* 查询 : 根据state状态查询用户列表,分页显示 * 注意!!: 如果入参是有多个,需要加注解指定参数名才能在xml中取值 *

* * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象) * @param state 状态 * @return 分页对象 */ IPage
selectPageVo(Page page, @Param("state") Integer state);}
  • UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页
  • UserServiceImpl.java 调用分页方法
public IPage
selectUserPage(Page
page, Integer state) { // 不进行 count sql 优化,解决 MP 无法自动优化 SQL 问题,这时候你需要自己查询 count 部分 // page.setOptimizeCountSql(false); // 当 total 为小于 0 或者设置 setSearchCount(false) 分页插件不会进行 count 查询 // 要点!! 分页返回的对象与传入的对象是同一个 return userMapper.selectPageVo(page, state);}

五 MP插件扩展

5.1 逻辑删除

先来解释下什么是逻辑删除:逻辑删除不是将数据直接delete,而是通过表中某个字段表示本条记录是否被删除,比如一个表中有个字段叫deleted(是否删除),0表示未删除,1表示已删除。什么时候使用逻辑删除呢,比如一个商场系统的订单模块,用户自己的订单是可以删除的,可对于这个系统来说,好不容易下一个单,怎么能就这么被删,所以引出了逻辑删除:前台查的时候查is_delete是0的,用户删除的时候将deleted字段值改为1即可。

5.1.1 配置->SpringBoot 配置方式:

  • application.yml 加入配置(如果你的默认值和mp默认的一样,该配置可无):
mybatis-plus:  global-config:    db-config:      logic-delete-value: 1 # 逻辑已删除值(默认为 1)      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  • 实体类字段上加上@TableLogic注解
@TableLogicprivate Integer deleted;

5.1.2 使用逻辑删除

使用起始很简单:依然使用MP自带的删除和查询的方法,查询时MP会自动在sql语句最后添加上deleted=0,删除的时候会自动改用update方法将deleted的值改为1;当然如果你是想忽略deleted的情况只能自己去Mapper添加方法了。

  • example删除时 update user set deleted=1 where id =1 and deleted=0查找时 select * from user where deleted=0

附件说明

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
  • 如果你需要再查出来就不应使用逻辑删除,而是以一个状态去表示。

如: 员工离职,账号被锁定等都应该是一个状态字段,此种场景不应使用逻辑删除。

  • 若确需查找删除数据,如老板需要查看历史所有数据的统计汇总信息,请单独手写sql。

5.2  乐观锁插件

5.2.1 主要适用场景

意图:

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

乐观锁配置需要2步 记得两步

5.2.2 步骤一:插件配置

spring xml定义一个乐观锁拦截器bean:

spring boot定义一个乐观锁拦截器bean:

@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor() {    return new OptimisticLockerInterceptor();}

5.2.3 步骤二:注解实体字段 @Version 必须要!

@Versionprivate Integer version;

特别说明:

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!

5.2.4 示例

示例Java代码(参考代码)

int id = 100;int version = 2;User u = new User();u.setId(id);u.setVersion(version);u.setXXX(xxx);if(userService.updateById(u)){    System.out.println("Update successfully");}else{    System.out.println("Update failed due to modified by others");}

示例SQL原理

update tbl_user set name = 'update',version = 3 where id = 100 and version = 2

本文章大部分内容参考官网文档,如有纰漏请留言告知,谢谢。

(完)

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

你可能感兴趣的文章
Intellij IDEA使用(三)——在Intellij IDEA中配置Tomcat服务器
查看>>
Intellij IDEA使用(四)—— 使用Intellij IDEA创建静态的web(HTML)项目
查看>>
Intellij IDEA使用(五)—— Intellij IDEA在使用中的一些其他常用功能或常用配置收集
查看>>
Intellij IDEA使用(六)—— 使用Intellij IDEA创建Java项目并配置jar包
查看>>
Eclipse使用(十)—— 使用Eclipse创建简单的Maven Java项目
查看>>
Eclipse使用(十一)—— 使用Eclipse创建简单的Maven JavaWeb项目
查看>>
Intellij IDEA使用(十三)—— 在Intellij IDEA中配置Maven
查看>>
面试题 —— 关于main方法的十个面试题
查看>>
集成测试(一)—— 使用PHP页面请求Spring项目的Java接口数据
查看>>
使用Maven构建的简单的单模块SSM项目
查看>>
Intellij IDEA使用(十四)—— 在IDEA中创建包(package)的问题
查看>>
Redis学习笔记(四)—— redis的常用命令和五大数据类型的简单使用
查看>>
Win10+VS2015编译libcurl
查看>>
Windows下使用jsoncpp
查看>>
Ubuntu下测试使用Nginx+uWsgi+Django
查看>>
Windows下编译x264
查看>>
visual studio调试内存泄漏工具
查看>>
开源Faac实现PCM编码AAC
查看>>
Windows下wave API 音频采集
查看>>
借船过河:一个据说能看穿你的人性和欲望的心理测试
查看>>