简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变(引入它不会对现有工程产生影响),为简化开发、提高效率而生(只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间热加载、代码生成、分页、性能分析等功能一应俱全)。
创建并初始化数据库
#创建数据库 mybatis_plus
create database mybatis_plus
#创建user表
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');
确定idea配置
java编译器
项目和文件编码
maven的配置
创建项目
使用 Spring Initializr 快速初始化一个 Spring Boot 工程
Group:com.aurora
Artifact:mybatis_plus
引入依赖
注意:引入 MyBatis-Plus 之后请不要再次引入 MyBatis,以避免因版本差异导致的问题。
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--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>
</dependencies>
idea中安装lombok插件
编写代码
1.配置
在 application.properties 配置文件中添加 MySQL 数据库的相关配置:
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
注意:
1.这里的 url 使用了 ?serverTimezone=GMT%2B8 后缀,因为8.0版本的jdbc驱动需要添加这个后缀,否则运行测试用例报告如下错误:
java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
2.这里的 driver-class-name 使用了 com.mysql.cj.jdbc.Driver ,在 jdbc 8 中 建议使用这个驱动,否则运行测试用例的时候会有 WARN 信息
2.启动类
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
@SpringBootApplication
@MapperScan("com.aurora.mptest.mapper") //因为mapper是动态生成实现类的对象 而动态生成对象默认找不到 所以需要MapperScan扫描才能找到
public class MptestApplication {
public static void main(String[] args) {
SpringApplication.run(MptestApplication.class, args);
}
}
3.添加实体
创建包 entity 编写实体类 User.java(此处使用了 Lombok 简化代码)
@Data //不需要写get和set方法 只需要加上@Data的注解 会生成get set方法
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
4.添加mapper
创建包 mapper 编写Mapper 接口: UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {
}
5.测试
添加测试类,进行功能测试:
@SpringBootTest
class DemomptestApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void findAll() {
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
}
执行单元测试 查询到数据
查看控制台输出:
查看sql输出日志
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
主键策略
1.插入操作
//添加
@Test
public void testAdd() {
User user = new User();
user.setName("lucy");
user.setAge(20);
user.setEmail("1243@qq.com");
int insert = userMapper.insert(user);
System.out.println(insert);
}
注意:数据库插入id值默认为:全局唯一id
MP的主键策略
MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法) 会生成一个19位的
雪花算法:分布式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)。
优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高。
AUTO 自增策略
需要在创建数据表的时候设置主键自增
实体字段中配置 @TableId(type = IdType.AUTO)
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
自动填充和乐观锁
更新操作
注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?
//修改
@Test
public void testUpdate() {
User user = new User();
user.setId(1421726743919628290L);
user.setName("antina");
int count = userMapper.updateById(user); //影响行数
System.out.println(count);
}
自动填充
数据库修改
在User表中添加datetime类型的新的字段 create_time、update_time
实体类修改
实体上增加字段并添加自动填充注解
@TableField(fill = FieldFill.INSERT)
private Date createTime; //create_time
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime; //update_time
实现元对象处理器接口
@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);
}
//mp执行修改操作,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
乐观锁
抢票 就是用乐观锁实现的 很多人抢同一张票 有一个人抢到了就把version改了 其他人就不能抢了
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
乐观锁实现流程
在表中添加一个version字段
修改实体类
添加 @Version 注解
@Version
private Integer version;
创建配置文件
创建包config,创建文件MybatisPlusConfig.java
此时可以删除主类中的 @MapperScan 扫描注解
@Configuration
@MapperScan("com.aurora.mptest.mapper")
public class MpConfig {
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
测试乐观锁
//测试乐观锁
@Test
public void testOptimisticLocker(){
//根据id查询
User user = userMapper.selectById(1L);
//修改稿
user.setName("张三");
//user.setVersion(user.getVersion()+1); 这个步骤不需要做 乐观锁会自动帮我们做
userMapper.updateById(user);
}
修改添加的数据的值 执行单元测试
version值默认加进去是1把李四的id 1421739524723089410 在测试类中进行修改
再测试一下乐观锁version的值变成了2 name值改成了张三
通过多个id批量查询
//多个id批量查询
@Test
public void testSelect1() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); //selectBatchIds里面是一个集合 集合就可以用List 或 Set
//asList可以传入多个值
System.out.println(users);
}
简单的条件查询
通过map封装查询条件
注意:map中的key对应数据库中的列名。如:数据库user_id,实体类是userId,这时map的key需要填写user_id
//简单条件查询
@Test
public void testSelect2() {
//根据Map构建条件 然后通过selectByMap 根据构建的条件进行查询
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("name","Jack");
columnMap.put("age",20);
List<User> users = userMapper.selectByMap(columnMap);
System.out.println(users);
}
分页插件
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
添加分页插件
配置类中添加@Bean配置
/**分页插件*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
测试selectPage分页
//分页查询
@Test
public void testSelectPage() {
Page<User> page = new Page(1,3);
// current 当前页 size 每页显示的条数
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);
}
根据id删除记录
//根据id删除
@Test
public void testDeleteById(){
int result = userMapper.deleteById(5L);
System.out.println(result);
}
批量删除
@Test
public void testSelect1() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); //selectBatchIds里面是一个集合 集合就可以用List 或 Set
//asList可以传入多个值
System.out.println(users);
}
简单条件删除
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
int result = userMapper.deleteByMap(map);
system.out.println(result);
}
物理删除和逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
逻辑删除的使用场景:
1.可以进行数据恢复
2.有关联数据,不便删除
逻辑删除实现流程:
1.数据库修改 添加 deleted字段
ALTERTABLE `user` ADD COLUMN `deleted` boolean DEFAULT false
2.实体类修改 添加deleted 字段,并加上 @TableLogic 注解
@TableLogic
private Integer deleted;
3.测试
测试后发现,数据并没有被删除,deleted字段的值由0变成了1
测试后分析打印的sql语句,是一条update
注意:被删除前,数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
@Test
public void testLogicDelete() {
int result = userMapper.deleteById(1L);
system.out.println(result);
}
测试逻辑删除后的查询
MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断
@Test
public void testLogicDeleteSelect() {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}