mybatis-plus学习及使用
什么是Mybatis-Plus
MyBatis-Plus
是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
Mybatis-Plus的特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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-Plus v3.4.2
准备数据库
1、创建数据库 mybatis-plus
2、导入如下脚本
DROP TABLE IF EXISTS USER;
CREATE TABLE USER
(
id BIGINT(20) NOT NULL AUTO_INCREMENT 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)
) DEFAULT CHARSET=UTF8;
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');
创建SpringBoot项目
导入依赖
修改 pom.xml
文件
<!-- MySql数据库相关依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- MyBatis-Plus相关依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
配置数据源
1、修改配置文件类型
将 src/main/resources/application.properties
更改为 src/main/resources/application.yml
2、添加如下配置
server:
port: 8080
spring:
datasource:
username: root
password: ok
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
配置启动类
修改 com.yacon.mybatisplus.MybatisPlusApplication
类
package com.yacon.mybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.yacon.mybatisplus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
创建POJO类
创建 com.yacon.mybatisplus.pojo.User
类
package com.yacon.mybatisplus.pojo;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
创建Mapper类
创建 com.yacon.mybatisplus.mapper.UserMapper
接口
package com.yacon.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yacon.mybatisplus.pojo.User;
public interface UserMapper extends BaseMapper<User> { }
编写测试方法
1、修改 src/test/java/com/yacon/mybatisplus/MybatisPlusApplicationTests.java
文件
package com.yacon.mybatisplus;
import com.yacon.mybatisplus.mapper.UserMapper;
import com.yacon.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
@SpringBootTest
class MybatisPlusApplicationTests {
@Resource
private UserMapper userMapper;
/**
* 查询所有用户列表
*/
@Test
public void testSelect(){
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
2、运行测试
日志输出
1、修改 src/main/resources/application.yml
配置文件,添加日志配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2、运行测试
基础CRUD
SELECT
根据ID查询
1、添加测试方法
/**
* 查询用户信息
*/
@Test
public void testGetUserById(){
User user = userMapper.selectById(5);
if(null == user) {
System.out.println("查询失败");
return;
}
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getAge());
System.out.println(user.getEmail());
}
2、运行测试
查询总条目数
1、编写测试方法
/**
* 查询总条目数
*/
@Test
public void testGetUserCount(){
Integer count = userMapper.selectCount(null);
System.out.println(count);
}
2、运行测试
条件查询
1、编写测试方法
/**
* 查询年龄大于20岁的用户
*/
@Test
public void testWhere(){
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.gt("age",20);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
2、运行测试
模糊查询
1、编写测试方法
/**
* 查询姓名包含 a 的用户
*/
@Test
public void testLike(){
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.like("name",'a');
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
2、运行测试
排序查询
1、添加测试方法
/**
* 查询用户列表,并按年龄逆序
*/
@Test
public void testOrder() {
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.orderByDesc("age");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
2、运行测试
INSERT
配置主键生成策略
1、确保数据表的主键列为自增列
2、修改 com.yacon.mybatisplus.pojo.User
类
@Data
public class User {
@TableId(type= IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
编写测试方法
1、添加测试方法
/**
* 插入用户
*/
@Test
public void testInsert(){
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@mail.com");
int result = userMapper.insert(user);
System.out.println(result > 0 ? "插入成功" : "插入失败");
System.out.println("ID="+user.getId());
}
2、运行测试
UPDATE
1、添加测试方法
/**
* 更新用户
*/
@Test
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setName("zhangsan");
int result = userMapper.updateById(user);
System.out.println(result > 0 ? "修改成功" : "修改失败");
}
2、运行测试
DELETE
1、添加测试方法
/**
* 删除用户
*/
@Test
public void testDelete(){
int result = userMapper.deleteById(6);
System.out.println(result > 0 ? "删除成功" : "删除失败");
}
2、运行测试
高级扩展
逻辑删除
逻辑删除
是指,在删除表中的某条数据时并不是真正的删除,而是更改某个表字段的值来表示该条数据已被删除
修改数据表
给 User
添加字段 deleted
,并设置默认值为 0
ALTER TABLE `mybatis_plus`.`user` ADD COLUMN `deleted` INT(1) DEFAULT 0 NOT NULL AFTER `email`;
修改实体类
修改 com.yacon.mybatisplus.pojo.User
类
@Data
public class User {
@TableId(type= IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer deleted;
}
添加配置
修改 src/main/resources/application.yml
文件
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除的值
logic-not-delete-value: 0 # 逻辑未删除的值
运行测试
1、运行 删除用户 测试方法
2、运行 查询所有用户列表 测试方法
自动填充字段
修改数据表
给 user
表添加两个字段 gmt-create
和 gmt-modified
ALTER TABLE `mybatis_plus`.`user`
ADD COLUMN `gmt_create` DATETIME NULL COMMENT '创建时间' AFTER `deleted`,
ADD COLUMN `gmt_modified` DATETIME NULL COMMENT '更新时间' AFTER `gmt_create`;
修改实体类
修改 com.yacon.mybatisplus.pojo.User
类
@Data
public class User {
@TableId(type= IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
}
编写填充处理器
创建 com.yacon.mybatisplus.handle.MyMetaObjectHandler
类
package com.yacon.mybatisplus.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("gmtCreate",new Date(), metaObject);
this.setFieldValByName("gmtModified",new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified",new Date(), metaObject);
}
}
运行测试
1、运行 插入用户 测试方法
2、运行 更新用户 测试方法
常用插件
乐观锁
在高并发的情况下,可能存在多个用户同时修改一条数据的情况,为了让某一用户在修改数据时不被被人更新,这时就需要用乐观锁将数据进行锁定
乐观锁实现方式
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
修改数据表
给 user
表添加 version
字段 b
ALTER TABLE `mybatis_plus`.`user`
ADD COLUMN `version` INT DEFAULT 1 NULL COMMENT '乐观锁' AFTER `email`;
修改实体类
修改 com.yacon.mybatisplus.pojo.User
类
@Data
public class User {
@TableId(type= IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
@Version
private Integer version;
}
注册插件
1、创建 com.yacon.mybatisplus.config.MyBatisPlusConfig
类
package com.yacon.mybatisplus.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
@MapperScan("com.yacon.mybatisplus.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 注册乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
2、修改 om.yacon.mybatisplus.MybatisPlusApplication
启动类
@SpringBootApplication
@MapperScan("com.yacon.mybatisplus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
运行测试
1、添加测试方法
/**
* 乐观锁测试
*/
@Test
public void testLocker() {
// 模拟线程1
User user1 = userMapper.selectById(7L);
user1.setName("张三");
// 模拟线程2插队
User user2 = userMapper.selectById(7L);
user2.setName("李四");
userMapper.updateById(user2);
userMapper.updateById(user1);
}
2、运行测试
分页查询
注册分页插件
修改 com.yacon.mybatisplus.config.MyBatisPlusConfig
类
注意注册插件的顺序,要先注册 分页插件
,再注册 乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 注册分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 注册乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
添加测试方法
修改 com.yacon.mybatisplus.MybatisPlusApplicationTests
测试类
/**
* 分页查询
*/
@Test
public void testPage(){
long current = 1; // 当前页码
long size = 2; // 每页显示条目数
Page<User> page = new Page<>(current,size);
userMapper.selectPage(page, null);
List<User> users = page.getRecords(); // 获取数据列表
users.forEach(System.out::println);
}
运行测试
性能分析
SQL性能校验
注册插件
修改 com.yacon.mybatisplus.config.MyBatisPlusConfig
类
性能分析插件 要放在 分页插件 和 乐观锁插件 之后
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 注册分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 注册乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 注册性能分析插件
interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
return interceptor;
}
运行测试
1、运行 查询所有用户列表 测试方法
由于我们开启了 逻辑删除,所以在查询时需要使用 deleted
为查询条件进行查询,但我们没有给 deleted
字段添加索引,所以这里报了一个 未使用索引的错误
2、修改数据库,给 deleted
字段添加索引
注意:添加组合索引时,deleted
一定要放在第一个,否则,使用单个deleted
作为条件查询时无法触发索引
ALTER TABLE `mybatis_plus`.`user`
ADD UNIQUE INDEX `id_deleted_unique` (`deleted`, `id`);
3、重新运行 查询所有用户列表 测试方法
SQL性能分析
该功能有性能损耗,不建议生产环境使用
添加依赖
修改 pom.xml
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
修改数据源
修改 src/main/resources/application.yml
spring:
datasource:
username: root
password: ok
url: jdbc:p6spy:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
配置p6spy
创建 src/main/resources/spy.properties
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
运行测试
执行 查询用户信息 测试方法
代码生成器
创建SpringBoot项目
新建一个SpringBoot项目
导入依赖
修改 pom.xml
文件
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
编写代码生成器
创建 MyBatisPlusAutoCode
类
package com.yacon.mybatisplus;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
/**
* 代码生成器
*/
public class MyBatisPlusAutoCode {
public static void main(String[] args) {
AutoGenerator mpg = new AutoGenerator();
/* 全局配置 */
GlobalConfig gc = new GlobalConfig();
//生成文件的输出目录
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
// 是否打开输出目录
gc.setOpen(false);
// 是否覆盖已有文件
gc.setFileOverride(true);
// 开发人员
gc.setAuthor("Yacon");
// 是否生成swagger2注解
gc.setSwagger2(true);
// 时间类型对应策略
gc.setDateType(DateType.TIME_PACK);
// mapper 命名方式
gc.setMapperName("%sDao");
// Mapper xml 命名方式
gc.setXmlName("%sDao");
// service 命名方式
gc.setServiceName("%sService");
// service impl 命名方式
gc.setServiceImplName("%sServiceImpl");
// controller 命名方式
gc.setControllerName("%sController");
// 指定生成的主键的ID类型
gc.setIdType(IdType.AUTO);
// 设置全局配置
mpg.setGlobalConfig(gc);
/* 数据源配置 */
DataSourceConfig dsc = new DataSourceConfig();
// 数据库类型
dsc.setDbType(DbType.MYSQL);
// 驱动连接的URL
dsc.setUrl("jdbc:mysql://localhost:3306/cvs_db?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
// 驱动名称
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
// 数据库账号
dsc.setUsername("root");
// 数据库密码
dsc.setPassword("ok");
// 设置数据库配置
mpg.setDataSource(dsc);
/* 包配置 */
PackageConfig pc = new PackageConfig();
// 父包模块名
pc.setModuleName("api");
// 父包名
pc.setParent("com.yacon.mybatisplus");
// Entity包名
pc.setEntity("pojo");
// Service包名
pc.setService("service");
// Service Impl包名
pc.setServiceImpl("service");
// Mapper包名
pc.setMapper("dao");
// Mapper XML包名
pc.setXml("dao");
// Controller包名
pc.setController("controller");
// 设置包配置
mpg.setPackageInfo(pc);
/* 策略配置 */
StrategyConfig strategy = new StrategyConfig();
// 需要包含的表名
strategy.setInclude("t_address","t_storage_record","t_supplier","t_sys_role","t_sys_user");
// 表前缀
strategy.setTablePrefix("t_");
// 数据库表映射到实体的命名策略
strategy.setNaming(NamingStrategy.underline_to_camel);
// 数据库表字段映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 是否为lombok模型
strategy.setEntityLombokModel(true);
// Boolean类型字段是否移除is前缀
strategy.setEntityBooleanColumnRemoveIsPrefix(true);
// 生成 @RestController 控制器
strategy.setRestControllerStyle(true);
// 驼峰转连字符
strategy.setControllerMappingHyphenStyle(true);
// 乐观锁属性名称
strategy.setVersionFieldName("version");
// 逻辑删除属性名称
strategy.setLogicDeleteFieldName("deleted");
// 表填充字段
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 设置策略配置
mpg.setStrategy(strategy);
// 执行
mpg.execute();
}
}
运行代码生成器