MyBatisPlus
一、MyBatisPlus入门
-
在创建SpringBoot项目时勾选需要的技术集
-
导入相应坐标
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> 注:由于MyBatisPlus并未被收录到idea的系统内置配置,无法直接选择加入,需要手动在pom.xml中配置添加
-
配置数据源
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC username: root password: 123456 -
使用@Mapper注解,标注mapper接口并继承BaseMapper<>
@Mapper public interface UserDao extends BaseMapper<User> { } -
测试
@Autowired private UserDao userDao; @Test void selectAll() { System.out.println(userDao.selectList(null)); }
二、MyBatisPlus的简介
MyBatis-Plus (简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生
图片来源于:简介 | MyBatis-Plus (baomidou.com)
三、标准数据层开发
1. 标准数据层CRUD功能
@Mapper public interface UserDao extends BaseMapper<User> { } ------------------------ @Autowired private UserDao userDao;
1-1. 新增
@Test void testInsert() { User user = new User(null, "test", "1234", 12, "1564456776"); System.out.println(userDao.insert(user)); }
1-2. 删除
@Test void testDeleteById() { User user = new User(); System.out.println(userDao.deleteById(1638010091989630977l)); }
1-3. 修改
@Test void testUpadteById() { User user = new User(); user.setId(1638011119938093058l); user.setAge(33); System.out.println(userDao.updateById(user)); }
1-4. 根据Id查询
@Test void testSelectById() { User user = new User(); System.out.println(userDao.selectById(1638011119938093058l)); }
1-5. 查询全部
@Test void testSelectAll() { System.out.println(userDao.selectList(null)); }
2. LomBok
Lombok,是一个Java类库,提供了一组注解,简化POJO实体类开发
Lombok - IntelliJ IDEs Plugin | Marketplace (jetbrains.com)
使用步骤:
-
导入相应坐标
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> 注:新版本IDEA已经内置了该插件,如果删除setter和getter方法程序有报红,则需要安装插件
-
使用注解
- @Setter:为模型类的属性提供setter方法
- @Getter:为模型类的属性提供getter方法
- @ToString:为模型类的属性提供toString方法
- @EqualsAndHashCode:为模型类的属性提供equals和hashcode方法
- @Data:是个组合注解,包含上面的注解的功能
- @NoArgsConstructor:提供一个无参构造函数
- @AllArgsConstructor:提供一个包含所有参数的构造函数
说明:Lombok只是简化模型类的编写,之前的方法也能继续使用,如:
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private String password; private Integer age; private String tel; public User(String name, String password) { this.name = name; this.password = password; } }
3. 分页插件
3-1. Page
Page继承了 IPage 类,实现了 简单分页模型 如果你要实现自己的分页模型可以继承 Page类或者实现 IPage类
Page的属性:
图片来源于:分页插件 | MyBatis-Plus (baomidou.com)
3-2. 分页插件的使用
-
配置MyBatisPlus的分页拦截器
/** * @Author: XIYAN * @Date: 2023/3/21 14:23 * @注释:MyBatisPlus配置 */ @Configuration public class MyBaitsPlusConfig { /** * MyBatisPlus拦截器 * * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { //创建拦截器对象 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); //添加分页拦截器 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } } -
测试
@Test void testSelectAllPage() { IPage page = new Page(1,4); userDao.selectPage(page, null); System.out.println("当前页码为:" + page.getCurrent() + ",每页显示:" + page.getSize() + "条,共:" + page.getPages() + "页"); System.out.println("总计:" + page.getTotal() + "条"); page.getRecords().forEach(System.out::println); }
附:开启日志
# 配置MyBatisPlus mybatis-plus: configuration: # 输出日志到控制台 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4. DQL编程控制
4-1. 条件查询方式
-
方式一:字符串
//字符串 QueryWrapper queryWrapper = new QueryWrapper(); //条件:年龄小于13 queryWrapper.lt("age", 13); System.out.println(userDao.selectList(queryWrapper)); -
方式二:lambda
//lambda QueryWrapper<User> queryWrapper = new QueryWrapper(); //条件:年龄小于13 queryWrapper.lambda().lt(User::getAge,13); System.out.println(userDao.selectList(queryWrapper)); -
方式三:lambda(LambdaQueryWrapper)
//lambda(LambdaQueryWrapper) LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper(); //条件:年龄小于13 lambdaQueryWrapper.lt(User::getAge,13); System.out.println(userDao.selectList(lambdaQueryWrapper));
注:or()相当于sql语句的or关键字,默认为and
4-2. 条件NULL值处理
在条件查询中如若出现某一条件为空可使用以下方法解决:
-
创建Query实体类,继承于model实体
@Data @NoArgsConstructor @AllArgsConstructor public class UserQuery extends User { private Integer qAge; } -
使用表达式判断当前条件是否为空
//模拟页面传递过来的查询数据 UserQuery uq = new UserQuery(); uq.setAge(10); uq.setQAge(30); LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>(); lqw.lt(uq.getQAge() != null,User::getAge, uq.getQAge()); lqw.gt(uq.getAge() != null,User::getAge, uq.getAge()); System.out.println(userDao.selectList(lqw));
4-2. 查询投影
4-3. 查询条件设定
-
等值匹配
eq(): 相当于 =,对应的sql语句为:
SELECT id,name,password,age,tel FROM user WHERE (name = ? AND password = ?) -
selectList:查询结果为多个或者单个
-
selectOne:查询结果为单个
-
-
范围匹配(>、>=、<、<=、between)
- gt():大于(>)
- ge():大于等于(>=)
- lt():小于(<)
- lte():小于等于(<=)
- between():between ? and ?
-
模糊匹配(like)
- like():前后加百分号,如 %J%
- likeLeft():前面加百分号,如 %J
- likeRight():后面加百分号,如 J%
-
排序(order)
-
orderBy排序
- condition:条件,true则添加排序,false则不添加排序
- isAsc:是否为升序,true升序,false降序
- columns:排序字段,可以有多个
-
orderByAsc/Desc(单个column):按照指定字段进行升序/降序
-
orderByAsc/Desc(多个column):按照多个字段进行升序/降序
-
orderByAsc/Desc
- condition:条件,true添加排序,false不添加排序
- 多个columns:按照多个字段进行排序
-
-
具体条件请参考官方文档的条件构造器
https://baomidou.com/pages/10c804/#abstractwrapper
4-4. 字段映射与表名映射
-
字段映射
@Data @NoArgsConstructor @AllArgsConstructor //实体类映射的表 @TableName("user") public class User { ... //不查询的字段 @TableField(select = false) private String password; private Integer age; private String tel; //不存在的字段 @TableField(exist = false) private Integer online; ... } -
表名映射
@Data @NoArgsConstructor @AllArgsConstructor //实体类映射的表 @TableName("user") public class User { ... }
表名映射(前缀)全局配置:
mybatis-plus: global-config: db-config: table-prefix: tbl_
三、DML编程控制
1. Id生成策略
@Data @NoArgsConstructor @AllArgsConstructor //实体类映射的表 @TableName("user") public class User { //使用数据库自增策略 @TableId(type = IdType.AUTO) private Long id; ... }
type属性可选值:
- NONE:不设置id生成策略
- AUTO:使用数据库ID自增,在使用该策略的时候一定要确保对应的 数据库表设置了ID主键自增,否则无效
- INPUT:用户手工输入id,在使用该策略的时候需要将表的自增策略删除掉
- ASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型),在使用该策略的时候不需要手动设置ID,如果手动设置ID,则会使用自己设置的值
- ASSIGN_UUID:以UUID生成算法作为id生成策略,在使用该策略的时候需要注意的是,主键的类型不能是Long,而应该改成String类型(表字段的主键类型需要设置为varchar,长度要大于32,因为UUID生成的主键为32位,如果长度小的话就会导致插入失败)
id生成策略全局配置:
mybatis-plus: global-config: db-config: id-type: assign_id
分布式ID是什么?
- 当数据量足够大的时候,一台数据库服务器存储不下,这个时候就需要多台数据库服务器进行存储 比如订单表就有可能被存储在不同的服务器上
- 如果用数据库表的自增主键,因为在两台服务器上所以会出现冲突这个时候就需要一个全局唯一ID,这个ID就是分布式ID。
雪花算法:
雪花算法(SnowFlake),是Twitter官方给出的算法实现是用Scala写的;其生成的结果是一个64bit大小的整数,它的结构如下图:
-
1bit,不用,因为二进制中最高位是符号位,1表示负数,0表示正数
生成的id一般都是用整数, 所以最高位固定为0
-
41bit-时间戳,用来记录时间戳,毫秒级 3. 10bit-工作机器id,用来记录工作机器id,其中高位5bit是数据中心ID其取值范围0-31,低位 5bit是工作节点ID其取值范围0-31,两个组合起来最多可以容纳1024个节点
-
序列号占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096 个ID
Id生成策略对比:
策略名称 | 具体描述 |
---|---|
NONE | 不设置id生成策略,MP不自动生成,约等于INPUT,所以这两种方式都需要用户手动设 置,但是手动设置第一个问题是容易出现相同的ID造成主键冲突,为了保证主键不冲突就需要做很 多判定,实现起来比较复杂 |
AUTO | 数据库ID自增,这种策略适合在数据库服务器只有1台的情况下使用,不可作为分布式ID使用 |
ASSIGN_UUID | 可以在分布式的情况下使用,而且能够保证唯一,但是生成的主键是32位的字符 串,长度过长占用空间而且还不能排序,查询性能也慢 |
ASSIGN_ID | 可以在分布式的情况下使用,生成的是Long类型的数字,可以排序性能也高,但是 生成的策略和服务器时间有关,如果修改了系统时间就有可能导致出现重复主键 |
2. 多记录操作
实际业务中数据删除带来的问题:统计报表
3. 逻辑删除
-
修改数据库表添加deleted列
-
实体类添加属性
@Data @NoArgsConstructor @AllArgsConstructor //实体类映射的表 @TableName("user") public class User { //使用数据库自增策略 @TableId(type = IdType.AUTO) private Long id; private String name; //不查询的字段 @TableField(select = false) private String password; private Integer age; private String tel; //不存在的字段 @TableField(exist = false) private Integer online; @TableLogic(value = "0", delval = "1") private Integer deleted; } 注:逻辑删除的本质其实是修改操作;如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段
逻辑删除全局配置:
mybatis-plus: global-config: db-config: # 逻辑删除字段名 logic-delete-field: deleted # 逻辑删除字面值:未删除为0 logic-not-delete-value: 0 # 逻辑删除字面值:删除为1 logic-delete-value: 1
4. 乐观锁
实现业务并发现象带来的问题:秒杀
-
数据库表添加version列
-
在实体类中添加属性
@Data @NoArgsConstructor @AllArgsConstructor //实体类映射的表 @TableName("user") public class User { ... @Version private Integer version; } -
添加乐观锁的拦截器
@Configuration public class MyBaitsPlusConfig { /** * MyBatisPlus拦截器 * * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { //创建拦截器对象 MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); ... //添加乐观锁拦截器 mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } } -
测试
@Test void testUpdate(){ //1.先通过要修改的数据id将当前数据查询出来 User user = userDao.selectById(3L); //version=3 User user2 = userDao.selectById(3L); //version=3 user2.setName("Jock aaa"); userDao.updateById(user2); //verion=3条件成立,执行成功=>version=4 user.setName("Jock bbb"); userDao.updateById(user); //verion=3条件不成立,执行失败 }
具体实现可参看官方文档:
https://baomidou.com/pages/0d93c0/#optimisticlockerinnerinterceptor
四、快速开发(代码生成器)
代码生成器包含以下内容:
- 模板: MyBatisPlus提供,可以自己提供,但是麻烦,不建议
- 数据库相关配置:读取数据库获取表和字段信息
- 开发者自定义配置:手工配置,比如ID生成策略
使用步骤:
-
在创建SpringBoot项目时勾选需要的技术集
-
导入相应坐标
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <!-- MyBatisPlus代码生成器--> <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> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.springmvc_restful</groupId> <artifactId>springmvc_restful</artifactId> <version>1.0-SNAPSHOT</version> </dependency> -
配置代码生成器
/** * @Author: XIYAN * @Date: 2023/3/22 14:50 * @注释:MyBatisPlus代码生成器 */ public class CodeGenerator { public static void main(String[] args) { //1.创建代码生成器对象AutoGenerator AutoGenerator autoGenerator = new AutoGenerator(); //================================================ //2.设置数据库相关配置 //创建yamlBean实例工厂对象 YamlPropertiesFactoryBean ymlBean = new YamlPropertiesFactoryBean(); //读取配置文件类路径 ymlBean.setResources(new ClassPathResource("application.yml")); //读取配置文件 Properties properties = ymlBean.getObject(); //创建数据库连接源对象 DataSourceConfig dataSourceConfig = new DataSourceConfig(); //设置MySQL驱动 dataSourceConfig.setDriverName(properties.getProperty("spring.datasource.driver-class-name")); //设置数据库链接 dataSourceConfig.setUrl(properties.getProperty("spring.datasource.url")); //设置用户名 dataSourceConfig.setUsername(properties.getProperty("spring.datasource.username")); //设置密码 dataSourceConfig.setPassword(properties.getProperty("spring.datasource.password")); autoGenerator.setDataSource(dataSourceConfig); //================================================ //3.设置全局配置 //创建全局配置对象 GlobalConfig globalConfig = new GlobalConfig(); //设置代码生成位置 globalConfig.setOutputDir(System.getProperty("user.dir") + "\\springboot_generator\\src\\main\\java"); //设置生成完毕后是否打开生成代码所在的目录 globalConfig.setOpen(false); //设置作者 globalConfig.setAuthor("XIYAN"); //设置是否覆盖原始生成的文件 globalConfig.setFileOverride(true); //设置数据层接口名,%s为占位符,指代模块名称 globalConfig.setMapperName("%sDao"); //设置Id生成策略 globalConfig.setIdType(IdType.AUTO); autoGenerator.setGlobalConfig(globalConfig); //================================================ //4.设置包名相关配置 //创建包名配置对象 PackageConfig packageConfig = new PackageConfig(); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径 packageConfig.setParent("com.springboot_generator"); //设置实体类包名 packageConfig.setEntity("model"); //设置数据层包名 packageConfig.setMapper("dao"); autoGenerator.setPackageInfo(packageConfig); //================================================ //5.设置策略相关配置 //创建策略配置对象 StrategyConfig strategyConfig = new StrategyConfig(); //设置当前参与生成的表名,参数为可变参数,不写默认为当前数据库连接对象的所以表 //strategyConfig.setInclude("tbl_user"); //设置数据库表的前缀名称,模块名 =数据库表名 - 前缀名 例如: User = tbl_user - tbl_ //strategyConfig.setTablePrefix("tbl_"); //设置是否启用Rest风格 strategyConfig.setRestControllerStyle(true); //设置乐观锁字段名 strategyConfig.setVersionFieldName("version"); //设置逻辑删除字段名 strategyConfig.setLogicDeleteFieldName("deleted"); //设置是否启用lombok strategyConfig.setEntityLombokModel(true); autoGenerator.setStrategy(strategyConfig); //================================================ //6.执行代码生成器 autoGenerator.execute(); } }
注意:代码生成器生成的Mapper.xml中对于MyBatis的环境是没有进行配置的,如果想要运行, 需要将配置文件中的内容进行完善后在运行,即配置MyBatis:MyBatis
附:对于Service层里MyBatisPlus封装的方法有哪些方法可用,可参看官方文档:
https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3
所有的笔记来源于:黑马程序员的个人空间_哔哩哔哩_bilibili
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通