1.MyBatisPlus简介
MyBatis-Plus
(简称 MP)是一个MyBatis
的增强工具,在MyBatis
的基础上只做增强不做改变,为简化开发、提高效率而生。
1.1 官网链接
- MyBatisPlus官网:https://baomidou.com/#/
1.2 MyBatisPlus特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
2.MyBatisPlus快速入门
2.1 官方教程地址
- MyBatisPlus快速入门:https://baomidou.com/guide/quick-start.html
2.2 快速入门实践
步骤
1、创建数据库,对应脚本如下
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)
);
2、创建数据表,对应脚本输入
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');
- 显示开发中表必备字段还有
version(版本)
、deleted(逻辑删除)
、create_time(创建时间)
、update_time(更新时间)
,后续步骤会增加。
3、创建SpringBoot项目
4、导入依赖
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--导入mybatis-plusSpringBoot依赖-->
<!--不要同时导入mybati和mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
5、连接数据库(配置application.yaml)
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatisplus?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 770880
driver-class-name: com.mysql.cj.jdbc.Driver
# spring-boot 2 默认采用高性能的 Hikari 作为连接池 更多配置可以参考 https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby
type: com.zaxxer.hikari.HikariDataSource
6、创建pojo类以及mapper类
//pojo类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
//mapper类
@Repository
public interface UserMapper extends BaseMapper<User> {
//所有crud都已完成
}
7、在主函数头部添加扫描组件
@MapperScan("com.jyc.mapper") //添加扫描包
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
8、编写测试
@SpringBootTest
class MybatisPlusApplicationTests {
// 注册组件
@Autowired
private UserMapper userMapper;
// 测试查询数据表内容
@Test
void contextLoads() {
//查询全部内容
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
- 读出数据内容说明测试成功
3.MybatisPlus核心功能
3.1 基本CRUD
插入(insert)
- 测试代码如下
@Test
void testInsert(){
User user = new User();
//设置插入数据
user.setName("Jyc");
user.setAge(18);
user.setEmail("508722744@qq.com");
//执行插入
userMapper.insert(user);
}
- 测试结果如图所示
- 可以发现没有设置id数据的时候,mybatisplus会默认自动帮我们填写一个id进去。原因见3.3自动填充
更新(update)
- 测试代码如下
@Test
void testInsert(){
User user = new User();
//设置更新数据
user.setName("Jyc");
user.setAge(18);
user.setEmail("508722744@qq.com");
//执行插入
userMapper.insert(user);
}
- 测试结果如图所示
- 在设置更新属性的时候,只要设置了的数据mybatisplus都会动态拼接sql,来更新设置的数据
查询(select)
- 测试代码如下
@Test
void testSelect(){
//查询单个id
User user1 = userMapper.selectById(1L);
System.out.println(user1);
//查询多个id
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
//条件查询
HashMap<String, Object> map = new HashMap<>();
//查询名字为jyc的
map.put("name","jyc");
userMapper.selectByMap(map);
}
- 测试结果如图所示
删除(delete)
- 测试代码如下
@Test
void testDelete(){
//删除单个
userMapper.deleteById(1L);
//删除多个
userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
//条件删除
HashMap<String, Object> map = new HashMap<>();
//删除名字为jyc的
map.put("name","jyc");
userMapper.deleteByMap(map);
}
- 测试结果如图
3.2 配置日志
通过日志查看sql执行内容,只需要配置application.yaml文件即可
# 配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 再次启动测试即可看到日志输出
3.3 主键生成策略
-
在3.1插入时,发现没有设置id却在插入后会自动添加。原因就是mybatisplus的自动填充机制
-
知识点拓展:雪花算法
- snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
-
通过设置注解,设置主键的增加方式
public enum IdType{
AUTO(0), //数据库id自增(主键比如设置自增)
NONE(1), //未设置主键
INPUT(2), //手动输入
ID_WORKER(3), //默认的全局唯一id(雪花算法)
UUID(4), //全局唯一id UUID
ID_WORKER_STR(5); //ID_WORKER字符串表示形式
}
@TableId(type = IdType.AUTO)
private Long id;
3.4 自动填充
-
所有数据表都必须配置
create_time(创建时间)
、update_time(更新时间)
两个字段,且应该是自动更新的。 -
自动填充方式一:数据库级别
- 在表中新增字段
create_time
、update_time
且设置自动更新以及默认值
- 在表中新增字段
-
自动填充方式二:代码级别
1、在表中新增字段
create_time
、update_time
但是不设置自动更新以及默认值2、在属性类添加自动更新注解
//在插入时进行更新 @TableField(fill = FieldFill.INSERT) private Date createTime; //在插入和更新时进行更新 @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
3.编写注解实现类
@Slf4j //日志打印 @Component //将处理器加到IOC容器中 public class MyObjectHandler implements MetaObjectHandler { /** * 插入时的填充策略 * * setFieldValByName(String fieldName, 设置的字段名 * Object fieldVal, 设置的字段值 * MetaObject metaObject) * */ @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } /** * 更新时的填充策略 * */ @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime",new Date(),metaObject); } }
4.执行SQL语句即可
3.5 乐观锁
乐观锁:顾名思义十分乐观,总是认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,总是认为总是出现问题,无论干什么都会去上锁!再去操作
常见使用字段:version(版本)
-
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
-
简单例子
- 存在两个线程要更新同一条数据
- 初始获取时version = 1
- 现在任意一个提前提交后,对应数据库的version则变成2
- 此时如果另外一个线程要提交时,version版本判断不对,则就提交失败
-
乐观锁代码测试
1、数据库添加version字段
2、实体类添加对应字段
/** * 乐观锁注解 */ @Version private Integer version;
3、注册组件,创建MyConfig.java
@Configuration @MapperScan("com.jyc.mapper") @EnableTransactionManagement public class MyConfig { /** * 注册乐观锁插件 */ @Bean public OptimisticLockerInterceptor OptimisticLockerInnerInterceptor() { return new OptimisticLockerInterceptor(); } }
4、成功用例测试
@Test void testVersion(){ //查询用户信息 User user = userMapper.selectById(1L); System.out.println(user); //修改用户信息 user.setName("jyc"); //执行修改操作 userMapper.updateById(user); }
- 对应版本修改为二
5、失败用例测试
@Test void testVersion(){ //用户1查询用户信息 User user1 = userMapper.selectById(1L); //用户1修改用户信息 user1.setName("jyc1"); //用户2查询用户信息 User user2 = userMapper.selectById(1L); //用户2修改用户信息 user2.setName("jyc2"); //用户2执行修改操作 userMapper.updateById(user2); //用户1执行修改操作 userMapper.updateById(user1); }
- 只会执行用户2操作而不会执行用户1
3.6 分页查询
1、注册组件
/**
* 注册分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
2、直接使用page对象即可
@Test
void testPage(){
//查询第0页5个数据
Page<User> page = new Page<>(0,5);
userMapper.selectPage(page,null);
}
3、查询结果如图
3.7 逻辑删除
物理删除:直接从数据库删除
逻辑删除:没从数据库删除,通过变量使其失效
1、数据表添加deleted
字段
2、实体类中添加
/**
* 逻辑删除
*/
@TableLogic
private Integer deleted;
3、配置逻辑删除
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 #删除时为1
logic-not-delete-value: 0 #没删除时为0
4、删除测试
@Test
void testDelete(){
userMapper.deleteById(2L);
}
- 删除结果如下,逻辑位设置为1但并没有真正删除
3.8 性能分析插件
性能分析拦截器,用于输出每条SQL语句及其执行时间
- 具体操作见官网:https://baomidou.com/guide/p6spy.html
3.9 条件构造器(重点)
条件构造器Wrapper相当于在调用SQL语句的时候,添加一些限制条件,完成一些高级的SQL操作。
1、测试样例1
@Test
void testWrapper1(){
//查询邮箱和用户名不为空,且年龄大于等于12的信息
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age",12);
userMapper.selectList(wrapper);
}
- 测试结果如图
2、测试样例2
@Test
void testWrapper2(){
//查询名字为test
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","test");
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
- 测试结果如图
3、测试样例3
@Test
void testWrapper3(){
//查询年龄在10~20之间的人
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age",10,20);
userMapper.selectCount(wrapper);
}
- 测试结果如图
3.10 代码生成器
帮助快速生成基本CRUD代码
1、导入依赖
2、编写代码生成器规则
3、运行代码生成器
- 导入依赖
<!-- MyBatis Plus 代码生成工具 -->
<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>
- 编写代码生成器
package com.jyc.mybatisplus;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
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.TemplateConfig;
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 com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.fasterxml.jackson.databind.deser.SettableAnyProperty;
import java.util.ArrayList;
import java.util.Scanner;
/**
* @author Jyc
* @date 2021/1/3
*/
public class CodeGenerator {
/**
* 设置作者
*/
private final static String AUTHOR = "Jyc";
/**
* 驱动名称
*/
private final static String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
/**
* 用户名
*/
private final static String USER_NAME = "root";
/**
* 用户密码
*/
private final static String PASSWORD = "770880";
/**
* JDBC链接地址
*/
private final static String JDBC_URL = "jdbc:mysql://127.0.0.1:3306/mybatisplus?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";
/**
* 数据库类型
*/
private final static DbType DB_TYPE = DbType.MYSQL;
/**
* 模块名
*/
private final static String MODULE = "generator";
/**
* 项目路径
*/
private String projectPath = System.getProperty("user.dir");
/**
* 公共包路径
*/
private String parentPackage = "com.jyc";
/**
* 逻辑删除字段名
*/
private String logicDelete = "deleted";
/**
* 主函数入口
*
* @param args
*/
public static void main(String[] args) {
CodeGenerator codeGenerator = new CodeGenerator();
codeGenerator.execute();
}
/**
* 读取控制台内容
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入" + tip + ":");
if (scanner.hasNext()) {
String ipt = scanner.next();
if (ipt != null && !ipt.trim().isEmpty()) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
/**
* 主函数运行
*/
public void execute() {
// 代码生成器对象
AutoGenerator mpg = new AutoGenerator();
// 1.全局配置
GlobalConfig gc = new GlobalConfig()
// 设置输出目录
.setOutputDir(projectPath + "/src/main/java")
// 设置作者
.setAuthor(AUTHOR)
// 设置关闭资源管理器
.setOpen(false)
// 设置文件内容覆盖
.setFileOverride(true)
// 设置Service的I前缀
.setServiceName("%sService")
// 设置主键递增策略
.setIdType(IdType.AUTO)
// 设置日期类型
.setDateType(DateType.ONLY_DATE);
// 导入全局配置
mpg.setGlobalConfig(gc);
// 2.设置数据源
DataSourceConfig dataSourceConfig = new DataSourceConfig()
// 设置数据库URL地址
.setUrl(JDBC_URL)
// 设置数据库驱动名称
.setDriverName(DRIVER_NAME)
// 设置数据库用户名
.setUsername(USER_NAME)
// 设置数据库密码
.setPassword(PASSWORD)
// 设置数据库类型
.setDbType(DB_TYPE);
// 导入数据库配置
mpg.setDataSource(dataSourceConfig);
// 3.模块的配置
PackageConfig pc = new PackageConfig()
// 设置模块上级
.setParent(parentPackage)
// 设置模块名
.setModuleName(MODULE)
.setEntity("entity")
.setMapper("mapper")
.setService("server")
.setController("controller");
// 导入模块配置
mpg.setPackageInfo(pc);
// 4.策略配置
TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFillS = new ArrayList<>();
tableFillS.add(createTime);
tableFillS.add(updateTime);
StrategyConfig strategy = new StrategyConfig()
.setChainModel(true)
.setEntityLombokModel(true)
.setRestControllerStyle(true)
.setTableFillList(tableFillS)
.setLogicDeleteFieldName(logicDelete)
.setEntityTableFieldAnnotationEnable(true)
.setNaming(NamingStrategy.underline_to_camel)
.setColumnNaming(NamingStrategy.underline_to_camel)
.setInclude(scanner("表名,多个英文逗号分割").split(","))
.setRestControllerStyle(true)
.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
mpg.execute();
}
}