目录
- 1. 特性
- 2. 快速入门
- 3. CRUD
- 3.1 insert
- 3.2 update
- 3.3 自动填充
- 3.4 乐观锁&悲观锁
- 3.5 查询操作
- 3.6 分页查询
- 3.7 delete
- 4 性能分析插件
- 5. 条件构造器
- 6. 代码自动生成
官网:https://baomidou.com/
1. 特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作,BaseMapper
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求,以后简单的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. 快速入门
使用第三方组件:
- 导入对应依赖
- 研究依赖如何配置
- 代码如何编写
- 提高扩展技术能力!
步骤
1、创建数据库 mybatis_plus
2、建个 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));-- 真实开发中必备字段:version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified
插几条数据
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');
3、建个SpringBoot项目
删掉没用的文件
导入依赖
pom
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId></dependency><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version></dependency>
说明:我们使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus! 版本的差异!
4、连接数据库
这一步和 mybatis 一样
application.yml
spring: #配置数据源 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus" /> 主键生成策略:
数据库插入的 id 为全局默认的 id(ID_WORKER
)
我们需要配置主键自增:
- 在实体类字段上添加注解:
@TableId(type =IdType.AUTO)
public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email;}
- 数据库字段一定要是自增的。(“设计表”—id字段勾选“自增”)
其余参数的含义:
public enum IdType { AUTO(0), //数据可id自增 NONE(1), //未设置主键 INPUT(2), //手动输入 ID_WORKER(3), //默认的全局唯一id UUID(4), //全局唯一id uuid ID_WORKER_STR(5); // ID_WORKEK 字符串表示法 private int key; private IdType(int key) { this.key = key; } public int getKey() { return this.key; }}
分布式系统唯一id生成方案:https://www.cnblogs.com/haoxinyue/p/5208136.html
雪花算法:Twitter的snowflake算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一
再插入一个李四:id 确实自增了
3.2 update
动态sql
注意:updateById()参数是 一个对象!
//测试更新@Testpublic void testUpdate(){ User user = new User(); user.setId(2L); user.setName("阿大"); //注意:updateById()参数是 一个对象! int i = userMapper.updateById(user); System.out.println(i);}
3.3 自动填充
创建时间、修改时间!这些个操作都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create .gmt_modified几乎所有的表都要配置上!而且需要自动化!
方式一:数据库级别(工作中是不允许修改数据库的)
1.在表中新增字段 create_time , update_time
两个字段都勾选:
2.再次测试插入方法,我们需要先把实体类同步
private Data creatTime;private Data updateTime;
@Testpublic void testUpdate(){ User user = new User(); user.setId(2L); user.setAge(18); user.setName("阿大"); //注意:updateById()参数是 一个对象! int i = userMapper.updateById(user); System.out.println(i);}
方式二:代码级别
1.删除数据库所有字段的默认值
2.实体类字段属性上添加注解
//记住用util包下的Date!!//字段添加填充内容@TableField(fill = FieldFill.INSERT)private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;
3.编写处理器来处理这个注解
package com.kuang.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@Component //把处理器加到IOC容器中public class MyMetaObjectHandler implements MetaObjectHandler { //插入时的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("Start insert fill.... "); this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } //更新时的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("Start update fill.... "); this.setFieldValByName("updateTime",new Date(),metaObject); }}
4.测试插入
@Testpublic void testInsert() { User user = new User(); user.setName("王八"); user.setAge(160); user.setEmail("222@qq.com"); int result = userMapper.insert(user); //帮我们生成id System.out.println(result); //受影响的行数 System.out.println(user); //发现id会自动回填}
成功!
3.4 乐观锁&悲观锁
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,他总是认为出现问题,无论干什么都会上锁!再去操作!
我们这里主要讲解 乐观锁机制!
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时,set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
案例:
1、给 user 表增加字段 version,默认值设为 1
现有数据的 version 都设置成 1
2、实体类 User 增加相应字段
@Version //乐观锁的注解private Integer version;
3、注册组件
package com.kuang.config;import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;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.kuang.mapper")//主启动类上的这个注解可以去掉了public class MyBatisPlusConfig { //注册乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }}
4、测试一下
模拟成功的情况
@Testpublic void testOptimisticLocker() { // 1、查询用户信息 User user = userMapper.selectById(1L); // 2、修改用户信息 user.setName("狂神"); user.setEmail("156420988@qq.com"); // 3、执行更新操作 userMapper.updateById(user);}
version 更新为 2
模拟失败的情况
@Testpublic void testOptimisticLocker2() { //模拟线程1 User user = userMapper.selectById(1L); user.setName("狂神22"); user.setEmail("100000@qq.com"); //此时线程2插队执行 User user2 = userMapper.selectById(1L); user2.setName("狂神33"); user2.setEmail("1111111@qq.com"); userMapper.updateById(user2); //抢先 update userMapper.updateById(user);}
可以看到,线程 1 的修改并未生效!
3.5 查询操作
//测试查询@Testpublic void testSelectById(){ User user = userMapper.selectById(1L); System.out.println(user);}//测试批量查询@Testpublic void testSelectBatchId(){ List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println);}//按条件查询之--使用Map操作@Testpublic void testSelectBatchIds(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","狂神33"); map.put("age","18"); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println);}
3.6 分页查询
分页在网站使用的十分之多!
1、原始的 limit 进行分页
2、pageHelper第三方插件
3、mybatis-plus其实也内置了分页插件!
如何使用?
配置拦截器组件即可
MyBatisPlusConfig 类
@Beanpublic PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor();}
测试分页:
//测试分页查询@Testpublic void testPage() { // 参数一:当前页 // 参数二:页面大小 // 使用了分页插件之后,所有的分页操作也变得简单了 Page<User> page = new Page<>(2, 5); userMapper.selectPage(page, null); page.getRecords().forEach(System.out::println); //获取总数 page.getTotal();}
3.7 delete
根据 entity 条件,删除记录: int delete(@Param(Constants.WRAPPER) Wrapper wrapper);
删除(根据ID 批量删除): int deleteBatchIds(@Param(Constants.COLLECTION) Collection
- 实体类增加对应属性,并添加
@TableLogic
注解
User 类
@TableLogic //逻辑删除private Integer deleted;
- 注册组件
MyBatisPlusConfig类
//注册逻辑删除@Beanpublic ISqlInjector sqlInjector(){ return new LogicSqlInjector();}
- 配置 application.yml
global-config 是在 mybatis-plus 的下一级
global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0
- 测试删除
@Testpublic void testDeleteById(){ userMapper.deleteById(2L); //必须加L转为long}
在数据表中依然有该用户,只是 deleted 字段变为了 1
再查询一下该用户,看看能不能查到?查不到,因为它会自动拼接 deleted 字段进行筛选,只查询 deleted = 0 的记录
4 性能分析插件
我们在平时的开发中,会遇到一些慢 sql
mybatis-plus 也提供了性能分析插件,如果超过这个时间就停止运行!
1、导入插件
MyBatisPlusConfig 类
@Bean@Profile({"dev","test"}) //设置dev 和 test环境开启public PerformanceInterceptor performanceInterceptor(){ PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(1); //sql语句超过1ms就不执行了 performanceInterceptor.setFormat(true);//是否设置sql语句格式化 return performanceInterceptor;}
application.yml中添加设置开发环境
spring: profiles: active: dev
2、使用插件
随便执行一个测试类中的 sql 语句,会报以下错误:
The SQL execution time is too large, please optimize !
5. 条件构造器
package com.kuang;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.kuang.mapper.UserMapper;import com.kuang.pojo.User;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import java.util.List;import java.util.Map;@SpringBootTestpublic class WrapperTest { @Resource private UserMapper userMapper; @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); // 和我们刚才学习的map对比一下 } @Test void test2(){ // 查询名字狂神说 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name","狂神说"); User user = userMapper.selectOne(wrapper); // 查询一个数据用selectOne,出现多个结果的话使用List 或者 Map System.out.println(user); } @Test void test3(){ // 查询年龄在 20 ~ 30 岁之间的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age",20,30); // 区间 Integer count = userMapper.selectCount(wrapper);// 查询结果数 System.out.println(count); } // 模糊查询 @Test void test4(){ // 查询年龄在 20 ~ 30 岁之间的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .notLike("name","e") //name 中不含 e .like("email","q") // %q% 邮箱中含 q //likeRight: 左和右说的是%的位置 10% .likeRight("age", "10"); //10%,age以10开头的 List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); } // 模糊查询 @Test void test5(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); // id 在子查询中查出来 wrapper.inSql("id","select id from user where id <= 3"); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); } //测试六:排序 @Test void test6(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); // 通过id进行排序 asc升序 desc降序 wrapper.orderByAsc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }}
6. 代码自动生成
dao、pojo、service、controller都给我自己去编写完成!
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。只需要改实体类名字和包名还有数据库配置即可。
测试:
test 目录下新建一个类
package com.kuang;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 KuangCode { public static void main(String[] args) { // 需要构建一个 代码自动生成器 对象 AutoGenerator mpg = new AutoGenerator(); // 配置策略 // 1、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir");//user.dir获取当前工程的根目录 gc.setOutputDir(projectPath + "/src/main/java");//输出目录 gc.setAuthor("狂神说"); gc.setOpen(false);//是否打开资源管理器 gc.setFileOverride(false);//是否覆盖 // 默认的service接口都是I开头 gc.setServiceName("%sService"); //去掉I gc.setIdType(IdType.ID_WORKER);//主键策略,默认即可 gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); mpg.setGlobalConfig(gc); //2、设置数据源(这里指定需要绑定的数据库) DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus" />