学习笔记及梳理来自尚硅谷Hellen老师视频观后感
1.简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。详情见官网,关键之处在特性。
- 润物无声
只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。 - 效率至上
只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。 - 丰富功能
热加载、代码生成、分页、性能分析等功能一应俱全
2.框架结构
3.快速配置环境
- 导入依赖
<!--mybatis-plus-->
<!--引入MP后就不要再引入Mybatis了-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--mysql运行时依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok,第一次使用时记得安装插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
- 配置application.properties文件,yml会更加合适
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
- 在主类中添加@MapperScan注解,也可以自己写一个MP的配置类
@MapperScan("com.springboot.mybatis_plus.mapper")
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
到此这三步已经完成了MP的配置!
4.测试CRUD
- 随便写一个实体类测试一下
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
- 创建mapper包,编写Mapper接口UserMapper
//IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
//为了避免报错,可以在 dao 层 的接口上添加 @Repository
@Repository
public interface UserMapper extends BaseMapper<User> {
//继承的BaseMapper类中有许多现成的CRUD方法,非常好用
}
接下来就可以进行注入测试了
- 测试CRUD
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void testSelectList() {
//UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
//所以不填写就是无任何条件
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
//查询指定id
User user = userMapper.selectById(1);
System.out.println(user);
}
@Test
public void testInsert(){
User user = new User();
user.setName("ly").setAge(22).setEmail("578104130@qq.com");
int insert = userMapper.insert(user);
System.out.println("影响的行数" + insert);
System.out.println("id" + user);
}
@Test
public void testUpdate(){
User user = new User();
user.setId((long) 7).setName("wxy");
int insert = userMapper.updateById(user);
System.out.println("影响的行数" + insert);
System.out.println("id" + user);
}
@Test
public void testDeleteById(){
int result = userMapper.deleteById(5L);
System.out.println(result);
}
}
- 查看sql日志
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
5.MP的特性
1) 主键策略
MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法),具体还有哪些主键策略,可以点进去IdType查看
@TableId(type = IdType.ASSIGN_ID)
private String id;
AUTO 自增策略,需要在创建数据表的时候设置主键自增,实体字段中配置 @TableId(type = IdType.AUTO)
@TableId(type = IdType.AUTO)
private Long id;
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
2) 自动填充
实体类添加字段并添加自动填充注解
//自动填充
@TableField(fill = FieldFill.INSERT)
private Date createTime;
//@TableField(fill = FieldFill.UPDATE)
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
实现元对象处理器接口
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
//首次插入的时候为两个字段赋值
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//更新的时候只需要修改updateTime
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
这样就实现了创建时间和修改时间的自动填充功能
3) 乐观锁插件
锁大家一定不陌生,当不同的用户同时读写同一份数据的时候,就会出现很多问题。锁分为两种,悲观锁(用户一处理数据就封锁住,不让其他人操作)和乐观锁(通过给数据加版本号)。
实现步骤
- 数据库中添加version字段
- 取出记录时,获取当前version
- 更新时,version + 1,如果where语句中的version版本不对,则更新失败
具体实现
- 修改实体类,添加 @Version 注解
@Version
private Integer version;
- 创建MP的配置文件并注册乐观锁到IOC容器中
@Configuration
@MapperScan("com.springboot.mybatis_plus.mapper")//可以将主启动类的扫描包配置在这里
public class MyBatisPlusConfig {
//乐观锁插件,本质为拦截器,加@Version注解
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
4) 分页插件
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
- 添加分页插件,配置类中添加@Bean配置
//分页插件,本质为拦截器
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
- 测试
@Test
public void testSelectPage() {
Page<User> page = new Page<>(1,5);
Page<User> pageParam = userMapper.selectPage(page, null);
pageParam.getRecords().forEach(System.out::println);
System.out.println(pageParam.getCurrent());
System.out.println(pageParam.getPages());
System.out.println(pageParam.getSize());
System.out.println(pageParam.getTotal());
System.out.println(pageParam.hasNext());
System.out.println(pageParam.hasPrevious());
}
结果如下
5) 逻辑删除(假删除)
- user表添加 deleted字段
ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT false
- 实体类修改,添加deleted 字段,并加上 @TableLogic 注解
@TableLogic
private Integer deleted;
- application.properties 加入以下配置,此为默认值,如果你的默认值和mp默认的一样,该配置可无
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
- 测试后发现,数据并没有被删除,deleted字段的值由0变成了1,测试后分析打印的sql语句,是一条update语句
注意:被删除前,数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
6.条件构造器
- 类图(该图来自尚硅谷Helen老师的讲义)
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : 查询条件封装
UpdateWrapper : Update 条件封装
AbstractLambdaWrapper : 使用Lambda 语法
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper - 测试
1)ge、gt、le、lt、isNull、isNotNull
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
2)eq、ne 注意seletOne()返回的是一条实体记录,当出现多条时会报错
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);//只能返回一条记录,多余一条则抛出异常
System.out.println(user);
}
3)between、notBetween 包含大小边界
@Test
public void testSelectCount() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(queryWrapper); //返回数据数量
System.out.println(count);
}
4)like、notLike、likeLeft、likeRight
selectMaps()返回Map集合列表,通常配合select()使用
@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.select("name", "age")
.like("name", "e")
.likeRight("email", "5");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}
5)in、notIn、inSql、notinSql、exists、notExists
- notIn(“age”,{1,2,3})—>age not in (1,2,3)
- 实现子查询
- inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3)
@Test
public void testSelectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.in("id", 1, 2, 3);
queryWrapper.inSql("id", "select id from user where id <= 3");
List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
objects.forEach(System.out::println);
}
6)or、and
注意:这里使用的是 UpdateWrapper 不调用or则默认为使用 and 连
@Test
public void testUpdate1() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
7)lambda表达式,lambda表达式内的逻辑优先运算
@Test
public void testUpdate2() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "n")
.or(i -> i.like("name", "a").eq("age", 20));
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
8)orderBy、orderByDesc、orderByAsc
@Test
public void testSelectListOrderBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age", "id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
9)set、setSql,最终的sql会合并 user.setAge(),以及userUpdateWrapper.set() 和 setSql() 中 的字段
@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(60);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "Peter")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '123@qq.com'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}
分页
// 方法一
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page, null)
page.getRecords().forEach(System.out::println);
System.out.println(page.getTotal());
// 方法二
QueryWrapper<UniversityEntity> wrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(key)) {
wrapper.eq("u_id",key)
.or().like("u_code",key)
.or().like("u_name", key)
.or().like("province",key);
}
IPage<UniversityEntity> page = this.page(new Query<UniversityEntity>().getPage(params,"sort",true), wrapper);
//TODO 返回的totalCount为0
return new PageUtils(page);
7) 查询方式
查询方式 | 说明 |
---|---|
setSqlSelect | 设置 SELECT 查询字段 |
where | WHERE 语句,拼接 + WHERE 条件 |
and | AND 语句,拼接 + AND 字段=值 |
andNew | AND 语句,拼接 + AND (字段=值) |
or | OR 语句,拼接 + OR 字段=值 |
orNew | OR 语句,拼接 + OR (字段=值) |
eq | 等于= |
allEq | 基于 map 内容等于= |
ne | 不等于<> |
gt | 大于> |
ge | 大于等于>= |
lt | 小于< |
le | 小于等于<= |
like | 模糊查询 LIKE |
notLike | 模糊查询 NOT LIKE |
in | IN 查询 |
notIn | NOT IN 查询 |
isNull | NULL 值查询 |
isNotNull | IS NOT NULL |
groupBy | 分组 GROUP BY |
having | HAVING 关键词 |
orderBy | 排序 ORDER BY |
orderAsc | ASC 排序 ORDER BY |
orderDesc | DESC 排序 ORDER BY |
exists | EXISTS 条件语句 |
notExists | NOT EXISTS 条件语句 |
between | BETWEEN 条件语句 |
notBetween | NOT BETWEEN 条件语句 |
addFilter | 自由拼接 SQL |
last | 拼接在最后,例如:last(“LIMIT 1”) |
更多技术博客请关注微信公众号:WHICH工作室