MyBatis-Plus
- 能够基于MyBatis-Plus完成标准Dao开发
- 能够掌握MyBatis-Plus的条件查询
- 能够掌握MyBatis-Plus的字段映射与表名映射
- 能够掌握id的生成策略
- 能够理解代码生成器的相关配置
# MyBatisPlus简介
# 入门案例
在开始学习MyBatis-Plus前,我们先参考MyBatis-Plus官网来做一个快速入门案例,通过这个案例来看下使用MyBatis-Plus到底有都简单。
- 创建数据库mp_db,并依照官网快速入门SQL脚本初始化数据表
DROP TABLE IF EXISTS 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');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
下面我们就从零开始使用MyBatis-Plus来实现该表的增删改查操作。
- 创建新模块,选择Spring Initializr初始化,并配置模块的相关基础信息
- 选择当前模块需要使用的技术集(仅保留MySQL驱动)
- 在pom.xml文件中手动引入MyBatis-Plus起步依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
2
3
4
5
完整pom.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>mybatisplus-01-quickstart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- MyBaits-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<!-- druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
注意事项
- 由于MyBatis-Plus并未被收录到Idea的系统内置配置当中,因此无法直接选择加入;
- 如果需要使用Druid数据库连接池,需要导入对应的依赖坐标。
- 将application.properties配置文件修改为application.yml,并在配置文件中添加如下内容:
server:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://localhost:3306/mp_db
2
3
4
5
6
7
8
9
10
- 在Spring Boot启动类中添加
@MapperScan
注解,扫描Mapper代理接口包路径
package com.itheima;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.itheima.mapper")
public class Application01 {
public static void main(String[] args) {
SpringApplication.run(Application01.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 编写实体类
User.java
,此处使用了Lombok插件,简化代码书写,使用Lombok需要引入对应的依赖,此处我们先这样写,后面在详细介绍一下Lombok
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
2
3
4
5
6
7
package com.itheima.domain;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
2
3
4
5
6
7
8
9
10
11
- 创建
com.itheima.mapper
包,并在该包下编写UserMapper
接口
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.domain.User;
public interface UserMapper extends BaseMapper<User> {
}
2
3
4
5
6
7
- 创建测试类,进行功能测试
package com.itheima.mapper;
import com.itheima.domain.User;
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
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
/**
* 测试新增
*/
@Test
public void testInsert() {
User user = new User();
user.setName("sunyy");
user.setAge(18);
user.setEmail("sunyy@itcast.cn");
userMapper.insert(user);
}
/**
* 测试修改
*/
@Test
public void testUpdate() {
User user = new User();
user.setId(1613360326898720770L);
user.setEmail("sunyy@itheima.cn");
userMapper.updateById(user);
}
/**
* 测试删除
*/
@Test
public void testDelete() {
userMapper.deleteById(1613360326898720770L);
}
/**
* 测试查询
*/
@Test
public void testSelect() {
// UserMapper中selectList()方法的参数为MP内置的条件封装器Wrapper
// 因此不填写就代表无任何条件
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
}
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
小结
通过以上几个简单的步骤,我们就实现了user
表的CRUD功能,甚至连XML文件都不用编写!由此我们可以看到继承MyBatis-Plus分层的简单,只需要引入starter起步依赖,配置mapper
扫描包路径即可,但MyBatis-Plus的强大还远不止这些功能,下面我们就来详细学习一下它。
# Lombok插件
*Lombok,是一个Java类库,提供了一组注解,用来简化POJO实体类的开发,使用lombok有两个前提:
- 在pom.xml文件中引入依赖坐标
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
2
3
4
5
6
7
- 在Idea中安装lombok插件
常用注解:@Data,为当前实体类在编译期设置对应的get/set
方法,无参构造方法,toString
方法,hashCode
方法,equals
方法等。
package com.itheima.domain;
import lombok.*;
/*
1 生成getter和setter方法:@Getter、@Setter
生成toString方法:@ToString
生成equals和hashcode方法:@EqualsAndHashCode
2 统一成以上所有:@Data
3 生成空参构造: @NoArgsConstructor
生成全参构造: @AllArgsConstructor
4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】
*/
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# MyBatis-Plus概述
MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本的CRUD,性能基本无损耗,直接面向对象操作
- 强大的CRUD操作:内置通用Mapper,通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求
- 支持Lambda形式调用:通过Lambda表达式,方便的编写各类查询条件,无需担心字段写错
- 支持主键自动生成:支持多达4种主键策略(内含分布式唯一ID生成器--
Sequence
),可自有配置,完美解决主键问题 - 支持ActiveRecord模式:支持ActiveRecord形式调用,实体类只需继承Model类即可进行强大的CRUD操作
- 支持自定义全局通用操作:支持全局通用方法注入(Write once, user anywhere)
- 内置代码生成器:采用代码或者Maven插件可以快速生成Mapper、Model、Service、Controller层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于MyBatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通
List
查询 - 分页插件支持多种数据库:支持MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer等多种数据库
- 内置性能分析插件:可输出SQL语句以及其执行事件,建议开发测试时启用该功能,能快速揪出慢查询SQL语句
- 内置全局拦截插件:提供全表delete、update操作智能分析阻断,也可以自定义拦截规则,预防误操作
# 标准数据层开发
# MyBatis-Plus的CRUD操作
package com.itheima.mapper;
import com.itheima.domain.User;
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
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
/**
* 测试新增
*/
@Test
public void testInsert() {
User user = new User();
user.setName("sunyy");
user.setAge(18);
user.setEmail("sunyy@itcast.cn");
userMapper.insert(user);
}
/**
* 测试修改
*/
@Test
public void testUpdate() {
User user = new User();
user.setId(1613360326898720770L);
user.setEmail("sunyy@itheima.cn");
userMapper.updateById(user);
}
/**
* 测试删除
*/
@Test
public void testDelete() {
userMapper.deleteById(1613360326898720770L);
}
/**
* 测试查询
*/
@Test
public void testSelect() {
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
}
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# MyBatis-Plus分页功能
使用MyBatis-Plus分页功能,我们需要如下步骤进行:
- 配置MyBatis-Plus分页拦截器,并将其交给Spring管理
- 在测试方法中执行分页查询
- 开启MyBatis-Plus日志记录功能,方便查看输出的SQL语句
- 第一步:配置MyBatis-Plus分页拦截器,并将其交给Spring管理
package com.itheima;
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;
/**
* MyBatis-Plus配置类
* @author sunyy
* @version 1.0
* @since 2023.1.12
*/
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 1. 创建MyBatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 2. 添加分页拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 第二步:在测试方法中执行分页查询
package com.itheima.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.domain.User;
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
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
/**
* 测试分页查询
*/
@Test
public void testSelectPage() {
// 1. 创建IPage分页对象,设置分页参数
IPage<User> page = new Page<>(1, 3);
// 2. 执行分页查询
userMapper.selectPage(page, null);
// 3. 获取分页结果
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
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
- 第三步:开启MyBatis-Plus日志记录功能,方便查看输出的SQL语句
server:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://localhost:3306/mp_db
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2
3
4
5
6
7
8
9
10
11
12
13
14
# 解决控制台日志打印过多问题
首先我们需要先了解下日志体系结构和logback
logback:
- 通过使用logback,我们可以控制日志信息输送的目的地是控制台、文件还是其他位置
- 也可以控制每一条日志的输出格式
- 还可以通过定义每一条日志信息的级别,更加细致地控制日志的生成过程
- 最令人感兴趣的是,我们可以通过一个配置文件logback.xml来灵活地进行配置,而不需要修改应用的代码
关于logback可以参考https://www.jianshu.com/p/75f9d11ae011。
- 取消Spring初始化日志打印
解决:在resources资源目录下,新建一个logback.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
</configuration>
2
3
4
- 取消SpringBoot启动的banner图标
server:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://localhost:3306/mp_db
main:
banner-mode: off # 关闭SpringBoot启动图标
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 取消MyBatisPlus启动的banner图标
server:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://localhost:3306/mp_db
main:
banner-mode: off # 关闭SpringBoot启动图标
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
banner: false # 关闭MyBatis-Plus启动图标
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# DQL编程控制
# 条件查询
MyBatis-Plus将书写复杂的SQL查询条件进行了封装,开发者可以使用编程的形式完成查询条件的组合。
# 单条件查询
- 方式一:按条件查询
/**
* 方式一:按条件查询
*/
@Test
public void testSelectConditionOne() {
QueryWrapper<User> qw = new QueryWrapper<>();
qw.lt("age", 20);
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
- 方式二:lambda格式按条件查询
/**
* 方式二:lambda格式按条件查询
*/
@Test
public void testSelectConditionTwo() {
QueryWrapper<User> qw = new QueryWrapper<>();
qw.lambda().lt(User::getAge, 20);
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
- 方式三:lambda格式按条件查询推荐
/**
* 方式三:lambda格式按条件查询 推荐方式
*/
@Test
public void testSelectConditionThree() {
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.lt(User::getAge, 20);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
# 组合条件查询
- and组合
/**
* and 组合条件
*/
@Test
public void testSelectByAnd() {
// 并且关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
// 并且关系:20至25岁之间的数据
lqw.lt(User::getAge, 25).gt(User::getAge, 20);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
- or组合
/**
* 或者关系
*/
@Test
public void testSelectByOr() {
// 或者关系
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
// 或者关系:小于20或者大于25的数据
lqw.lt(User::getAge, 20).or().gt(User::getAge, 25);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
# NULL值处理方式
在实际项目中,在搜索场景下,多条件查询中,有的条件的值可能为空,那么针对这些条件为空值的情况我们要做一些处理,以防查询不到我们预期的数据。
- if语句控制条件的追加
/**
* if 语句控制条件追加
*/
@Test
public void testSelectByIf() {
Integer minAge = 24; // 由前端发送参数,此处简化
Integer maxAge = null; // 由前端发送参数,此处简化
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
if (minAge != null) {
lqw.gt(User::getAge, minAge);
}
if (maxAge != null) {
lqw.lt(User::getAge, maxAge);
}
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 条件参数控制
/**
* 条件参数控制
*/
@Test
public void testSelectByCondition() {
Integer minAge = 24; // 由前端发送参数,此处简化
Integer maxAge = null; // 由前端发送参数,此处简化
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.gt(minAge != null, User::getAge, minAge);
lqw.lt(maxAge != null, User::getAge, maxAge);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
13
- 条件参数控制(链式编程)
/**
* 条件参数控制(链式编程)
*/
@Test
public void testSelectByChainCondition() {
Integer minAge = 24; // 由前端发送参数,此处简化
Integer maxAge = null; // 由前端发送参数,此处简化
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.gt(minAge != null, User::getAge, minAge)
.lt(maxAge != null, User::getAge, maxAge);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
13
# 查询投影
- **查询结果包含模型类中部分属性
/**
* 查询结果包含模型类中部分属性
*/
@Test
public void testSelectPart() {
// 方式一
// LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
// lqw.select(User::getId, User::getName, User::getAge);
// 方式二
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id", "name", "age");
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 查询结果包含模型类中未定义属性
/**
* 查询结果中包含模型类中未定义属性
*/
@Test
public void testSelectUnknown() {
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("count(*) as count, age");
qw.groupBy("age");
List<Map<String, Object>> maps = userMapper.selectMaps(qw);
System.out.println(maps);
}
2
3
4
5
6
7
8
9
10
11
# 查询条件设定
条件查询有哪些组合,可以回顾JavaWeb阶段学习的DQL语句
- 范围匹配(
>
,=
,between
) - 模糊匹配(
like
) - 空判定(
null
) - 包含性匹配(
in
) - 分组(
group
) - 排序(
order
) - ...
- eq匹配
/**
* 相等匹配
*/
@Test
public void testSelectByEq() {
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.eq(User::getName, "Jone").eq(User::getAge, 18);
User user = userMapper.selectOne(lqw);
System.out.println(user);
}
2
3
4
5
6
7
8
9
10
- 范围匹配
/**
* 范围匹配
*/
@Test
public void testSelectByBetween() {
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
// 范围查询 lt le gt ge eq between
lqw.between(User::getAge, 20, 24);
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
- 模糊匹配
/**
* 模糊匹配
*/
@Test
public void testSelectByLike() {
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
// 全模糊
// lqw.like(User::getName, "J");
// 右模糊
// lqw.likeRight(User::getName, "J")
// 左模糊
lqw.likeLeft(User::getName, "J");
List<User> users = userMapper.selectList(lqw);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 聚合函数分组查询
/**
* 聚合分组查询
*/
@Test
public void testSelectByGroup() {
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("count(*) as count", "age").groupBy("age");
List<Map<String, Object>> maps = userMapper.selectMaps(qw);
System.out.println(maps);
}
2
3
4
5
6
7
8
9
10
# 字段、表名映射
上面我们测试的例子都比较简单,但还有一些情况我们要考虑到,比如:我们在user
表中添加的一个*密码(pwd)*字段,在实体类User
中添加了一个属性password
,那么现在我们在查询会出现什么效果呢?
实际场景中,我们可能遇到的情况还会更多,那么下面我们就来逐一解决常见的一些问题。
- 问题一:表字段与实体类属性名设计不同步;解决:在实体类属性上方,使用
@TableField
注解属性,通过value
属性,设置当前属性对应的数据库表中的字段名
- 问题二:实体类中添加了数据表中未定义的属性;解决:在实体类属性上方,使用
@TableField
注解,通过exist
属性,设置属性在数据表中不存在,默认为true
为存在。此属性无法与value
属性合并使用。
- 问题三:采用默认查询,将表中不应该展示出来的字段查询出来;解决:在实体类属性上方,使用
@TableField
注解,通过select
属性,设置该属性是否参与查询,此属性与select()
映射配置不冲突。
- 问题三:表名与实体类名设计不同步,比如,将
user
表名改为tb_user
;解决:在实体类上方,使用@TableName
注解,通过value
属性,设置当前实体类对应的数据库表名
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName(value = "tbl_user")
public class User {
/**
* id为Long型,原因是数据表中id未bigint类型,并且MyBatis-Plus
* 有一套自己的id生成方案,且生成的id必须是Long类型
*/
private Long id;
private String name;
private Integer age;
private String email;
@TableField(value = "pwd", select = false)
private String password;
@TableField(exist = false) // 表示online属性在数据表中不存在与之对应的字段,不参与CRUD操作
private Boolean online;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# DML编程控制
# ID生成策略(@TableId注解)
名称 | @TableId |
---|---|
类型 | 属性注解 |
位置 | 实体类中用于标识主键属性的上方 |
作用 | 设置当前类中主键属性的生成策略 |
相关属性 | type: 设置主键属性的生成策略,值参照IdType 枚举值 |
如果涉及到的实体类特别多,逐个配置表名和实体类映射关系以及id
生成策略比较麻烦时,我们也可以采用全局配置策略
全局配置策略:在application.yml配置文件中进行配置
mybatis-plus:
global-config:
db-config:
# id生成策略
id-type: assign_id
# 表名前缀
table-prefix: tbl_
2
3
4
5
6
7
# 批量操作
下面我们来看看MyBatis-Plus如何实现批量查询、批量删除操作。
- 根据主键查询多条记录
/**
* 根据主键集合查询多条记录
*/
@Test
public void testSelectByIds() {
List<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
ids.add(3L);
List<User> users = userMapper.selectBatchIds(ids);
System.out.println(users);
}
2
3
4
5
6
7
8
9
10
11
12
- 根据主键集合删除多条记录
/**
* 批量新增
*/
@Test
public void testInsertBatch() {
List<Long> ids = new ArrayList<>();
ids.add(6L);
ids.add(7L);
ids.add(8L);
int count = userMapper.deleteBatchIds(ids);
Assert.isTrue(count == 3, "批量删除成功");
}
2
3
4
5
6
7
8
9
10
11
12
# 逻辑删除
在实际业务系统中,如果删除一条数据,是否会真的从数据库中删除该条数据呢?或者说我们使用的支付宝、京东在删除数据时是物理删除还是逻辑删除?
- 物理删除:业务数据从数据库中丢弃,不易恢复
- 逻辑删除:为数据设置是否可用字段,删除时设置状态字段为不可用状态,数据依旧保留在数据库中
- 在数据表中添加逻辑删除标记字段,并设置默认值为0
在实体类中添加对应属性,并标记为逻辑删除字段,标记方式有两种:
- 局部:使用
@TableLogic(value = "0", delval="1")
注解
package com.itheima.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; @Data // @TableName(value = "tbl_user") public class User { /** * id为Long型,原因是数据表中id未bigint类型,并且MyBatis-Plus * 有一套自己的id生成方案,且生成的id必须是Long类型 */ // @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; @TableField(value = "pwd", select = false) private String password; @TableField(exist = false) // 表示online属性在数据表中不存在与之对应的字段,不参与CRUD操作 private Boolean online; @TableLogic(value = "0", delval = "1") private Integer deleted; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24- 全局:在application.yml配置文件中配置
mybatis-plus: global-config: db-config: table-prefix: tbl_ # 逻辑删除字段名 logic-delete-field: deleted # 逻辑删除字面值:未删除为0 logic-not-delete-value: 0 # 逻辑删除字面值:删除为1 logic-delete-value: 1
1
2
3
4
5
6
7
8
9
10- 局部:使用
提示
逻辑删除本质上是修改操作,如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。
# 乐观锁
秒杀系统之超卖问题:在做抢购系统的时候,我们首先应该想到的是怎么才能避免超卖,比如说:库存只有100,结果卖了200,这种情况肯定是不能接受的。如果我们不再代码中针对该问题进行处理,很有可能就会造成这种不良后果。
- 在数据表中添加乐观锁标记字段,并设置默认值
- 实体类中添加对应的属性,并标记为乐观锁属性*
package com.itheima.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
@Data
// @TableName(value = "tbl_user")
public class User {
/**
* id为Long型,原因是数据表中id未bigint类型,并且MyBatis-Plus
* 有一套自己的id生成方案,且生成的id必须是Long类型
*/
// @TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableField(value = "pwd", select = false)
private String password;
@TableField(exist = false) // 表示online属性在数据表中不存在与之对应的字段,不参与CRUD操作
private Boolean online;
@TableLogic(value = "0", delval = "1")
private Integer deleted;
@Version
private Integer version;
}
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
- 配置乐观锁拦截器,实现乐观锁机制对SQL语句拦截拼装
package com.itheima.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis-Plus配置类
* @author sunyy
* @version 1.0
* @since 2023.1.12
*/
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 1. 创建MyBatisPlusInterceptor拦截器对象
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 2. 添加分页拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 3. 乐观锁拦截器
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
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
- 乐观锁效果演示
/**
* 测试乐观锁
*/
@Test
public void testOptimisticLocker() {
// 1. 每次修改乐观锁版本号都+1
// User user = new User();
// user.setId(1L);
// user.setName("Jone1");
// user.setVersion(0);
// userMapper.updateById(user);
// 2. 先查询在修改
// User user = userMapper.selectById(1L);
// user.setName("Joine2");
// userMapper.updateById(user);
// 3. 乐观锁保护机制
User user1 = userMapper.selectById(1L); // version = 2
User user2 = userMapper.selectById(1L); // version = 2
user1.setName("Jone 111");
userMapper.updateById(user1); // version = 3
user2.setName("Jone 222");
userMapper.updateById(user2); // version = 2 条件不再成立
}
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
# 代码生成器
问题:数据库设计完成后,如何快速创建我们的Dao层、Service层、Controller层代码?
# MyBatisPlus提供模板
- Mapper接口模板
- 实体类模板
# 工程搭建和代码生成器使用
- 创建SpringBoot工程,添加代码生成器相关依赖,其他依赖自行添加
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.itheima</groupId>
<artifactId>mybatisplus-02-generator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBaits-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<!-- druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
- 编写代码生成器方法
package com.itheima;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class CodeGenerator {
@Test
public void codeGenerator() {
// 1. 创建代码生成器,执行代码生成操作
AutoGenerator generator = new AutoGenerator();
// 2. 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mp_db?serverTimezone=UTC");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("1234");
generator.setDataSource(dataSourceConfig);
// 3. 执行生成操作
generator.execute();
}
}
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
问题
生成的代码不在我们的项目目录下,这时就需要开发者自定义配置。
# 开发者自定义配置
- 全局配置
// 2.1 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir("src/main/java"); // 设置生成代码位置
globalConfig.setOpen(false); // 生成完毕后是否打开代码所在目录
globalConfig.setAuthor("sunyy"); // 设置作者
globalConfig.setFileOverride(true); // 设置是否覆盖之前生成的文件
globalConfig.setMapperName("%sMapper"); // 设置数据层接口名,%s为占位符,指代模块名称
globalConfig.setIdType(IdType.AUTO); // 设置id生成策略
generator.setGlobalConfig(globalConfig);
2
3
4
5
6
7
8
9
- 包名相关配置
// 2.2 包名配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.itheima"); // 设置生成的包名
packageConfig.setModuleName("user"); // 设置模块名
packageConfig.setEntity("entity"); // 设置实体类包名
packageConfig.setMapper("mapper"); // 设置数据层包名
generator.setPackageInfo(packageConfig);
2
3
4
5
6
7
- 策略配置
// 2.3 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("tbl_user"); // 设置参与生成的表名,参数为可变参数
strategyConfig.setTablePrefix("tbl_"); // 设置数据表的前缀
strategyConfig.setRestControllerStyle(true); // 是否启用Rest风格
strategyConfig.setLogicDeleteFieldName("deleted"); // 逻辑删除字段
strategyConfig.setVersionFieldName("version"); // 乐观锁字段
strategyConfig.setEntityLombokModel(true); // 是否启用lombok
generator.setStrategy(strategyConfig);
2
3
4
5
6
7
8
9