一.MyBatisPlus入门
<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.16</version>
</dependency>
注意事项1:由于mp并未被收录到idea的系统内置配置,无法直接选择加入
注意事项2:如果使用Druid数据源,需要导入对应坐标
(类名与表名对应,属性名与字段名对应)
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
id bigint(20) primary key auto_increment,
name varchar(32) not null,
password varchar(32) not null,
age int(3) not null ,
tel varchar(32) not null
);
insert into user values(null,'tom','123456',12,'12345678910');
insert into user values(null,'jack','123456',8,'12345678910');
insert into user values(null,'jerry','123456',15,'12345678910');
insert into user values(null,'tom','123456',9,'12345678910');
insert into user values(null,'snake','123456',28,'12345678910');
insert into user values(null,'张益达','123456',22,'12345678910');
insert into user values(null,'张大炮','123456',16,'12345678910');
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
//自行添加getter、setter、toString()等方法
}
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
username: root
password: root
package com.itheima.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserDao extends BaseMapper<User> {
}
package com.itheima;
import com.itheima.dao.UserDao;
import com.itheima.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
public class Mybatisplus01QuickstartApplicationTests {
@Autowired
private UserDao userDao;
@Test
void testGetAll() {
List<User> userList = userDao.selectList(null);
System.out.println(userList);
}
}
二.MyBatisPlus概述
MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
特性
无侵入:只做增强不做改变,不会对现有工程产生影响
强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作
支持 Lambda:编写查询条件无需担心字段写错
支持主键自动生成
内置分页插件
……
操作
三.MyBatisPlus分页功能
①:设置分页拦截器作为Spring管理的bean
package com.itheima.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 MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1 创建MybatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
//2 添加分页拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mpInterceptor;
}
}
②:执行分页查询
//分页查询
@Test
void testSelectPage(){
//1 创建IPage分页对象,设置分页参数
IPage<User> page=new Page<>(1,3);
//2 执行分页查询
userDao.selectPage(page,null);
//3 获取分页结果
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());
}
四.开启MyBatisPlus日志
# 开启mp的日志(输出到控制台)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
五.解决日志打印过多问题
1.取消初始化spring日志打印
做法:在resources下新建一个logback.xml文件,名称固定,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
2.取消SpringBoot启动banner图标
spring:
main:
banner-mode: off # 关闭SpringBoot启动图标(banner)
3. 取消MybatisPlus启动banner图标
# mybatis-plus日志控制台输出
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: off # 关闭mybatisplus启动图标
六.DQL编程控制
1.条件查询方式
-
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合
- QueryWrapper<泛型> qw=new QueryWrapper<>();
- qw.条件
- 数据接口名.CRUD(qw)
//方式一:按条件查询
QueryWrapper<User> qw=new QueryWrapper<>();
qw.lt("age", 18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
lambda按条件查询
//方式二:lambda格式按条件查询
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 10);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
lambda按条件查询(推荐)
- LambdaQueryWrapper<泛型> lqw = new LambdaQueryWrapper<User>();
- lqw.条件//里面的条件的字段用 泛型::xxx
- 数据接口名.CRUD(lqw)
//方式三:lambda格式按条件查询
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
2.组合条件
并且关系(and)
条件用.进行并且操作
//并且关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//并且关系:10到30岁之间
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
或者关系(or)
条件用.or.进行或者关系
//或者关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者关系:小于10岁或者大于30岁
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
3.NULL值处理
如下搜索场景,在多条件查询中,有条件的值为空应该怎么解决?
if语句控制条件追加
Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if(minAge!=null){
lqw.gt(User::getAge, minAge);
}
if(maxAge!=null){
lqw.lt(User::getAge, maxAge);
}
List<User> userList = userDao.selectList(lqw);
userList.forEach(System.out::println);
条件参数控制
Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//参数1:如果表达式为true,那么查询才使用该条件
lqw.gt(minAge!=null,User::getAge, minAge);
lqw.lt(maxAge!=null,User::getAge, maxAge);
List<User> userList = userDao.selectList(lqw);
userList.forEach(System.out::println);
条件参数控制(链式编程)
Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//参数1:如果表达式为true,那么查询才使用该条件
lqw.gt(minAge!=null,User::getAge, minAge)
.lt(maxAge!=null,User::getAge, maxAge);
List<User> userList = userDao.selectList(lqw);
userList.forEach(System.out::println);
判断是否空在拼接条件
- lqw.gt/lt( null!=xx.get属性,泛型::get属性,泛型.get属性)
- .gt/lt( null!=xx.get属性,泛型::get属性,泛型.get属性2);
当只有一条数据时,采用 XX 名 = xxDao.selectOne(lqw)
常用条件
eq 等于
lqw.eq(泛型::get属性,“值”);可以链式编程,下面两个类似
le ge 带等号
lt gt 不带等号
between 在a和b 之间
lqw.between(泛型::get属性,“小值”,“大值”) 注意:不可以写反位置
like 模糊匹配
lqw.like(泛型::get属性,“值”)
likeRight 右边是%
likeLeft 左边是%
4.查询投影-设置【查询字段、分组、分页】
1.只用lambda格式
lqw.select(泛型::get属性,泛型::get属性,..);
2.非lambda格式
qw.select("属性名","属性名","属性名",。。)
包含真的列,或是聚合列
1.qw.select("count(*) as count");
2.qw.groupBy("属性名");
List<Map<String,Obiect>> 名=xxDao.selectMaps(qw);
注意:只能写实体类中带有的属性,或聚合的属性,其他属性必须采用原始复杂的方式
查询结果包含模型类中部分属性
/*LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getId, User::getName, User::getAge);*/
//或者
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("id", "name", "age", "tel");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
查询结果包含模型类中未定义的属性
QueryWrapper<User> lqw = new QueryWrapper<User>();
lqw.select("count(*) as count, tel");
lqw.groupBy("tel");
List<Map<String, Object>> userList = userDao.selectMaps(lqw);
System.out.println(userList);
5.查询条件匹配选择
-
用户登录(eq匹配)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);
-
购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
-
查信息,搜索新闻(非全文检索版:like匹配)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//模糊匹配 like
lqw.likeLeft(User::getName, "J");
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
-
统计报表(分组查询聚合函数)
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("gender","count(*) as nums");
qw.groupBy("gender");
List<Map<String, Object>> maps = userDao.selectMaps(qw);
System.out.println(maps);
查询API
6.字段映射
- 不一致时使用@TableField(value="数据库的字段名")放在属性名上方
- 添加数据库中未定义的属性使用@TableField(exist=false)放在属性名上方
- 默认查询开放更多的字段查看权限@TableField(select=false)放在属性名上方,无法被查询
7.表名映射
表名与实体类(泛型)的名字不一致@TableName("数据库表名")放在类上面
七.DML编程控制
1. id生成策略(Insert)
主键生成的策略有哪几种方式?
1.id生成策略
@TableId(type = IdType.类型) 放在id属性上
- AUTO(0):使用数据库id自增策略控制id生成
- NONE(1):不设置id生成策略
- INPUT(2):用户手工输入id
- ASSIGN_ID(3):雪花算法生成id(可兼容数值型与字符串型) 64位二进制的串Long类型,占位符1位,41位时间戳,10位机器码(5位表示群组的标记,5位机器的标记),12位序列号
- ASSIGN_UUID(4):以UUID生成算法作为id生成策略
2.全局策略配置
mybatis-plus:
global-config: #全局配置
db-config:
id-type: assign_id #id采用雪花算法
table-prefix: tbl_ #表的前缀
2.多记录操作(批量Delete/Select)
先将列表集合中加入id
List<Long> list = new ArrayList<>();
list.add(值1);
1.主键删除多条记录
接口数据名.selectBatchIds(list)
2.主键查询多条记录
接口数据名.deletetBatchIds(list)
3.逻辑删除
- 数据库表中添加逻辑删除标记字段
- 实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
//逻辑删除字段,标记当前记录是否被删除
@TableLogic
private Integer deleted;
配置逻辑删除字面值
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_ #可省略数据库表的前缀,让后面的字段与实体类对应
logic-delete-field: deleted # 逻辑删除字段名
logic-not-delete-value: 0 # 逻辑删除字面值:未删除为0
logic-delete-value: 1 # 逻辑删除字面值:删除为1
逻辑删除本质:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。
4. 乐观锁(Update)
乐观锁(秒杀)具体实现步骤
1.数据库表中添加锁标记字段
version可赋默认值1
2.实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
@Version
private Integer version;
3.配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mpInterceptor() {
//1.定义Mp拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
//2.添加乐观锁拦截器
mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mpInterceptor;
}
}
4.使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行
@Test
public void testUpdate() {
/*User user = new User();
user.setId(3L);
user.setName("Jock666");
user.setVersion(1);
userDao.updateById(user);*/
//1.先通过要修改的数据id将当前数据查询出来
//User user = userDao.selectById(3L);
//2.将要修改的属性逐一设置进去
//user.setName("Jock888");
//userDao.updateById(user);
//1.先通过要修改的数据id将当前数据查询出来
User user = userDao.selectById(3L); //version=3
User user2 = userDao.selectById(3L); //version=3
user2.setName("Jock aaa");
userDao.updateById(user2); //version=>4
user.setName("Jock bbb");
userDao.updateById(user); //verion=3?条件还成立吗?
}