MyBatisPlus
- 1、MyBatisPlus简介
- MyBatisPlus概述
- 入门案例
- 2、标准数据层开发
- MyBatisPlus开启日志
- 标准CRUD
- 分页查询
- 3、DQL编程控制
- 条件查询
- null值处理
- 查询投影
- 查询条件
- 字段映射与表名映射
- 4、DML编程控制
- id生成策略控制
- 多数据操作
- 逻辑删除
- 乐观锁
- 5、代码生成器
1、MyBatisPlus简介
MyBatisPlus概述
- MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在
简化开发
、提高效率
- MyBatisPlus官网1
- MyBatisPlus官网2
- MyBatisPlus特性
- 无侵入:只做增强不做改变,不会对现有工程产生影响
- 强大的CRUD操作:内置通用Mapper,少量配置即可实现单表CRUD操作
- 支持Lambda:编写查询条件无需担心字段写错
- 支持主键自动生成
- 内置分页插件
- ……
入门案例
- 开发方式:
- 基于MyBatis使用MyBatisPlus
- 基于SpringBoot使用MyBatisPlus
基于SpringBoot使用MyBatisPlus
- SpringBoot整合MyBatis开发过程(复习)
- 创建SpringBoot工程
- 勾选配置使用的技术
- 设置dataSource相关配置属性(JDBC参数)
- 定义数据层接口映射配置
- 入门案例
- 创建数据库
CREATE DATABASE `mybatisplus_db`; USER `mybatisplus_db`; CREATE TABLE `user`( `id` INT(20) NOT NULL PRIMARY KEY, `name` VARCHAR(30) NOT NULL, `password` VARCHAR(30) NOT NULL, `age` INT(3) NOT NULL, `tel` VARCHAR(30) )ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO `user` (`id`,`name`,`password`,`age`,`tel`) VALUES (1,'Tom','tom',15,'1234567'), (2,'Jerry','jerry',16,'1234568'), (3,'Jcok','123456',18,'123456'), (4,'小吴','123456',21,'123456')
- 创建一个SpringBoot工程
- 导入MyBatisPlus依赖
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency>
- 导入数据库驱动依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.16</version> </dependency>
- 编写数据库配置文件
- 编写实体类
package com.mybatisplus_01_quickstart.domain; public class User { private Long id; private String name; private String password; private int age; private String tel; @Override public String toString( ) { return "User{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + ", tel='" + tel + '\'' + '}'; } public int getAge( ) { return age; } public void setAge( int age ) { this.age = age; } public Long getId( ) { return id; } public void setId( Long id ) { this.id = id; } public String getName( ) { return name; } public void setName( String name ) { this.name = name; } public String getPassword( ) { return password; } public void setPassword( String password ) { this.password = password; } public String getTel( ) { return tel; } public void setTel( String tel ) { this.tel = tel; } }
- 编写接口层
- 定义数据接口,继承
BaseMapper<user>
package com.mybatisplus_01_quickstart.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.mybatisplus_01_quickstart.domain.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserDao extends BaseMapper<User> { }
- 定义数据接口,继承
- 测试
package com.mybatisplus_01_quickstart; import com.mybatisplus_01_quickstart.dao.UserDao; import com.mybatisplus_01_quickstart.domain.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest class Mybatisplus01QuickstartApplicationTests { @Autowired private UserDao userDao; @Test void testGetAll( ) { List<User> userList = userDao.selectList(null); System.out.println(userList); } }
- 运行结果
- 创建数据库
2、标准数据层开发
MyBatisPlus开启日志
-
在配置文件中添加配置(application.yml)
#开启mp的日志(输出到控制台) mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
标准CRUD
-
标准CRUD制作
功能 自定义接口 新增 boolean save(T t) 删除 boolean delete(int id) 修改 boolean update(T t) 根据id查询 T getById(int id) 查询全部 List getAll() 分页查询 PageInfo getAll(int page, int size) 按条件查询 List getAll<condition condition -
添加
@Test void testSave(){ User user = new User(); user.setName("小吴在敲bug"); user.setId(123L); user.setPassword("123456"); user.setAge(21); user.setTel("2929054638"); userDao.insert(user); }
-
删除
@Test void testDelete(){ userDao.deleteById(123L); }
-
修改:有什么属性修改什么属性,没有就不变
@Test void testUpdate(){ User user = new User(); user.setName("小吴在敲bug"); user.setId(1L); userDao.updateById(user); }
-
查
@Test void testGetById(){ User user = userDao.selectById(1L); System.out.println(user); }
分页查询
-
mp要实现分页查询需要添加拦截器
package com.mybatisplus_01_quickstart.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MpConfig { @Bean public MybatisPlusInterceptor mpInterceptor(){ //1.定义Mp拦截器 MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor(); //2.添加分页的拦截器 mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mpInterceptor; } }
-
MyBatisPlus分页demo
@Test void testGetByPage(){ IPage page = new Page(1,2); //参数一:从第几页开始 参数二:每页显示多少数据 userDao.selectPage(page,null); System.out.println("当前页码:"+page.getCurrent()); System.out.println("每页显示数:"+page.getSize()); System.out.println("一共多少页:"+page.getPages()); System.out.println("一共多少条数据:"+page.getTotal()); System.out.println("数据"+page.getRecords()); }
3、DQL编程控制
条件查询
-
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合
-
默认是and
-
如果or需要添加
//查询年龄大于等于16岁,小于65岁的用户 lqw.lt(User::getAge,50).or().ge(User::getAge,16);
-
-
方式一:按条件查询
@Test void testGetAll( ) { //按条件查询 QueryWrapper qw = new QueryWrapper(); qw.lt("age",18); List<User> userList = userDao.selectList(qw); System.out.println(userList); }
-
方式二:lambda格式
@Test void testGetAll( ) { //lambda格式按条件查询 QueryWrapper<User> qw = new QueryWrapper(); qw.lambda().lt(User::getAge,16); List<User> userList = userDao.selectList(qw); System.out.println(userList); }
-
lambda对象格式
@Test void testGetAll( ) { //lambda对象格式按条件查询 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); lqw.lt(User::getAge,16); List<User> userList = userDao.selectList(lqw); System.out.println(userList); }
null值处理
- 在条件查询中添加null判定
@Test void testGetAll( ) { //模拟页面传递过来的查询数据 UserQuery uq = new UserQuery(); uq.setAge(10); //lambda对象格式按条件查询 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); //先判断第一个参数是否为true,如果为true连接当前条件 lqw.lt(null != uq.getAge2(), User::getAge, uq.getAge2()); lqw.ge(null != uq.getAge() , User::getAge, uq.getAge()); List<User> userList = userDao.selectList(lqw); System.out.println(userList); }
查询投影
-
设置要查询的字段
- lambda格式
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); lqw.select(User::getAge,User::getName,User::getAge);
- 普通格式
@Test void testGetAll( ) { QueryWrapper<User> lqw = new QueryWrapper<>(); lqw.select("id","name","tel"); List<User> userList = userDao.selectList(lqw); System.out.println(userList); }
- lambda格式
-
统计记录(
count(1)
):不能使用lambda格式
@Test void testGetAll( ) { QueryWrapper<User> qw = new QueryWrapper<>(); qw.select("count(1) as count"); List<Map<String, Object>> userList = userDao.selectMaps(qw); System.out.println(userList); }
-
分组查询:不能使用lambda表达式
- .groupBy(“分组字段”)
@Test void testGetAll( ) { QueryWrapper<User> qw = new QueryWrapper<>(); qw.select("count(1) as count","tel"); qw.groupBy("tel"); List<Map<String, Object>> userList = userDao.selectMaps(qw); System.out.println(userList); }
查询条件
-
范围匹配(>、=、between)
- lt:小于
- le:小于等于
- gt:大于
- ge:大于等于
- eq:等于
- between:第一个参数为起始值,第二个为结束值
@Test void testGetAll( ) { LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); //等同于= lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry2"); User loginUser = userDao.selectOne(lqw); System.out.println(loginUser); }
-
模糊匹配(like)
- 非全文检索版:like匹配
@Test void testGetAll( ) { LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>(); //模糊查询like lqw.likeRight(User::getName,"J"); List<User> userList = userDao.selectList(lqw); System.out.println(userList); }
-
空判定(null)
-
包含性匹配(in)
-
分组(group)
-
排序(order)
-
……
-
官网
字段映射与表名映射
- 问题一:表字段与编码属性设计不同步
- 问题二:实体类中添加了数据库中未定义的属性
- 问题三:采用默认查询开放了更多的字段查看权限
- 问题四:表名与实体类名开发设计不同步
- 解决方案:
- 名称:@TableField
- 类型:
属性注解
- 位置:模型类属性定义上方
- 作用:设置当前属性对应的数据库表中的字段
- 例如:
public class User { @TableField(value = "pwd") private String password; @TableField(exist = false) private Integer online; @TableField(select=false) private Long id; }
- value(默认):设置数据库表字段名称
exist
: 设置属性在数据库表字段是否存在,默认为true。此属性无法与value合并使用- select:设置属性是否参与查询,此属性与select()映射不冲突
- 类型:
- 名称: @TableName
- 类型:
类注解
- 位置:实体类定义上方
- 作用:设置当前类对应与数据库表关系
- 例如:
@TableName(value = "tbl_user") public class User {}
- value:设置数据库表名称
- 类型:
4、DML编程控制
id生成策略控制
- 不同的表应用不同的id生成策略
- 日志:自增(1,2,3,4,……)
- 购物订单:特殊硅脂(FQ23948AK3843)
- 外卖单:关联地区日期等信息(06 04 2023 34 91)
- 关系表:可省略id
- ……
- 名称:@TableId
- 类型:
属性注解
- 位置:实体类中用于表示主键的属性上方定义
- 作用:设置当前类中主键属性的生成
- 例如:
public class User { @TableId(type = IdType.AUTO) private Long id; }
- 类型:
- AUTO:使用数控id自增策略控制id生成
- NOME:不设置id生成策略
- INPUT:用户手工输入id
- ASSIGN_ID:雪花算法生成id(
可兼容数值型与字符串型
) - ASSIGN_UUID:以UUID生成算法作为id生成策略
- 全局配置
- 配置id策略
mybatis-plus: # id生成策略 global-config: db-config: id-type: assign_id
- 配置数据库表名前缀
mybatis-plus: global-config: db-config: # 数据库表名前缀 table-prefix: tbl_
- 配置id策略
多数据操作
-
根据删除多个数据
@Test void testDelete( ) { List<Long> list = new ArrayList<>(); list.add(1L); list.add(2L); userDao.deleteBatchIds(list); }
-
根据主键查询多条数据
@Test void testSelect( ) { List<Long> list = new ArrayList<>(); list.add(1L); list.add(2L); userDao.selectBatchIds(list); }
逻辑删除
- 删除操作业务问题:业务数据从数据库中丢弃
- 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中
- 标记逻辑删除以后使用mp查询不出来
-
修改数据库,添加一个字段,并设置默认值
-
修改实体类
- value:没有删除的标记
- delval:删除的标记
package com.mybatisplus_03_dml.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; @Data public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private String password; private Integer age; private String tel; //逻辑删除字段,标记当前记录是否被删除 @TableLogic(value = "0",delval = "1") private Integer deleted; }
-
测试
@Test void testDelete(){ userDao.deleteById(3l); }
-
运行结果
-
逻辑删除全局配置
mybatis-plus: global-config: db-config: logic-delete-field: deleted #删除标记字段 logic-not-delete-value: 0 #显示标记 logic-delete-value: 1 #删除标记
乐观锁
业务并发现象带来的问题:秒杀
-
修改数据库:添加字段来标记谁在操作这个数据
-
修改实体类
package com.mybatisplus_03_dml.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data; @Data public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private String password; private Integer age; private String tel; private Integer deleted; @Version //添加注解 private Integer version; }
-
编写拦截器
package com.mybatisplus_03_dml.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MpConfig { @Bean public MybatisPlusInterceptor mpInterceptor(){ //1.定义Mp拦截器 MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor(); //2.添加乐观锁拦截器 mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mpInterceptor; } }
-
测试
@Test void testUpdate(){ //1.先查询数据,获取到version数据 User userA = userDao.selectById(2l); //version=2 User userB = userDao.selectById(2l); //version=2 //2.执行数据修改操作 userB.setName("JockAAA"); userDao.updateById(userB); //version=3 userA.setName("JockBBB"); userDao.updateById(userB); //version=2?条件不成立 }
5、代码生成器
- 模板:MyBatisPlus提供
- 数据库相关配置:读取数据库获取信息
- 开发者自定义配置:手工配置
-
创建一个新工程
-
导入依赖
<!--MyBatisPlus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!--代码生成器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!--veLocity模板引擎--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>
-
编写代码生成器
package com.mybatisplus_04_generator; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; public class Generator { public static void main( String[] args ) { //实例化代码生成器 AutoGenerator autoGenerator = new AutoGenerator(); //配置数据库驱动 DataSourceConfig dataSource = new DataSourceConfig(); dataSource.setDriverName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db"); dataSource.setUsername("root"); dataSource.setPassword("123456"); autoGenerator.setDataSource(dataSource); //设置全局配置 GlobalConfig globalConfig = new GlobalConfig(); //设置代码生成位置 globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java"); //设置生成完毕是否打开生成代码所在的目录 globalConfig.setOpen(false); //设置作者 globalConfig.setAuthor("小吴在敲Bug"); //设置是否覆盖原始生成的文件 globalConfig.setFileOverride(true); //设置数据层接口名,%s为占位符,指代实体类名称 globalConfig.setMapperName("%sDao"); //设置id生成策略 globalConfig.setIdType(IdType.ASSIGN_ID); //添加配置 autoGenerator.setGlobalConfig(globalConfig); //设置包名相关配置 PackageConfig packageConfig = new PackageConfig(); //设置生成的报名,与代码所在位置不冲突,二者叠加完整路径 packageConfig.setParent("com.mybatisplus_04_generator"); //设置实体类包名 packageConfig.setEntity("domain"); //设置实体类包名 packageConfig.setMapper("dao"); //添加配置 autoGenerator.setPackageInfo(packageConfig); //策略配置 StrategyConfig strategyConfig = new StrategyConfig(); //设置当前参与生成的表名,参数为可变参数 strategyConfig.setInclude("tbl_user"); //设置数据库表的前缀名称:实体类名 = 数据库表名 - 前缀名 例如:User=tbl_user strategyConfig.setTablePrefix("tbl_"); //设置是否启用Rest风格 strategyConfig.setRestControllerStyle(true); //设置乐观锁字段名 strategyConfig.setLogicDeleteFieldName("deleted"); //设置是否启用Lombok strategyConfig.setEntityLombokModel(true); //添加配置 autoGenerator.setStrategy(strategyConfig); //执行代码生成器 autoGenerator.execute(); } }