MP的使用
MyBatisPlus
前言
记录自己在使用MyBatisPuls时遇到的一些问题,一个找不到Bean的问题真的卡了我好久,一个上午都在解决这个问题,因为自己对于底层的实现并不了解,所以导致在报错的时候也不懂是怎么一回事😞,通过一上午的摸索,也对注解开发的过程有了一定的了解,记录下来怕自己下次又忘了,也是对自己知识的巩固
简介
MyBatisPlus(简称MP)是基于MyBatis框架上的增强开发工具,具有无侵入,支持lambda,内置通用Mapper,支持主键自动生成,内置分页插件等特性
开发方式
本文是基于SpringBoot使用MyBatisPlus,SpringBoot版本为3.4.0,Mp版本为3.5.7,可以兼容使用
1
2
3
4
5
6
7
8
9
10
11 <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
如何快速开发
快速开发实体类
1
2
3
4 ><dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>SpringBoot默认管理了lombok的版本依赖,不需要指定版本号,记得下载lombok插件
1
2
3
4
5
6
7
8
9
10
11
12 package com.dome.domain;
import lombok.Data;
public class User {
private Integer id;
private String username;
private String password;
private Integer role ;
private String myname;
}注意事项
导入lombok后在使用注解,这就实体类要写的所有代码,
@Data
不包含构造方法的注解,要用什么自己添上,Lombok对静态属性不会提供get、set方法, transient修饰实体类属性(修饰的属性不会被序列化),@TableField(exist=false)
,这个注解用来表示数据表中不存在该字段,默认是true
主键自增策略
MP的默认主键策略是基于雪花算法的自增主键,主键采用雪花算法生成值的前提是实体类的主键属性名称必须为id,数据表字段带有_的可以自动映射到驼峰式命名的属性上(t_user——》tUser)
- 数据库名不同,在类上增加@TableName(“mp_user”)
- 主键ID的驼峰一般无法识别,在主键属性上增加@TableId
- 属性与字段名不相同,在属性上增加@TableField(“name”)
也可以在
yml
配置文件中开启全局配置
逻辑删除和乐观锁
逻辑删除:在数据设置中有一个是否可用字段,如果要删除这条数据,就将该字段设置为不可用,数据仍然保留在数据库中
乐观锁:主要用于秒杀抢单,乐观锁查询记录时不会上锁,但是会在更新记录的时候去判断下有没有人去更新了这条记录,数据库中有一个字段记录了更新的值,每次更新该字段就自增
实现乐观锁需要在version字段上添加@Version注解
添加拦截器,一定要先查询再更新,不然乐观锁没法生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package com.dome.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;
public class MpConfig {
public MybatisPlusInterceptor mybatisPlusInterceptor(){
final MybatisPlusInterceptor mp = new MybatisPlusInterceptor();
//添加具体的拦截器,这是分页拦截器,开启分页查询功能
mp.addInnerInterceptor(new PaginationInnerInterceptor());
// 添加乐观锁插件,可以添加多个拦截器
mp.addInnerInterceptor(newOptimisticLockerInnerInterceptor());
return mp;
}
}
条件查询和分页查询
使用内置的查询方法进行查询时,实体类要实现序列化接口
Serialzable
普通查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 //方式一:按条件查询
QueryWrapper qw = new QueryWrapper();
qw.lt("age",18);
List<User> userList = userDao.selectList(qw);
System.out.println(userList);
//方式二: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<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 10);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
条件传递过来是空值怎么办
1
2
3
4
5
6
7
8
9 LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//先判定第一个参数是否为true,如果为true连接当前条件
//相当于不为空的话就链接uq.get出来的值
lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2());
lqw.gt(null != uq.getAge(),User::getAge, uq.getAge());
//链式链接
lqw.lt(null != uq.getAge2(),User::getAge, uq.getAge2())
.gt(null != uq.getAge(),User::getAge, uq.getAge());
List<User> userList = userDao.selectList(lqw);3.查询投影和分组聚合
1
2
3
4
5
6
7
8
9
10
11
12
13
14 //查询投影,选择自己想看的字段
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);
//分组查询聚合函数,不能用lambda
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);
LambdaQueryWrapper
和QueryWrapper
都是用来构建查询条件的,一个支持lambda表达式
代码生成器
官方代码
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 FastAutoGenerator.create("url", "username", "password")
.globalConfig(builder -> builder
.author("Baomidou")
.outputDir(Paths.get(System.getProperty("user.dir")) + "/src/main/java")
.commentDate("yyyy-MM-dd")
)
.packageConfig(builder -> builder
.parent("com.baomidou.mybatisplus")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.xml("mapper.xml")
)
.strategyConfig(builder -> builder
.entityBuilder()
.enableLombok()
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();导入的坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 package com.dome;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
public class Generator {
public static void main(String[] args) {
// 使用 FastAutoGenerator 快速配置代码生成器
FastAutoGenerator.create("jdbc:mysql://localhost:3306/my_book?serverTimezone=GMT%2B8", "username", "1234")
.globalConfig(builder -> {
builder.author("YU") // 设置作者
.outputDir("Mp_dome02_generator\\src\\main\\java"); // 输出目录
})
.packageConfig(builder -> {
builder.parent("com.xxx") // 设置父包名
.entity("domain") // 设置实体类包名
.mapper("dao") // 设置 Mapper 接口包名
.service("service") // 设置 Service 接口包名
.serviceImpl("service.impl") // 设置 Service 实现类包名
.xml("mappers"); // 设置 Mapper XML 文件包名
})
.strategyConfig(builder -> {
builder.addInclude("user") // 设置需要生成的表名
.entityBuilder()
.enableLombok() // 启用 Lombok
.enableTableFieldAnnotation() // 启用字段注解
.controllerBuilder()
.enableRestStyle(); // 启用 REST 风格
})
.templateEngine(new VelocityTemplateEngine()) // 使用 Velocity 模板引擎
.execute(); // 执行生成
}
}**这里使用的是velocity模板,如果要导入其他模板,跟换相应的坐标,代码生成器的版本要和上面导入的坐标兼容**
1
2
3
4
5
6
7
8
9
10
11
12
13
14 <!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.7</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.4.1</version>
</dependency>
遇到的问题
- 出现报错
Error creating bean with name
出现这个错误的原因是找不到bean,说明在Spring容器中并没有找到你自动装配的对象
一种是
Error creating bean with name 'dataSource' defined in class path resource
,这种就是数据库连接有问题,不要以为在代码中写了数据库用户密码就不要在配置中写了
1
2
3
4
5
6
7
8
9
10
11 mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath*:mapper/*Mapper.xml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/my_book?serverTimezone=UTC
username: username
password: 1234
- 还是找不到bean的问题,这次是找不到在
service
和mapper
包下的bean出现这种情况的原因是没有开启注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.dome;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
public class MpTestGeneratApplication {
public static void main(String[] args) {
SpringApplication.run(MpTestGeneratApplication.class, args);
}
}把他们加入到扫包的行列中就解决了找不到bean的问题,在mapper包下它并没有生成
@Mapper
注解,所以直接在主运行程序导入,但在service包下有@Service
注解它也还是会出现扫不到的问题,所以直接在主运行类上加入要扫的包是最保险的做法