1、mp是什么
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生
只做增加不做改变,引入它不会对现有工程产生影响
效率至上
只需要简单配置,即可快速进行CRUD操作,从而节省大量时间。
功能丰富
热加载、代码生成、分页、性能分析等功能一应俱全
2、mp实现添加修改删除查询
3、mp自动填充乐观锁
4、mp逻辑删除
5、mp分页查询
一、MyBatis-Plus入门
1、创建数据库,创建数据库表
CREATE TABLE user
(
id BIGINT(20)NOT NULL COMMENT '主键ID',
NAME VARCHAR(30)NULL DEFAULT NULL COMMENT '姓名',
age INT(11)NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50)NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
INSERT INTO user (id, name, age, email)VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
2、创建工程springboot
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
3、在项目引入mp和相关的依赖
<!-- mybatis-plus--> <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> </dependency> <!-- lombok用来简化实体类--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
4、配置数据库信息application.properties
#spring boot2.0内置jdbc5驱动 #mysql数据库连接 #spring.datasource.driver-class-name=com.mysql.jdbc.Driver #spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false #spring.datasource.username=root #spring.datsource.password=root #spring boot2.1及以上(内置jdbc8驱动) 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=root
5、编写核心代码
package com.atguigu.demomptest.mapper; import com.atguigu.demomptest.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; public interface UserMapper extends BaseMapper<User> { }
6、添加Mapper扫描
package com.atguigu.demomptest; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.atguigu.demomptest.mapper") public class DemomptestApplication { public static void main(String[] args) { SpringApplication.run(DemomptestApplication.class, args); } }
7、测试
package com.atguigu.demomptest; import com.atguigu.demomptest.entity.User; import com.atguigu.demomptest.mapper.UserMapper; 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 class DemomptestApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { List<User> users = userMapper.selectList(null); System.out.println(users); } }
查看sql输出日志
#mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
添加操作
//添加 @Test public void testAdd(){ User user = new User(); user.setName("lucy"); user.setAge(20); user.setEmail("123@qq.com"); int insert = userMapper.insert(user); }
//
二、主键策略
ASSIGN_ID 19位随机值 ASSIGN_UUID 随机值 AUTO 自动增长 INPUT 指定 NONE 没有策略,需要指定
2.1ASSIGN_ID
Mybatis-Plus默认的主键策略是:ASSIGN_ID(使用雪花算法)
主键策略使用在entity
public class User { @TableId(type= IdType.ASSIGN_ID)
雪花算法:分布式ID生成器
雪花算法是由Twitter公布式主键生成算法,它能够保证不同表的主键不重复性,以及相同表的主键的有序性。
核心思想:
长度共64bit(一个long)型
首先是一个符号位,1bit标识,由long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
41bit时间戳(毫秒级),存储的时间戳的差值(当前时间戳-开始时间戳),结果约等于69.73年
10bit作为机器的ID(5个bit是数据中心 ,5个bit的机器ID,可以部署在1024个节点)
12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以生产4096个ID)
要想影响到所有实体的配置,可以设置全局主键配置(application.properties)
#全局设置主键生成策略 mybatis-plus.global-config.db-config.id-type=auto
如果没有配置策略,则默认为ASSIGN_ID
三、自动填充
需要描述:
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作
1.1数据库修改
在User表中添加datetime类型的新字段create_time、update_time
1.2实体类修改
实体上增字段并添加自动填充注释
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
1.3实现元对象处理器接口
package com.atguigu.demomptest.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; /** * @ClassName MyMetaObjectHandler * @Description * @Author 小黄debug * @Date 2022/4/14 11:14 * @Version 1.0 **/ @Component public class MyMetaObjectHandler implements MetaObjectHandler { //mp执行添加操作,这个方法执行 @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); //ByName根据名称,Field 属性,Val值 //第一个参数为需要修改的字段,第二个参数为默认值,第三个默认为传入的参数即可 } //mp执行修改操作,这个方法执行 @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime",new Date(),metaObject); } }
四、乐观锁
1、场景
主要适用的场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
什么是乐观锁:
作用:用于解决更新丢失问题
取出记录时,获取当前version
执行更新时,set version = new Version where version = oldVersion
如果version不对,就更新失败
2、乐观锁实现流程
2.1修改实体类和表,添加version字段
添加@Version注解
@Version @TableField(fill = FieldFill.INSERT) private Integer version;
给元对象处理器接口MyMetaObjectHandler在insert方法添加,此动作为version在创建时添加默认值
this.setFieldValByName("version",1,metaObject);
2.2创建配置文件
创建config,创建文件MybatisPlusConfig.java
注意类上面需要添加
@Configuration
@MapperScan("com.atguigu.demomptest.mapper")
此时可以删除主类中的@MapperScan扫描注解
2.3注册乐观锁插件
package com.atguigu.demomptest.config; import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @ClassName MpConfig * @Description * @Author 小黄debug * @Date 2022/4/14 14:26 * @Version 1.0 **/ @Configuration @MapperScan("com.atguigu.demomptest.mapper") public class MpConfig { /** * 乐观锁 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); //此处如果使用新版mp3.4.3就要使用MybatisPlusInterceptor } }
2.4、测试乐观锁
//测试乐观锁 @Test public void testOptimisticLocker(){ //根据id查询 User user = userMapper.selectById(1514495857628672001L); //修改 user.setName("张三"); //这里版本会自动更新版本号 userMapper.updateById(user); }
结果数据有更新,且新版本号为老版本号+1
五、查询
1、查询
1.1通过多个id批量查询
//多个id批量查询 @Test public void testSelect(){ List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3,99)); System.out.println(users); }
1.2简单的条件查询
//多个id批量查询 @Test public void testSelect2(){ Map<String,Object> columnMap = new HashMap<>(); columnMap.put("name","Jack"); columnMap.put("age",20); List<User> users = userMapper.selectByMap(columnMap); System.out.println(users); }
2、分页
2.1分页插件
1)配置分布插件,在MyConfig类中添加以下内容
/** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); }
2)编写分页代码
2.1)插件Page对象,传入两个参数
* 当前页
* 每页记录数
//分页查询 @Test public void testSelectPage(){ Page<User> page = new Page(1,3); //selectPage(page,queryWrapper),queryWrapper查询条件 Page<User> userPage = userMapper.selectPage(page, null); //返回对象得到分页所有数据 long pages = userPage.getPages();//总页数 long current = userPage.getCurrent();//当前页 List<User> records = userPage.getRecords();//当前数据 long total = userPage.getTotal();//总记录数 boolean hasNext = userPage.hasNext();//下一页 boolean hasPrevious = userPage.hasPrevious();//上一页 System.out.println(pages); System.out.println(current); System.out.println(records); System.out.println(total); System.out.println(hasNext); System.out.println(hasPrevious); }
2.2)调用mp的方法实现分页
2.2测试selectMapperPage
六、删除与逻辑删除
1、删除
1.1、根据id删除
//根据id删除 @Test public void testDeleteById(){ int i = userMapper.deleteById(1L); System.out.println(i); }
1.2、批量删除
//批量删除 @Test public void testDeleteBatchIds(){ int i = userMapper.deleteBatchIds(Arrays.asList(2, 3)); System.out.println(i); }
1.3、简单条件删除
//简单条件删除 @Test public void testDeleteByMap(){ HashMap<String,Object> map = new HashMap<>(); map.put("name","lucymary"); map.put("age",22); int i = userMapper.deleteByMap(map); System.out.println(i); }
2、逻辑删除
2.1、物理删除和逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后 查询不到此条被删除数据
逻辑删除:假删除,将对应数据中代表是否被邮件字段状态修改为"被删除状态",之后在数据库中仍旧能看到此条数据记录
逻辑删除的使用场景:可以进行数据恢复,有关联数据,不便删除
2.2、逻辑删除实现流程
2.2.1、数据库修改
表添加字段,对应实体类添加属性,作为逻辑删除标志deleted
2.2.2、实体类修改
在实体类逻辑删除属性上添加一个注解
@TableLogic @TableField(fill = FieldFill.INSERT) private Integer deleted;
@TableLogic表示逻辑删除,仅用于自动注入的sql,会将delete语句变成update
2.2.3、配置(可选)
在MyMetaObjectHandler的insertFill类里加上
this.setFieldValByName("deleted",0,metaObject);
2.2.4、测试
添加一条数据,查看deleted的状态是否为0
使用测试删除,查看该条数据的deleted状态是否为1,为1表示逻辑删除成功
2.2.5、测试逻辑删除后的查询
MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断
@Test void contextLoads() { List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }
七、条件构造器和常用接口
1、wapper介绍
Wrapper:条件构造抽象类,最顶端父类
AbstractWrapper:用于查询条件封装,生成sql的where条件
QueryWrapper:查询条件封装
UpdateWrapper:Update条件封装
AbstractLambdaWrapper:使用lambda语法
LambdaQueryWrapper:用于Lambda语法使用查询Wrapper
LambdaUpdateWrapper:Lambda更新封装Wrapper
@SpringBootTest
public class QueryWrapperTests{
@Autowired
private UserMapper userMapper;
}
2、测试用例
eq相等 ne、neq不相等, gt大于, lt小于 gte、ge大于等于 lte、le 小于等于 not非 mod求模 is [not] div by是否能被某数整除 is [not] even是否为偶数
2.1、ge、gt、le、lt、isNull、isNotNull
@Test public void testSelectge(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //ge、gt、le、lt、 queryWrapper.ge("age",21);//ge大于等于 List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
2.2、eq、ne
注意:selectOne()返回的是一条实体记录
@Test public void testSelecteq(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //ge、gt、le、lt、 queryWrapper.eq("name","张三");//eq,等于 List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
2.3、between、notBetween
@Test public void testSelectBetween(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //ge、gt、le、lt、 queryWrapper.between("age",24,28);//eq,等于 List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); //SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0 AND (age BETWEEN ? AND ?) }
注意:between是闭区间,是包含当前参数的,notBetween同理
2.4、like、notLike、likeLeft、likeRight
selectMaps()返回Map集合列表,通常配合select()使用
select * from user where name like '%张%'
@Test public void testSelectLike(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //ge、gt、le、lt、 queryWrapper.like("name","张");//ge大于等于 List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
2.5、orderyBy、orderByDesc、orderByAsc
@Test public void testSelectOrderByDesc(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //ge、gt、le、lt、 queryWrapper.orderByDesc("id");//ge大于等于 List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
3、查询方式
查询方式 说明
setSqlSelect 设置select查询字段
where where语句,拼接+Where条件
存在疑惑
动态查询和连接查询的实现