MybatisPuls学习
前言
学习之前,需要有SSM基础比较好,Mybatis可以节省我们大量的sql代码编写的时间,尤其在简单的sql语句中只需要调用一个方法即可完成增删改查
![在这里插入图片描述](https://img-blog.csdnimg.cn/e06f79d0c29b497dbde8c3575c3f56fb.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiN5aSq5oeC57yW56iL,size_20,color_FFFFFF,t_70,g_se,x_16)
正如官方所介绍为简化开发而生
一、MybatisPuls有什么特点
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。
mysql,oracle,db2,h2,hsql,sqlite,postgresql,sqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb
二、使用步骤
1.快速入门(官网案例)
- 创建数据库和user表
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)
);
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');
- 使用IDEA创建工程 官网默认用h2数据库我们不需要进行导入
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus不用在导入mybatis了-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
- 配置数据源
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 开始编码
- 传统mybatis写法依然支持不做介绍,可参考mybatis原生案例
- 使用mybatis-plus (pojo->mapper接口->使用)我们可以看到都不需要配置xml文件
创建User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
创建Mapper接口
//在对应的Mapper上面是实现基本的接口BaseMapper
@Repository
public interface UserMapper extends BaseMapper<User> {
//所有的crud的擦欧总已经编写完毕了
}
简单看看baseMapper里面已经定义好啦非常多的接口
入口文件增加注解扫描Mapper
@MapperScan("cn.qileyun.mybatisplus.mapper")
测试一下看看
//继承baseMapper所有可以使用它的所有方法,里面已经封装好了基本crud代码
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
2. 配置日志输出
#配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3. MybatisPlus中的雪花算法
User user = new User();
user.setName("我爱学编程");
user.setAge(3);
user.setEmail("1541177517@qq.com");
int result = userMapper.insert(user);//帮我们自动生成id
System.out.println(result);
System.out.println(user);
我们并没有插入id,当是给我们生成一个全局唯一id,我们来探究一下
4. 自定义ID生成策略
在pojo类中定义id
- AUTO 自增 数据库id必须是自增的
- UUID uuid全球唯一id
- INPUT 手动输入需要自己配置id
- ID_WORKER_STR 数字->字符串
- INONE 不使用id生成
- ID_WORKER 默认全局唯一id 雪花算法
@TableId(type = IdType.AUTO)//自增 数据库id必须是自增的
private Long id;
5. 自动填充
- 修改数据库
- 修改实体类
// 字段填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
- 编写处理器
@Slf4j
@Component//加入到ico容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@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);
}
}
- 执行插入和修改
6. 乐观锁
-
乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,在次更新值测试
-
悲观锁:顾名思义十分悲观,无论干什么都会上锁!再去操作!
-
取出记录时,获取当前version
-
更新时,带上version
-
执行更新时,set version = newVersion where = version = oldVersion
-
如果version不对,就更新失败
--A
update user set name = "lala",version = version+1 where id=2 and version = 1
--B 如果现在B先抢占了线程,就会导致A修改失败,因为version字段已经+1操作变成了2了
update user set name = "lala",version = version+1 where id=2 and version = 1
使用乐观锁插件
- 增加字段
- 实体类添加字段
@Version
private Integer version;
- 注册组件
//扫描我们的mapper文件夹
@MapperScan("cn.qileyun.mybatisplus.mapper")
@EnableTransactionManagement
@Configuration//配置类
public class MybatisPlusConfig {
//注册乐观锁插件 这个按照你的mybatis二选一
/**
* 旧版
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
- 测试看看
// 测试成功的案例
@Test
public void testOptimisticLocker() {
//1. 查询用户信息
User user = userMapper.selectById(1L);
//2.修改用户信息
user.setName("zengfeiixang");
user.setEmail("1541177517@qq.com");
//3. 执行更新操作
userMapper.updateById(user);
}
// 测试失败的案例
@Test
public void testOptimisticLocker2() {
//1. 查询用户信息
User user = userMapper.selectById(1L);
//2.修改用户信息
user.setName("bbbbb");
user.setEmail("1541177517@qq.com");
//模拟另外一个线程执行插队操作
User user2 = userMapper.selectById(1L);
user2.setName("aaaaaa");
user2.setEmail("1541177517@qq.com");
//3. 执行更新操作
userMapper.updateById(user2);//执行成功
userMapper.updateById(user);//更改失败如果没有乐观锁就会覆盖插队线程的值
}
7. 查询
基本查询
//测试查询
@Test
public void testSelectById(){
//查询单个
User user = userMapper.selectById(1L);
System.out.println(user);
}
//测试批量查询
@Test
public void testSelectByBatchId(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
users.forEach(System.out::println);
}
//条件查询 map
@Test
public void testSelectByBatchIds(){
HashMap<String, Object> map = new HashMap<>();
//自定义查询
map.put("name","Tom");
map.put("age",28);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
分页在网站使用的十分之多
1、原始的limit进行分页
2、pageHelper 第三方插件
3、MP其实内置了分页插件
使用教程
- 注册插件
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 新版本好像用这个了没有测试 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
- 测试一下
//测试分页查询
public void testPage(){
//参数1 第一页
//参数2 一页条目数量
IPage<User> page = new Page<>(1,5);
IPage<User> userIPage = userMapper.selectPage(page, null);
userIPage.getRecords().forEach(System.out::println);
System.out.println(page.getTotal());//总计
}
8. 删除
普通删除
//测试删除
@Test
public void testDeleteById(){
userMapper.deleteById(1468132628929495041L);
}
//批量删除
@Test
public void testDeleteBatchId(){
userMapper.deleteBatchIds(Arrays.asList(1468132628929495041L,1468132628929495042L));
}
//通过Map删除
public void testDeleteMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","lala");
userMapper.deleteByMap(map);
}
逻辑删除
物理删除:从数据库中直接移除
逻辑删除:在数据库中没有移除,而是通过一个变量来让他失效!delete =0 => delete=1
可以把他看成一个回收站一样,只是给他标识一个已经不存在了
-
数据库增加字段
增加int类型的delete字段并且默认值为0
-
实体类增加字段
@TableLogic
private Integer deleted;
- 编写配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
- 注册插件
//逻辑删除
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
- 测试删除代码
其实执行的不在是delete操作了而是更新了字段吧deleted = 1
查询看看可以看出已经自动加上了deleted = 0的条件,判断是否被删除过
9. 性能分析插件(好像新版被移除了)
MP提供了性能分析插件,如果超过这个时间就停止运行!
- 导入插件
//sql执行效率插件
@Bean
@Profile({"dev","test"})//设置dev test环境开启,保证我们的效率
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//设置sql执行的最大时间,如果超超过了就不执行
performanceInterceptor.setFormat(true);//sql格式化
return new PerformanceInterceptor();
}
- 配置spring为开发环境
spring:
profiles:
active: dev
- 测试使用
如果这个时间超过100ms就会报错
9. 条件构造器
可以看出官方有非常多的条件
判断条件
@Test
void contextLoads() {
//查询name不为空的用户,年龄大于等于12,并且邮箱不为空
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name").isNotNull("email").ge("age",12);
userMapper.selectList(wrapper).forEach(System.out::println);
}
@Test
void test2(){
//查询名字lala
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","lala");
//selectOne 查询一个数据
System.out.println(userMapper.selectOne(wrapper));
}
//查询 10 到 20 岁的用户数
@Test
void test3(){
QueryWrapper<User> wrapper=new QueryWrapper<>();
wrapper.between("age",10,20);
Integer count = userMapper.selectCount(wrapper);
//查询 10 到 20 岁的用户的结果数
System.out.println(count);
}
使用 like + notLike + likeRight
@Test
void selectLike01(){
QueryWrapper<User> wrapper=new QueryWrapper<>();
wrapper
.notLike("name","To") //名字不包含 To
.like("name","o") //名字包含 o 的
//左和右 左:%e 右:e% 两边:%e%
//右查询
.likeRight("email","test");
List<Map<String, Object>> users = userMapper.selectMaps(wrapper);
users.forEach(System.out::println);
}
子查询
@Test
void selectLike02(){
QueryWrapper<User> wrapper=new QueryWrapper<>();
wrapper
//一个SQL语句写的子查询
.inSql("id","select id from user where age<20");
List<Object> users = userMapper.selectObjs(wrapper);
users.forEach(System.out::println);
}
降序查询
@Test
void selectLike03(){
QueryWrapper<User> wrapper=new QueryWrapper<>();
//降序排序
wrapper.orderByDesc("id");
//升序排序
//wrapper.orderByAsc("id");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
更多的可以参考官方文档
10. 代码生成器
- 添加依赖
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.0.7</version>
</dependency>
<!--模版 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
- 测试类编写生成代码类
//需要构建一个代码生成器对象
AutoGenerator mpg = new AutoGenerator();
//全局配置
GlobalConfig gc = new GlobalConfig();
String property = System.getProperty("user.dir");
gc.setOutputDir(property+"/src/main/java");
gc.setAuthor("奇乐云");
gc.setOpen(false);
gc.setFileOverride(false);//是否覆盖
gc.setServiceImplName("%sService");//去除Service的I前缀
gc.setIdType(IdType.ID_WORKER);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//设置数据源
DataSourceConfig dsc= new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//包的配置
PackageConfig pc = new PackageConfig();
//pc.setModuleName("blog");//模块
pc.setParent("cn.qileyun.mybatisplus");//包根路径
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
//策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("user");//设置要映射的表 可以传入多个表
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);//自动lombok
strategy.setRestControllerStyle(true);
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);
//乐观锁
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);//
mpg.setStrategy(strategy);
mpg.execute();//执行策略
看到一个写的不错的代码生成器
package com.yixin;
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.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 org.junit.platform.commons.util.StringUtils;
import java.util.ArrayList;
import java.util.Scanner;
public class CodeGenerator {
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");
gc.setOutputDir(projectPath + "/src/main/java");//设置代码生成路径
gc.setFileOverride(true);//是否覆盖以前文件
gc.setOpen(false);//是否打开生成目录
gc.setAuthor("yixin");//设置项目作者名称
gc.setIdType(IdType.AUTO);//设置主键策略
gc.setBaseResultMap(true);//生成基本ResultMap
gc.setBaseColumnList(true);//生成基本ColumnList
gc.setServiceName("%sService");//去掉服务默认前缀
gc.setDateType(DateType.ONLY_DATE);//设置时间类型
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.yixin");
pc.setMapper("mapper");
pc.setXml("mapper.xml");
pc.setEntity("pojo");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig sc = new StrategyConfig();
sc.setNaming(NamingStrategy.underline_to_camel);
sc.setColumnNaming(NamingStrategy.underline_to_camel);
sc.setEntityLombokModel(true);//自动lombok
sc.setRestControllerStyle(true);
sc.setControllerMappingHyphenStyle(true);
sc.setLogicDeleteFieldName("deleted");//设置逻辑删除
//设置自动填充配置
TableFill gmt_create = new TableFill("create_time", FieldFill.INSERT);
TableFill gmt_modified = new TableFill("update_time", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills=new ArrayList<>();
tableFills.add(gmt_create);
tableFills.add(gmt_modified);
sc.setTableFillList(tableFills);
//乐观锁
sc.setVersionFieldName("version");
sc.setRestControllerStyle(true);//驼峰命名
// sc.setTablePrefix("tbl_"); 设置表名前缀
sc.setInclude(scanner("表名,多个英文逗号分割").split(","));
mpg.setStrategy(sc);
// 生成代码
mpg.execute();
}
}
- 测试看看
其他参数可以参考官网进行配置,新版本做了很多更新。
总结
这里是看了狂神说的mybatis plus的入门教程,但是看了官网最新的mybatis plus貌似改了蛮多东西的,有机会多看看官网教程。