一 Mybatis-Plus概述
Myabtis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发,提高效率而生
二 SpringBoot融合Mybatis-Plus
1. 在pom.xml中引入对应的依赖
<!-- MybatisPlus场景启动器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
当然,因为是和数据库打交道,所有那些mysql驱动也需要引入,这个在Mybatis里面讲解了,不重复了
2. 添加配置文件
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
#输出SQL日志
logging:
level:
root: info
com.cctv.mapper: debug
3. 编写Mapper接口继承BaseMapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cctv.model.Employee;
public interface EmployeeMapper extends BaseMapper<Employee> {
}
因为BaseMapper中封装了很多可以直接使用的函数,可以满足大部分日常的CRUD功能
4. 在启动类里面添加@MapperScan注解
@MapperScan注解里面的值是自己project里面的Mapper包
三 Mybatis-Plus中的基本CRUD
我们这里使用mapper来做CRUD,所以可以通过@Autowired注入之前在mapper包自定义的mapper接口
1. 增(insert)
2. 删(delete)
3. 改(update)
4. 查(select)
这里重点讲解一下那些By的. ById就是根据id来操作,我们传入id即可.ByMap是根据map来操作,map里面的key必须是数据库里面的字段名,然后根据k/v来进行操作.selectBatchIds是传入一个数组,数组里面存放的是id值,底层调用了数据库的IN语法
四 Mybatis-Plus注解
1. @TableName: 用于定义表名
2. TableId: 用于定义表的主键
value: 数据库主键字段名
type: 用于定义主键类型(主键策略idType)
| 主键策略 | 介绍 |
|---|---|
| idType.AUTO | 主键自增 |
| idType.NONE | 未设置主键 |
| idType.INPUT | 主要自己输入主键 |
| idType.ASSIGN_ID | 系统分配ID,用于数据类型数据(long,对应mysql中的BIGINT类型) |
| idType.ASSING_UUID | 系统分配UUID,用于字符串型类型,(String,对应mysql中的varchar(32)) |
3. @TableField: 用于定义非主键字段
value: 用于定义非主键数据库字段名
exist: 用于指定是否为数据库字段
fill: 用于字段填充策略
3.1 字段填充策略
添加,修改数据时,每次都会使用相同的方式进行填充,比如说数据的创建时间,修改时间等,Mybatis-Plus支持自动填充策略
(1) 使用@TableField注解,标注需要进行填充的字段
(2) 自定义一个类,实现MetaObjectHandler接口,并重写方法,添加@Component注解
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createDate",new Date(),metaObject);
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
}
4. @TableLogic
@TableLogic: 逻辑删除. 逻辑删除相对于物理删除是修改数据的某个字段的状态,字段仍然保留在数据库中,只是在查询的时候不显示而已.我们可以给数据库增加一个delete_flag字段,用于表示逻辑删除
value: 表示没有删除的时候的状态,delval: 表示删除的状态.我们在使用mapper接口的那些方法时候底层会默认给我们加上这些查询条件
5. @Version
在多用户环境中,在同一时间可能会有多个用户更新相同的数据,这会产生冲突,这就是著名的并发性问题,解决的方案之一就是加乐观锁.
举个例子:
未加乐观锁:
张三想修改price-->update t1 set price=100;然后李四又想修改,update t1 set price=150;这时李四就会覆盖掉张三修改的信息,从而产生冲突
加了乐观锁:
张三修改update t1 set price=100,version=2 where version=1;然后李四再来修改的时候就会是update t1 set price=150 where version=1;由于之前version改变了,导致李四修改会出现失败,从而解决了并发问题.
5.1 在对应属性添加@Version注解
5.2 编写一个配置类将OptimisticLockerInterceptor添加到拦截器
五 Mybatis-Plus中使用Service
1. 自定义一个service接口继承IService
2. 自定义service实现类继承ServiceImpl同时实现"1"中自定义的service
3. Wrapper
IService里面封装BaseMapper里面的很多CRUD方法,我们可以直接调用.我这里将一个重点Wrapper(筛选条件)
QueryWrapper(LambdaQueryWrapper)是用于查询;UpdateWrapper(LambdaUpdateWrapper)适用于修改.
3.1 QueryWrapper
无论是select还是eq里面的第一个都是数据库里面的字段名,第二个就是需要操作的值
3.2 LambdaQueryWrapper
相比较于QueryWrapper,LambdaQueryWrapper里面传递的就是把数据库里面的字段名改成"实体类::getXXX"
3.3 UpdateWrapper
相比较QueryWrapper,这里使用set,因为要涉及到修改.同时里面传递的参数是数据库字段名
3.4 LambdaUpdateWrapper
相比于UpdateWrapper,这里所有的方法传递的参数从数据库字段名改成了"实体类::getXXX"
3.5 Wrapper常用方法
eq: 等于(=)
ne: 不等于(!=)
gt: 大于(>)
ge: 大于等于(>=)
lt: 小于(<)
le: 小于等于(<=)
select: 查询
set: 修改
like: 模糊查询
六 Mybatis-Plus分页插件
Mybatis-Plus内部整合了PageHelper,所以我们只需要引入即可
1. 在拦截器引入PageHelper
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
}
2. 引入hutool工具(为了序列化)
hutool官网: hutool.cn/
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
3. 使用PageHelper
current: 代表当前页, size: 代表每页数量
可以直接使用service调用page来使用分页
JSONUtil.toJsonStr: 用于序列化
七 Mybatis-Plus自定义sql
1. 在resource下面新建一个mapper包,然后在mapper下新建对应的xml文件
2. 在之前自定义的mapper接口中自定义方法,然后在对应的xml文件中写对应的sql语句,类似于之前Mybatis
八 Mybatis-Plus代码生成器(类似于Mybatis中的逆向工程)
1. 导入依赖
<!-- 代码生成器的依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
2. 在工程下生成一个类,并且配置
package com.cctv;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.LikeTable;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class GeneratorApp {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
// 判断用户是否输入
if (scanner.hasNext()) {
// 拿到输入内容
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
String moduleName = scanner("模块名");
String tableName = scanner("表名(多个用,号分隔,或者按前缀(pms*))");
String prefixName = scanner("需要替换的表前缀");
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
// 获得当前项目的路径
String projectPath = System.getProperty("user.dir")+"/generator";
// 设置生成路径
gc.setOutputDir(projectPath + "/src/main/java");
// 作者
gc.setAuthor("沐珀尘");
// 代码生成是不是要打开所在文件夹
gc.setOpen(false);
// 生成Swagger2注解
gc.setSwagger2(true);
// 会在mapper.xml 生成一个基础的<ResultMap> 映射所有的字段
gc.setBaseResultMap(true);
// 同文件生成覆盖
gc.setFileOverride(true);
//gc.setDateType(DateType.ONLY_DATE)
// 实体名:直接用表名 %s=表名
gc.setEntityName("%s");
// mapper接口名
gc.setMapperName("%sMapper");
// mapper.xml 文件名
gc.setXmlName("%sMapper");
// 业务逻辑类接口名
gc.setServiceName("%sService");
// 业务逻辑类实现类名
gc.setServiceImplName("%sImplService");
// 将全局配置设置到AutoGenerator
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
// 模块名
pc.setModuleName(moduleName);
// 包名
pc.setParent("com.cctv");
// 完整的报名: com.cctv.pms
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 把已有的xml生成置空
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
// 表名的生成策略:下划线转驼峰 pms_product -- PmsProduct
strategy.setNaming(NamingStrategy.underline_to_camel);
// 列名的生成策略:下划线转驼峰 last_name -- lastName
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
// 在controller类上是否生成@RestController
strategy.setRestControllerStyle(true);
// 公共父类
//strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
if(tableName.indexOf('*')>0){
// 按前缀生成表
strategy.setLikeTable(new LikeTable(tableName.replace('*','_')));
}
else{
// 要生成的表名 多个用逗号分隔
strategy.setInclude(tableName);
}
// 设置表替换前缀
strategy.setTablePrefix(prefixName);
// 驼峰转连字符 比如 pms_product --> controller @RequestMapping("/pms/pmsProduct")
//strategy.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
// 进行生成
mpg.execute();
}
}
2.1 模块名
2.2 表名
自然就是数据库名表,如果有多个可以用","隔开,如果前缀一样可以使用图中样例
2.3 替换的前缀
因为数据库表名大部分采用蛇形命名,但是我们生成javaBean实体类采用的是小驼峰,所以需要把部分东西替换掉