目录
一、用前准备:
在创建SpringBoot时只需要勾选 mySQL驱动即可
1、导入依赖:以下版本基于SpringBoot 2.6.2
<!-- myBatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<!--连接池和mySQL驱动-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2、yml配置数据库:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/python?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
二、简单体验:
1、写一个Dao接口打上mapper注解。继承BaseMapper<>类,泛型写数据表对应的实体类。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.springbootstudy.db.pojo.StudentInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface testDao extends BaseMapper<StudentInfo> {
}
2、直接调用就会发现testDao的对象会有很多的内置方法:
三、分页查询:
1、分页
书写一个配置类用于myBatis的拦截器,直接复制粘贴即可
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 mpInterceptor(){
//1、定义mp拦截器
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//2、添加拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
由于小程序传来的数据是start=1 或start+count这种,比如每次请求五条,则第一次start = 0第二次等于5.....MybatisPlus的Ipage对象两个参数一个是起始页码,一个是请求条数,起始页码是从1卡开始的,所以必须对start进行处理将其转化为对应的页码
1、创建pageCounter对象保存page和count
/**bo用于对所需的业务对象进行封装*/
@Data
public class PageCounter {
private Integer page;
private Integer count;
}
2、写一个工具类用于转化start与page页码的关系:
通过这给类的转化,得到的page是从第1页开始的,就可以直接给MyBatisPlus使用了
public class CommonUtil {
public static PageCounter convertToPaeParameter(Integer start, Integer count){
Integer page = start/count+1;
PageCounter res = new PageCounter();
res.setPage(page);
res.setCount(count);
return res;
}
}
controller层:
@GetMapping("/latest")
public Paging<Spu> getLatestPagingSpu(@RequestParam(defaultValue = "0") Integer start,
@RequestParam(defaultValue = "10") Integer count) {
PageCounter pageCounter = CommonUtil.convertToPaeParameter(start, count);
return spuService.getLatestPagingSpu(pageCounter.getPage(), pageCounter.getCount());
}
service层:
@Override
public Paging<Spu> getLatestPagingSpu(Integer page, Integer count) {
//1、定义page对现象
IPage pages = new Page(page, count);
//2、定义查询条件
QueryWrapper<Spu> wrapper = new QueryWrapper<>();
//condition可以进行一个bool判断,当为true时,后面的都生效,否则后面写这个条件不生效
wrapper.orderBy(true, false, "create_time");
//3、开始执行查询:
IPage p = spuDao.selectPage(pages, wrapper);
return new Paging<Spu>(p.getTotal(), count, page, (int) p.getPages(), p.getRecords());
}
分页查询API:
@Test
void testByPage(){
IPage pages = new Page(1,2);
IPage page = tDao.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());
}
2、条件分页与排序:
分页查询的第二个参数上面写的是null,实际上他是进行条件查询,排序进行准备的。
@Test
public void test(Integer page, Integer count) {
//1、定义page对现象
IPage pages = new Page(page,count);
//2、定义查询条件
QueryWrapper<Spu> query = new QueryWrapper<>();
query.eq("online",1); //条件1:让online属性=1才进行分页排序
//属性二:排序
//第一个参数condition是一个布尔判断,当为true时,后面的排序生效,否则后面写这个排序不生效
//第二个参数,是否是正序,true正序,false倒序
//第三个参数是排序字段,字段名与数据库的列一致而非对应的实体类。
query.orderBy(true,false,"create_time");
//3、开始执行查询:
IPage p = spuDao.selectPage(pages, query);
}
四、条件查询:
前置知识:
lt | less than 小于 |
le | less than or equalto 小于等于 |
eq | equal to等于 |
ne | not equal 不等于 |
ge | greater than or equal to 大于等于 |
gt | greater then 大于 |
between | 查询[a,b]的范围 |
1、且条件查询:
链式调用和分开写都是 且 的条件
@Test //且条件查询
void queryTest(){
//1、创建一个lambda查询器
LambdaQueryWrapper<StudentInfo> lqw =new LambdaQueryWrapper<>();
//2、书写条件 查询成绩大于90 且 小于100
lqw.lt(StudentInfo::getScore,100);
lqw.gt(StudentInfo::getScore,90);
//也可以链式写法:
//lqw.lt(StudentInfo::getScore,100).gt(StudentInfo::getScore,90);
//3、查询器传递给查询方法
List<StudentInfo> list = tDao.selectList(lqw);
System.out.println(list);
}
2、或条件查询:
@Test //或条件查询
void queryTest(){
//1、创建一个lambda查询器
LambdaQueryWrapper<StudentInfo> lqw =new LambdaQueryWrapper<>();
//2、书写条件 查询成绩大大于90 或 小于30
lqw.lt(StudentInfo::getScore,30).or().gt(StudentInfo::getScore,90);
//3、查询器传递给查询方法
List<StudentInfo> list = tDao.selectList(lqw);
System.out.println(list);
}
3、null值查询:
StudentInfo studentInfo = new StudentInfo();
studentInfo.setScore(30);
//判断前面的条件,如果是true就链接后面的条件
lqw.lt(null != studentInfo.getScore(), StudentInfo::getScore, 50);
4、查询投影:
就是显示查询出来的部分字段,不是都显示全部字段
1)特定对象类型的投影查询:
@Test
//特定对象类型的投影查询
void queryTest() {
//1、创建一个lambda查询器
LambdaQueryWrapper<StudentInfo> lqw = new LambdaQueryWrapper<>();
//2、书写条件 分数 和 姓名
lqw.select(StudentInfo::getScore,StudentInfo::getStuname);
//3、查询器传递给查询方法
List<StudentInfo> list = tDao.selectList(lqw);
System.out.println(list);
}
2)map类型的投影查询:
(查询总数count(*)必须用这种方法)
@Test
//map类型投影查询
void queryTest2(){
//1、创建一个查询器
QueryWrapper<StudentInfo> qw =new QueryWrapper<>();
// qw.select("count(*) as count"); 查询所有
qw.select("stuName","score");
//2、查询器传递给查询方法
List<Map<String, Object>> maps = tDao.selectMaps(qw);
System.out.println(maps);
}
3、分组统计:
@Test
//分组统计:
void queryTest2(){
//1、创建一个lambda查询器
QueryWrapper<StudentInfo> qw =new QueryWrapper<>();
qw.select("count(*) as count","score");
//2、分组条件
qw.groupBy("score");
//3、查询器传递给查询方法
List<Map<String, Object>> maps = tDao.selectMaps(qw);
System.out.println(maps);
}
4、eq等于查询:
用于密码验证等场景
@Test
//LambdaQueryWrapper方式查询
void queryTest() {
//1、创建一个lambda查询器
LambdaQueryWrapper<StudentInfo> lqw = new LambdaQueryWrapper<>();
//2、查询分数为99且id为1的学生
lqw.eq(StudentInfo::getScore,99).eq(StudentInfo::getId,1);
//3、查询器传递给查询方法
StudentInfo one = tDao.selectOne(lqw); //selectOne专门用于查询一条数据
//查出来返回对象,查不出来返回null
System.out.println(one);
}
@Test
//QueryWrapper方式查询
void queryTest3(){
//1、创建一个lambda查询器
QueryWrapper<StudentInfo> qw =new QueryWrapper<>();
//2、查询条件
qw.eq("score",99);
//3、查询字段
qw.select("stuName","score");
//4、查询器传递给查询方法
List<Map<String, Object>> maps = tDao.selectMaps(qw);
System.out.println(maps);
}
5、模糊查询like:
对于Bottom来说like("B")表示B%,llike("B")表示%B.
@Test
//LambdaQueryWrapper方式模糊匹配
void queryTest() {
//1、创建一个lambda查询器
LambdaQueryWrapper<StudentInfo> lqw = new LambdaQueryWrapper<>();
//2、查询李*的学生
lqw.like(StudentInfo::getStuname,"李");
//3、查询器传递给查询方法
List<StudentInfo> list = tDao.selectList(lqw);
System.out.println(list);
}
6、映射匹配兼容性:
查询我们会发现:
1、数据库的表名与实体类的表名不一致
2、实体类有的字段表中不存在这个字段
3、实体类属性的名称与表的名称不匹配
7、根据数组条件查询:
前端传来一个数组,现在要通过该数组的数据进行条件查询,如果传来的是id,则有默认的selectById()方法,但是如果不是id是其他属性的数据,此时如果使用sql查询就是使用in关键字,所以在MybatisPlus中也相同。
//根据一组name来查询数据
public List<Theme> getByNames(List<String> nameList) {
QueryWrapper<Theme> wrapper = new QueryWrapper<>();
wrapper.lambda().in(Theme::getName,nameList);
return themeDao.selectList(wrapper);
}
五、主键生成策略:
主要策略:
lAUTO(0) | 使用数据库id自增策略控制id生成 |
lNONE(1) | 不设置id生成策略 |
lINPUT(2) | 用户手工输入id |
lASSIGN_ID(3) | 雪花算法生成id(可兼容数值型与字符串型) |
lASSIGN_UUID(4) | 以UUID生成算法作为id生成策略 |
雪花算法:
1、由64位二进制数组成
2、首位是0保证生成的数是正数
3、41位由当前时间戳生成
4、当同一时间戳收到了多条请求后面再加一段机器号防止重复
5、防止同一计器在同一时间收到多条请求出现了序列号,表示当前设备的第几个请求。
使用时候只需要在id上打上注解即可:
上述的表前缀和主键策略可以全局配置:
六、删除:
一)批量删除
之前在使用MyBatis的时候想要实现动态的批量删除需要使用forEach标签,操作十分复杂,有了MyBatis-Plus之后,我们只需要调用相应的API然后传入一个List即可实现批量删除。
二)逻辑删除:
逻辑删除:表面上好像是删除了,但是实际上执行的是update语句,是用一个字段作为标志,来表示这个数据是否被删除。
当执行查询语句时将不会再查询出来被逻辑删除的数据,因为Mybatis-Plus会自动加上一句 where deleted=0
下面的deleted字段就表示逻辑删除字段,1表示数据被删除,0表示数据没有被删除。
1、直接在实体类的属性上加注解:(不常用)
2、通用配置形式:
我们只需要在yml中进行配置即可:
1、在数据库中要有对应的deleted字段
2、一定要给deleted字段选上默认值选项
对于时间类型的逻辑删除:
如果数据库中的逻辑删除字段是一个时间,不仅仅有两种可能是由多种可能的,所以需要用到下面的方式:
当数据被删除后,该部分显示的是删除的时间。
1、配置Mybatis-Plus:
mybatis-plus:
global-config:
db-config:
# 逻辑删除(软删除),删除时添加当前时间,未删除是NULL
logic-delete-value: NOW()
logic-not-delete-value: 'NULL'
2、为对应字段添加注解:
@Data
public class BasePojo {
@TableLogic
private Date deleteTime;
private Date createTime;
private Date updateTime;
}
七、ServiceImpl<>类使用:
通过前面的学习我们知道MyBatis-Plus已经为我们提供了很多方便的方法供我们使用,但是我们发现对于简单的selectById()方法,我们需要controller层调用serviceImpl层,然后serviceImpl再调用对应的Mapper层,我们试想能不能直接在Controller调用这种简单的方法,此时需要用到ServiceImpl类。
1、将Mapper层打上@Mapper注解,同时继承BaseMapper类。
@Mapper
public interface BannerMapper extends BaseMapper<BannerDO> {
}
2、来到Service接口,继承IService类,泛型是对应的DO模型类(数据库映射出来的那个类)
此步是针对具有接口的风格,没有可以直接省略。
public interface BannerService extends IService<BannerDO> {
}
3、ServiceImpl真正业务类,继承ServiceImpl<>,第一个泛型是对应的Mapper接口,第二个是对应的DO类:
@Service
public class BannerServiceImpl extends ServiceImpl<BannerMapper,BannerDO> implements BannerService {
}
4、Controller类,直接将Service注入即可使用:
当我们 . 时会发现多了很多方法:
2、同时可以通过getBaseMapper来获取更多方法: