🍓 简介:java系列技术分享(👉持续更新中...🔥)
🍓 初衷:一起学习、一起进步、坚持不懈
🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏
🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝
一、 Mybatis-Plus是什么?
MyBatis-Plus
(简称 MP)是一个 MyBatis
的增强工具,在 MyBatis 的基础上只做增
强不做改变,为简化开发、提高效率而生。
MybatisPlus可以节省大量时间
,所有的CRUD代码都可以自动化完成
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
二、Mybatis和Mybatis-Plus区别
MyBatis:
- 所有SQL语句全部自己写
- 手动解析实体关系映射转换为MyBatis内部对象注入容器
- 不支持Lambda形式调用
- 驼峰(属性)和下划线(字段)的映射关系
mybatis
中默认是关闭的。
Mybatis Plus:
- 强大的条件构造器,满足各类使用需求
- 内置的Mapper,通用的Service,少量配置即可实现单表大部分CRUD操作
- 支持Lambda形式调用
- 提供了基本的CRUD功能,连SQL语句都不需要编写
- 自动解析实体关系映射转换为MyBatis内部对象注入容器
- 驼峰(属性)和下划线(字段)的映射关系
Mybatis-Plus
中 默认是开启的 ,不需要额外配置。
三、快速入门
我们将通过一个简单的 Demo 来阐述 MyBatis-Plus 的强大功能
3.1 创建user表结构
id | name | age | |
---|---|---|---|
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 |
创建表结构
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)
);
新增数据
sql
DELETE FROM user;
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');
结果展示
![在这里插入图片描述](img-blog.csdnimg.cn/07e67c759a6... =900x300)
3.2 新建Boot项目
引用spring boot starter 父工程
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/>
</parent>
引入对应依赖这里使用mysql数据库
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
3.3 添加配置
在 application.yml 配置文件中添加 mysql
数据库的相关配置:
yaml
spring:
# 数据源配置
datasource:
# 数据库连接信息配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///test_demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true&failOverReadOnly=false
username: root
password: root
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:
java
@SpringBootApplication
@MapperScan("com.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3.4 新建对应实体类
java
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
编写 Mapper 包下的 UserMapper
接口 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
java
//@Mapper 没有没配@MapperScan的需添加@Mapper注解
public interface UserMapper extends BaseMapper<User> {
}
3.5 测试
java
@SpringBootTest
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
}
UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器
Wrapper
,所以不填写就是无任何条件
3.6 控制台输出
四、开启日志SQL可见
yaml
mybatis-plus:
configuration:
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
sql可见
五、CRUD
方法非常多
,我简单列出几个,具体请查看官方文档中的接口链接
:接口文档
java
// 插入一条记录
int insert(T entity);
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// 根据 ID 修改
int updateById(@Param(Constants.ENTITY) T entity);
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
5.1 Mapper-数据操作层
继承BaseMapper接口
java
public interface UserDao extends BaseMapper<TestUser> {
}
5.2 Service -- 业务逻辑层
继承IService
接口
java
public interface UserService extends IService<User> {
}
5.3 实现类
继承ServiceImpl
实现UserService
接口
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
六、条件构造器Wrapper
AbstractWrapper
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
七、分页插件
7.1 分页查询测试(有问题)
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectPage(){
Page<User> page = new Page<>(1,2);
Page<User> result = userMapper.selectPage(page, null);
System.out.println("当前页码"+result.getCurrent());
System.out.println("每页数量"+result.getSize());
System.out.println("数据总数"+result.getTotal());
System.out.println("当前页数据"+result.getRecords());
}
}
发现分页未生效
7.2 增加分页拦截器配置(成功)
java
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor pageInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
7.3 Page对象
该类继承了 IPage
类,实现了 简单分页模型
如果你要实现自己的分页模型可以继承 Page
类或者实现 IPage 类
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
records | List | emptyList | ==查询数据列表== |
total | Long | 0 | 查询列表总记录数 |
size | Long | 10 | 每页显示条数,默认 10 |
current | Long | 1 | 当前页 |
orders | List | emptyList | 排序字段信息,允许前端传入的时候,注意 SQL 注入问题,可以使用 SqlInjectionUtils.check(...) 检查文本 |
optimizeCountSql | boolean | true | 自动优化 COUNT SQL 如果遇到 jSqlParser 无法解析情况,设置该参数为 false |
optimizeJoinOfCountSql | boolean | true | 自动优化 COUNT SQL 是否把 join 查询部分移除 |
searchCount | boolean | true | 是否进行 count 查询,如果指向查询到列表不要查询总记录数,设置该参数为 false |
maxLimit | Long | 单页分页条数限制 | |
countId | String | xml 自定义 count 查询的 statementId |
八、查询编写
8.1 条件查询的3种格式
8.1.1 第一种 (不推荐)
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectPage(){
//查询年龄大于5岁小于20岁的用户
//构造条件
QueryWrapper<User> qw = new QueryWrapper<>();
qw.lt("age",20);
qw.gt("age",5);
List<User> userList = userMapper.selectList(qw);
System.out.println(userList);
}
}
结果成功 缺点
:字段名容易写错
8.1.2 第二种 Lambda 表达式
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectPage(){
//查询年龄大于5岁小于20岁的用户
//构造条件
QueryWrapper<User> qw = new QueryWrapper<>();
qw.lambda().lt(User::getAge,20).gt(User::getAge,5);
List<User> userList = userMapper.selectList(qw);
System.out.println(userList);
}
}
结果成功,但是每次都需要.lambda()
8.1.3 第三种 直接使用LambdaQueryWrapper
(推荐)
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectPage(){
//查询年龄大于5岁小于20岁的用户
//构造条件
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
qw.lt(User::getAge,20).gt(User::getAge,5);
List<User> userList = userMapper.selectList(lqw);
System.out.println(userList);
}
}
8.2 查询条件null值处理
由于存在请求参数可能有值或者null的情况,所以要添加判断
表中数据
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectNotNull(){
//模拟查询条件
UserDTO userDTO = new UserDTO();
userDTO.setAge(5);
userDTO.setAge2(20);
//构造条件
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
//链式编程,分开也可以
lqw.ge(null!=userDTO.getAge(),User::getAge,userDTO.getAge())
.lt(null!=userDTO.getAge2(),User::getAge,userDTO.getAge2());
List<User> userList = userMapper.selectList(lqw);
System.out.println(userList);
}
}
结果
8.3 查询部分属性
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectPortion(){
//构造条件
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.select(User::getId,User::getName);
List<User> userList = userMapper.selectList(lqw);
System.out.println(userList);
}
}
8.4 查询未定义的属性
java
@SpringBootTest
public class TestPlus {
@Autowired
private UserMapper userMapper;
@Test
void testSelectPortion2(){
//构造条件
QueryWrapper<User> lqw = new QueryWrapper<>();
lqw.select("count(1) num,age");
lqw.groupBy("age");
List<Map<String, Object>> maps = userMapper.selectMaps(lqw);
System.out.println(maps);
}
}
并且别名也能生效
九、常用注解使用(配置映射关系)
9.1 @TableName 表名映射
- 描述:
表名注解,标识当前实体类与数据库表的对应关系
- 使用位置:实体类
java
@TableName("sys_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | ==数据库表名== |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定) |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名 @since 3.3.1 |
9.2 @TableField 字段映射
设置当前对应数据库表中的字段关系
java
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname")
private String name;
private Integer age;
private String email;
}
- 数据库中未定义属性
java
@TableField(exist=false)
private Integer result;
exist:设置属性在数据库表字段中是否存在,默认为true.此属性无法与value合并使用
- 密码不想被查询
java
@TableField(value = "pwd",select = false)
private String password;
select:设置属性是否参与查询,此属性与select()映射配置不冲突
注解属性如下:
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | |
update | String | 否 | "" | |
insertStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:IGNORED update table_a set column=#{columnProperty} |
whereStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC 类型 (该默认值不代表会按照该值生效) |
typeHandler | Class<? extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
numericScale | String | 否 | "" | 指定小数点后保留的位数 |
9.3 @TableId 主键类型
- 描述:主键注解
- 使用位置:实体类主键字段
java
@TableName("sys_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
private Integer age;
private String email;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
IdType
值 | 描述 |
---|---|
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认 default 方法) |
ID_WORKER | 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID ) |
UUID | 32 位 UUID 字符串(please use ASSIGN_UUID ) |
ID_WORKER_STR | 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID ) |
9.1 @Version 乐观锁
- 第一步 :表中增加乐观锁标记 2. 实体类中添加对应字段
java
@Data
public class User {
@Version
private Integer version;
}
- 配置乐观锁拦截器
java
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor pageInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//乐观锁
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
使用乐观锁在修改前必须先获取对应version字段数据
- 原理 update user set age = 12 ,version = version +1 where version = ?
? 查询出来的version结果
9.1 @TableLogic 逻辑删除
java
public class User{
@TableLogic(value = "0",delval = "1")
private Integer isDelete;
}
或者配置文件全局配置
yaml
mybatis-plus:
global-config:
db-config:
logic-delete-field: isDelete
#逻辑未删除值
logic-not-delete-value: 0
#逻辑已删除值
logic-delete-value: 1
十、全局配置
yaml
mybatis-plus:
global-config:
db-config:
#id类型
id-type: ASSIGN_ID
#数据库表默认前缀
table-prefix: tb_
本篇持续更新...........