一、什么是Mybatis-plus
Mybatis的出现 是为了简化JDBC的复杂操作,而Mybatis-plus的出现则是为了简化Mybatis的操作,它不会改变mybatis原有的东西,只会在原有的基础上增加功能,可以说,它是非入侵式的,蕴含了AOP的思想
二、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操作CRUD的数据库
三、快速入门
- 现有一张
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 |
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');
-
创建一个SpringBoot项目
-
添加MybatisPlus启动器依赖
<!-- mybatis-plus-启动器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
-
编写实体类
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
-
编写Mapper类
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}
/*
继承BaseMapper<T>类 T是一个泛型 这里填入我们的实体类
我们就可以直接使用MybatisPlus为我们封装好的CRUD方法
*/
-
编写数据库数据源和日志
#数据库连接配置 mysql 8.0版本的多了时区设置 GMT%2b8就是我们东八区的时区 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=GMT%2b8 username: root password: 123456 #日志配置 默认控制台输出 这里我们使用默认的日志输出 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
测试
@Autowired UserMapper userMapper; @Test void contextLoads() { List<User> users = userMapper.selectList(null); for (User user : users) { System.out.println(user); } }
这样 我们的Mybatis-Plus环境就算搭建完成了
四、CRUD的探究
Insert
查看insert的方法 只有一个 返回值是一个entity实体类对象
我们在不插入ID的情况下 进行测试看看发生什么
//插入测试
@Test
public void text1(){
User user = new User();
user.setAge(18);
user.setName("狂神");
user.setEmail("147258369@qq.com");
userMapper.insert(user);
}
通过控制台输出 我们发现 它自动给我们生成了一个Long类型的数字 那么它是怎么来的呢?
原来,Mybatis-plus的insert语句中,内置了主键自增策略,当我们的ID为NULL时,它会自动给我们生成一串数字,这串数字是由雪花算法
产生的,而产生的前提是ID的类型必须为Long
类型,雪花算法也是它的默认类型
注意:
如果我们的ID类型为其他类型。且插入的时候为NULL 运行程序就会报错
Caused by:
org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.llf.Pojo.User' with value '1418934148565385218' Cause: java.lang.IllegalArgumentException: argument type mismatch
翻译:无法将long类型的值'1418934148565385218'赋给ID
雪花算法是什么呢?
说白了 它就是一种随机生成主键的算法 它有以下特点
- 最高位是符号位,始终为0,不可用。
- 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
- 10位的机器标识,10位的长度最多支持部署1024个节点。
- 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
其他的主键生成策略
在mybatis-plus中,除了默认的雪花算法还有许多主键生成策略
-
ASSIGN_ID
Twitter的分布式自增ID算法
雪花算法 就是我们默认的主键生成策略形式:
1418884175761063937
-
ASSIGN_UUID
形式:
6904b36e-e220-2fcf-4e00-23c3449e1e24
随机生成UUID,UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写 优点是
优点:性能非常高,JDK自带本地生成,无网络消耗
缺点:(1)只保证了唯一性,趋势递增。
(2)无序,无法预测他的生成规则,不能生成递增有序的数字。
(3)mysql官方推荐主键越短越好,UUID包含32个16位进制的字母数字,每一个都很长。
(4)B+树索引的分裂。主键是包含索引的,mysql的索引是通过B+树来实现的,因为UUID是无序的,插入无序,不但会导致一些中间节点产生分裂,也会白白创造很多不饱和的节点,大大降低了数据库插入的性能。
-
AUTO
主键自增,也是最简单的主键生成策略,但是实现的前提必须是数据库必须打开主键自增的设置
-
INPUT
自己输入 即自己Set注入
User user = userMapper.selectById(1l); user.setId(2L); userMapper.updateById(user);
-
NONE
什么也不输入 如果这张表中没有主键,即为NULL 但基本上不会这么做。
delete
物理删除
:delete 语句就是物理删除 一旦删除 就没有了
逻辑删除
:通过代码设置 在查询的时候不显示 实际上数据库中还有 只是状态不同了 类似于回收站
物理删除
-
deleteById
通过单个id删除
-
deleteBatchIds
通过多个id同时删除
-
deleteByMap
通过一个Map集合删除
@Test public void text5(){ userMapper.deleteById(1418846421555712001l); System.out.println("通过id删除成功"); userMapper.deleteBatchIds(Arrays.asList(1418844779586318338l,1418844779586318337l)); System.out.println("多个ID删除成功"); HashMap<String, Object> map = new HashMap<>(); map.put("id","10"); userMapper.deleteByMap(map); System.out.println("通过map删除成功"); }
逻辑删除
- 逻辑删除的本质就是通过查询不同状态 来显示数据 所以我们在数据库中增加字段
deleted
我们设定0为正常显示 1为不显示 并设置默认值为0
- 然后在Pojo实体类中完善字段 并添加注解
//逻辑删除注解
@TableLogic
private Integer deleted;
- 在application中进行
0
、1
显示设置
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
- 测试
userMapper.deleteById(4);
System.out.println("通过id删除成功");
这时我们可以发现删除语句 在后台已经变成了更新语句
-
查询
@Test void contextLoads() { List<User> users = userMapper.selectList(null); for (User user : users) { System.out.println(user); } }
通过查询 和数据库查询 我们发现这些值在数据库中依然存在 只是查询不到了 这就表明实现了逻辑删除
updete
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LI180oOJ-1627193026476)(C:\Users\machenike\AppData\Roaming\Typora\typora-user-images\image-20210725092011217.png)]
更新 只有两种方法 ,一种是传入一个对象,一种是传入对象+构造器Wrapper 关于构造器 我们这里先让他为null
-
update
传入一个对象 修改所有值 相当于普通更新 没有
where
条件条件(Wapper构造器可以添加条件 但这里先不用)
@Test
public void text8(){
User user = new User();
user.setId(1418932799022907393L);
user.setAge(18);
user.setName("狂");
user.setEmail("147258369@qq.com");
userMapper.update(user,null);
}
他把所有信息都更改了
- updateById
顾名思义 传入一个对象 通过id修改内容
@Test
public void text8(){
User user1 = new User();
user1.setId(11L);
user1.setAge(1);
// userMapper.update(user,null);
userMapper.updateById(user1);
}
自动填充
按照规范,我们在数据库设计的过程中会加入create_time、update_time,但是这些时间理论上应该是自动生成,而不是我们一个一个New Data()实现的 Mybatis-Plus就为我们完成了这个功能
- 在数据库添加字段 create_time、update_time
- 完善实体类,在属性create_time、update_time上添加注解
//在插入时填充时间
@TableField(fill = FieldFill.INSERT_UPDATE )
private Date createTime;
//更新时更新时间
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
- 创建
MetaObjectHandler
类,进行设置
package com.llf.Handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
//添加@Componment注解 表示被Spring托管
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 自动填充时间
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
//插入时填充 create_time、update_time时间
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
//修改时 填充update_time时间
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
- 测试
select
和删除的方法一样 就不过多叙述
五、乐观锁
什么是乐观锁?
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。
简单来说,就是 比较乐观,认为程序是不会出毛病的,如果出毛病了 就进行解决
而悲观锁与其相反,悲观锁在做任何事的时候,都会提前检查,这样虽然提高了安全性,但使效率大大降低
实现乐观锁
- 首先在数据库添加字段 version 使其默认值为1
- 完善实体类
//添加注解 告诉系统 这是一个乐观锁
@Version
private Integer version;
- 创建config类 配置引入插件配置(全是官网原码,我们直接复制就行)
package com.llf.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
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;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//打开事务管理 即事务回滚之类的操作
@EnableTransactionManagement
// 告诉springBoot 这是spring配置类
@Configuration
public class MybatisplusConfig {
/**
乐观锁: OptimisticLockerInnerInterceptor
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
- 测试
//乐观锁成功测试
// 所谓成功,就是对象A查询数据库后,拿到Version 在执行更新操作的时候 将拿到的version与此时数据库中对应的version进行对比,刚好一致 因此 就成功了
@Test
public void text2(){
User user = userMapper.selectById(1l);
user.setId(11L);
user.setName("过");
userMapper.updateById(user);
}
//乐观锁失败测试
// 所谓失败,就是一个对象A查询完数据库后,在提交更新操作之前,另一个对象B也查询了数据库,并完成了更新操作,这时数据库中的version是A查询的version+1,这显然与A查询的version不相等 所以就失败了
@Test
public void text3(){
User user = userMapper.selectById(1l);
user.setId(2L);
user.setName("杨过");
User user2 = userMapper.selectById(1l);
user.setId(2L);
user.setName("杨过");
userMapper.updateById(user2);
userMapper.updateById(user);
}
六、分页
Mybatis-plus为我们准备了比PageHelper更为方便的分页插件
- 引入分页插件
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor1() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
将其放在 乐观锁的config配置文件中
因为同属于MybatisPlus配置文件
- 测试
//分页查寻
@Test
public void text7(){
//第1页 显示3个 第一个参数是Current 第二个参数是Size
Page<User> page = new Page<>(1,3);
userMapper.selectPage(page,null);
//records 总记录
List<User> userList = page.getRecords();
for (User user : userList) {
System.out.println(user);
}
long size = page.getSize();
System.out.println("页面显示大小"+size);
long total = page.getTotal();
System.out.println("总数量"+total);
long current = page.getCurrent();
System.out.println("当前页码"+current);
long pages = page.getPages();
System.out.println("总页数"+pages);
}
和PageHelper一样 在查询之前 横切了一个count(*)的查询 来查询total
在 查询后自动添加limit 分页查询 得到结果
总记录:records
页面显示大小":size
总数量":total 总记录是一个集合 总数量是一个值
当前页码":current
"总页数":pages
七、条件构造器
条件构造器就好比是一个能够自动帮你拼接sql的工具,按照约定大于配置
的理念,我们只需要在合适的地方填入合适的参数,条件构造器就能帮我们完成复杂查询、子查询等操作
https://mp.baomidou.com/guide/wrapper.html#select
官方提供了许多接口供我们使用,且每一个接口的使用、参数说明都讲的很清楚
-
测试
@Test public void test1(){ /* 模糊查询 查询名字里有狂字的所有人 查询操作 就创建QueryWrapper<>() 对应的 什么操作 就创建什么操作的Wrapper */ QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.like("name","狂"); //如果查询一个人 就使用selectOne() 这样效率更快 List<Object> list = userMapper.selectObjs(wrapper); for (Object o : list) { System.out.println(o); } } @Test void test2(){ //查询eamil以T开头的人 QueryWrapper<User> wrapper = new QueryWrapper<>(); //以xxx开头 就是xx% 也就是likeRight //以xxx结尾 就是%xxx 也就是likeLeft wrapper.likeRight("email","t"); List<Object> list = userMapper.selectObjs(wrapper); for (Object o : list) { System.out.println(o); } } @Test void test3(){ //查询名字不为空的人 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("name"); List<Object> objects = userMapper.selectObjs(wrapper); for (Object object : objects) { System.out.println(object); } }
更多的例子 可以参考官方例子
八、代码生成器
代码生成器 也就是我们常说的逆向工程 他可以帮助我们自动生成底层的代码框架。Pojo、Mapper、service、Controller
且支持自动生成Swagger2API注解 乐观锁、自动填充等等
- 导入依赖
MyBatis-Plus 从 3.0.3
之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖
<!-- 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 引擎模板-->
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
代码
package com.llf;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
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.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class MybatisPlusCode {
/**
* <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) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
/**
全局配置
*/
GlobalConfig gc = new GlobalConfig();
//扫描当前路径
String projectPath = System.getProperty("user.dir");
//代码生成在/src/main/java目录下
gc.setOutputDir(projectPath + "/src/main/java");
//作者名字
gc.setAuthor("llf");
//是否打开生成后的文件夹
gc.setOpen(false);
//实体属性 Swagger2 注解
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
/**
*
/ 数据源配置
*
*/
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
//将数据源仍进代码生成器
mpg.setDataSource(dsc);
/**
* 包配置
*/
PackageConfig pc = new PackageConfig();
// 模块名
pc.setModuleName(scanner("模块名"));
//项目包结构 例如com.llf.xxx
pc.setParent("com.llf");
//实体类包名
pc.setEntity("Pojo");
//Mapper包名
pc.setMapper("Mapper");
//Service包名
pc.setService("Service");
//去掉service的前缀
gc.setServiceName("%sService");
//ServiceImpl包名
pc.setServiceImpl("ServiceImpl");
//Mapperxml包名
pc.setXml("Mapperxml");
//将包结构设置仍进生成器
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 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.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录,自定义目录用");
if (fileType == FileType.MAPPER) {
// 已经生成 mapper 文件判断存在,不想重新生成返回 false
return !new File(filePath).exists();
}
// 允许生成模板文件
return true;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
//包名类名驼峰自动转换
strategy.setNaming(NamingStrategy.underline_to_camel);
//数据库字段驼峰自动转换
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//实体类字段Lambok
strategy.setEntityLombokModel(true);
//Controller自动restful风格
strategy.setRestControllerStyle(true);
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
问题注意
为什么JPA不能使用?
JPA提示的方式需要根据Mapper找到实体类, 找到实体类有以下五种方式
- 继承mybatis-plus的BaseMapper
- Mapper.xml 文件有 resultMap 标签
- 在Mapper类上增加注释指定实体类, 例如:
@Entity com.xx.xx.UserModel
为什么生成的表名和期望的表名不一致
JPA提示生成代码, 按照以下规则找到表名
- 实体类有JPA注解, 例如:
@Table(name="t_user")
- 实体类有mybais-plus注解, 例如:
@TableName("t_user")
- 实体类有注释:
@TableName com.xx.xx.UserModel
- 如果不存在以上规则, 将驼峰转下划线. 例如 UserMode 的表名为: user_model
拓展
如果觉得这样子配置很麻烦,我们可以试试官方新出的MybatisX的插件 它简化了更多配置 可以说是安装即用
https://mp.baomidou.com/guide/mybatisx-idea-plugin.html