一、简介

官网:http://mp.baomidou.com/
参考教程:http://mp.baomidou.com/guide/
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

二、特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 支持关键词自动转义:支持数据库关键词(order、key……)自动转义,还可自定义关键词
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
  • 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击

    三、创建数据库并初始化

    1、创建数据库mybatis_plus

    1. create database `mybatis_plus`;

    2、创建User表

    1. DROP TABLE IF EXISTS user;
    2. CREATE TABLE user
    3. (
    4. id BIGINT(20) NOT NULL COMMENT '主键ID',
    5. name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    6. age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    7. email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    8. PRIMARY KEY (id)
    9. );

    3、对User表进行初始化

    1. DELETE FROM user;
    2. INSERT INTO user (id, name, age, email) VALUES
    3. (1, 'Jone', 18, 'test1@baomidou.com'),
    4. (2, 'Jack', 20, 'test2@baomidou.com'),
    5. (3, 'Tom', 28, 'test3@baomidou.com'),
    6. (4, 'Sandy', 21, 'test4@baomidou.com'),
    7. (5, 'Billie', 24, 'test5@baomidou.com');

    四、IDEA创建项目并导入相关依赖

    1、Spring Initializr快速创建springboot项目,版本2.2.1
    2、导入依赖

    1. <!--mybatis-plus-->
    2. <dependency>
    3. <groupId>com.baomidou</groupId>
    4. <artifactId>mybatis-plus-boot-starter</artifactId>
    5. <version>3.0.5</version>
    6. </dependency>
    7. <!--mysql-->
    8. <dependency>
    9. <groupId>mysql</groupId>
    10. <artifactId>mysql-connector-java</artifactId>
    11. </dependency>
    12. <!--lombok用来简化实体类-->
    13. <dependency>
    14. <groupId>org.projectlombok</groupId>
    15. <artifactId>lombok</artifactId>
    16. </dependency>

注意:引入 MyBatis-Plus 之后请不要再次引入 MyBatis 以及 MyBatis-Spring,以避免因版本差异导致的问题。
3、在IDEA中安装lombok插件
4、配置application.yml

mysql8.0以下,不需要配置时区

  1. #mysql数据库连接
  2. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
  3. spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus
  4. spring.datasource.username=root
  5. spring.datasource.password=123456

mysql8.0及以上

  1. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  2. spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
  3. spring.datasource.username=root
  4. spring.datasource.password=123456

5、编写代码
(1)主启动类中加上@MapperScan 注解,扫描 Mapper 文件夹

  1. @SpringBootApplication
  2. @MapperScan("com.guang.mapper")
  3. public class MybatisPlusApplication {
  4. ......
  5. }

(2)实体类

  1. @Data
  2. public class User {
  3. private Long id;
  4. private String name;
  5. private Integer age;
  6. private String email;
  7. }

lombok的使用参考:https://blog.csdn.net/motui/article/details/79012846
(3)mapper

  1. @Repository
  2. public interface UserMapper extends BaseMapper<User> {
  3. }

(4)单元测试查看效果

  1. @Autowired
  2. private UserMapper userMapper;
  3. @Test
  4. void contextLoads() {
  5. List<User> users = userMapper.selectList(null);
  6. for (User u : users) {
  7. System.out.println(u);
  8. }
  9. }

(5)添加mybatis-plus日志,查看sql执行情况

  1. #mybatis日志
  2. mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

五、插入操作

  1. @Test
  2. void insertUser(){
  3. User user=new User();
  4. user.setAge(12);
  5. user.setName("Lisa");
  6. user.setEmail("Lisa@qq.com");
  7. int insert = userMapper.insert(user);
  8. System.out.println("insert影响的行数:"+insert);
  9. }

mybatis-plus自动生成id策略

六、分布式id生成策略

分布式id生成策略:
参考https://www.cnblogs.com/haoxinyue/p/5208136.html

1、数据库自增长序列或字段
2、UUID
3、UUID的变种
4、Redis生成ID
5、Twitter的snowflake算法
6、利用zookeeper生成唯一ID
7、MongoDB的ObjectId
8、TiDB的主键

(1)IDWORKER默认生成19位Long为id
MyBatis-Plus默认的主键策略是:ID_WORKER
全局唯一ID_

(2)ID_WORKER_STR默认生成19为字符串类型id
(3)自增策略

  • 要想主键自增需要配置如下主键策略
    • 需要在创建数据表的时候设置主键自增
    • 实体字段中配置 @TableId(type = IdType.AUTO)
      1. @TableId(type = IdType.AUTO)
      2. private Long id;

(4)其他主键生成策略

  1. /**
  2. * 数据库ID自增
  3. */
  4. AUTO(0),
  5. /**
  6. * 该类型为未设置主键类型
  7. */
  8. NONE(1),
  9. /**
  10. * 用户输入ID
  11. * 该类型可以通过自己注册自动填充插件进行填充
  12. */
  13. INPUT(2),
  14. /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
  15. /**
  16. * 全局唯一ID (idWorker)
  17. */
  18. ID_WORKER(3),
  19. /**
  20. * 全局唯一ID (UUID)
  21. */
  22. UUID(4),
  23. /**
  24. * 字符串全局唯一ID (idWorker 的字符串表示)
  25. */
  26. ID_WORKER_STR(5);

设置全局id主键生成策略,所有的实体类主键根据该策略生成在配置文件中添加

  1. #全局设置主键生成策略
  2. mybatis-plus.global-config.db-config.id-type=auto

七、更新操作

注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?

  1. @Test
  2. public void testUpdateById(){
  3. User user = new User();
  4. user.setId(1L);
  5. user.setAge(28);
  6. int result = userMapper.updateById(user);
  7. //结果影响行数
  8. System.out.println(result);
  9. }

八、自动填充功能实现

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:

1、手动填充
在创建或者更新的时候,自行将属性set进去。

2、mybatis-plus自动填充
(1)数据库表中添加自动填充字段
在User表中添加datetime类型的新的字段 create_time、update_time
(2)在实体类该属性上添加注解
@TableField(fill = FieldFill.INSERT)、@TableField(fill = FieldFill.INSERT_UPDATE)
(3)创建MyMetaObjectHandler处理器并继承 MetaObjectHandler

  1. @Component
  2. public class MyMetaObjectHandler implements MetaObjectHandler {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(MyMetaObjectHandler.class);
  4. //添加时更新创建时间、更新时间
  5. @Override
  6. public void insertFill(MetaObject metaObject) {
  7. LOGGER.info("start insert fill ....");
  8. this.setFieldValByName("createTime", new Date(), metaObject);
  9. this.setFieldValByName("updateTime", new Date(), metaObject);
  10. }
  11. //修改时更新修改时间
  12. @Override
  13. public void updateFill(MetaObject metaObject) {
  14. LOGGER.info("start update fill ....");
  15. this.setFieldValByName("updateTime", new Date(), metaObject);
  16. }
  17. }

九、乐观锁、悲观锁

1、乐观锁
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

(1)数据库中添加version字段

  1. ALTER TABLE `user` ADD COLUMN `version` INT

(2)在实体类添加version字段并添加@version注解

  1. @Version
  2. @TableField(fill = FieldFill.INSERT)
  3. private Integer version;

(3)元对象处理器接口添加version的insert默认值

  1. @Override
  2. public void insertFill(MetaObject metaObject) {
  3. ......
  4. this.setFieldValByName("version", 1, metaObject);
  5. }

特别说明:

  • 支持的数据类型只有 int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法
  • update(entity, wrapper) 方法下, wrapper 不能复用!!!

(4)创建配置类MybatisPlusConfig并注册乐观锁bean

  1. @EnableTransactionManagement
  2. @Configuration
  3. @MapperScan("com.guang.mybatis_plus.mapper")
  4. public class MybatisPlusConfig {
  5. /**
  6. * 乐观锁插件
  7. */
  8. @Bean
  9. public OptimisticLockerInterceptor optimisticLockerInterceptor() {
  10. return new OptimisticLockerInterceptor();
  11. }
  12. }

(5)测试乐观锁成功,version更新

  1. public void testOptimisticLocker() {
  2. //查询
  3. User user = userMapper.selectById(1L);
  4. //修改数据
  5. user.setName("Helen Yao");
  6. user.setEmail("helen@qq.com");
  7. //执行更新
  8. userMapper.updateById(user);
  9. }

(6)测试乐观锁失败案例

  1. public void testOptimisticLockerFail() {
  2. //查询
  3. User user = userMapper.selectById(1L);
  4. //修改数据
  5. user.setName("Helen Yao1");
  6. user.setEmail("helen@qq.com1");
  7. //模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了version
  8. user.setVersion(user.getVersion() - 1);
  9. //执行更新
  10. userMapper.updateById(user);
  11. }

十、多种普通操作

1、根据id查询

  1. @Test
  2. public void testSelectById(){
  3. User user = userMapper.selectById(1L);
  4. System.out.println(user);
  5. }

2、多个id批量查询list集合装id

  1. @Test
  2. public void testSelectBatchIds(){
  3. List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
  4. users.forEach(System.out::println);
  5. }

3、条件查询map集合的key装字段,value装值

  1. @Test
  2. public void testSelectByMap(){
  3. HashMap<String, Object> map = new HashMap<>();
  4. map.put("name", "Helen");
  5. map.put("age", 18);
  6. List<User> users = userMapper.selectByMap(map);
  7. }

4、分页查询
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

1、删除主类上的@MapperScan扫描注解
2、创建配置类TeacherConfig添加分页插件

  1. @Configuration
  2. @MapperScan("com.guang.eduservice.mapper")
  3. public class TeacherConfig {
  4. /**
  5. * 分页插件
  6. */
  7. @Bean
  8. public PaginationInterceptor paginationInterceptor() {
  9. return new PaginationInterceptor();
  10. }
  11. }

(1)简单分页查询测试,通过page对象获取相关数据

  1. @Test
  2. public void testSelectPage() {
  3. Page<User> page = new Page<>(1,5);
  4. userMapper.selectPage(page, null);
  5. page.getRecords().forEach(System.out::println);
  6. System.out.println(page.getCurrent());
  7. System.out.println(page.getPages());
  8. System.out.println(page.getSize());
  9. System.out.println(page.getTotal());
  10. System.out.println(page.hasNext());
  11. System.out.println(page.hasPrevious());
  12. }

控制台sql语句输出:SELECT id,name,age,email,create_time,update_time FROM user LIMIT 0,5

(2)条件加分页查询测试

  1. @Test
  2. public void testSelectPageCondition() {
  3. Page<EduTeacher> page = new Page<>(current, limit);
  4. if (null == page) return R.ERROE();
  5. QueryWrapper wrapper = new QueryWrapper();
  6. String name = teacherVo.getName();
  7. Integer level = teacherVo.getLevel();
  8. String begin = teacherVo.getBegin();
  9. String end = teacherVo.getEnd();
  10. if (null != name) {
  11. wrapper.like("name", name);
  12. }
  13. if (null != level) {
  14. wrapper.eq("level", level);
  15. }
  16. if (null != begin) {
  17. wrapper.ge("gmt_create", begin);
  18. }
  19. if (null != end) {
  20. wrapper.ge("gmt_create", end);
  21. }
  22. teacherService.page(page, wrapper);
  23. long summary = page.getTotal();
  24. List<EduTeacher> list = page.getRecords();
  25. HashMap<String, Object> resultmap = new HashMap<>();
  26. resultmap.put("total", summary);
  27. resultmap.put("item", list);
  28. return R.OK().setData(resultmap);
  29. }

十一、实现删除功能

1、物理删除

  1. @Test
  2. public void testDeleteById(){
  3. int result = userMapper.deleteById(8L);
  4. System.out.println(result);
  5. }

2、批量删除List集合装id

  1. @Test
  2. public void testDeleteBatchIds() {
  3. int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
  4. System.out.println(result);
  5. }

3、按条件查询删除map集合key装字段value装值

  1. @Test
  2. public void testDeleteByMap() {
  3. HashMap<String, Object> map = new HashMap<>();
  4. map.put("name", "Helen");
  5. map.put("age", 18);
  6. int result = userMapper.deleteByMap(map);
  7. System.out.println(result);
  8. }

4、逻辑删除

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

(1)数据库中添加 deleted字段

  1. ALTER TABLE `user` ADD COLUMN `deleted` boolean

(2)实体类添加deleted 字段
并加上 @TableLogic 注解 和 @TableField(fill = FieldFill.INSERT) 注解

  1. @TableLogic
  2. @TableField(fill = FieldFill.INSERT)
  3. private Integer deleted;

(3)元对象处理器接口添加deleted的insert默认值

  1. @Override
  2. public void insertFill(MetaObject metaObject) {
  3. ......
  4. this.setFieldValByName("deleted", 0, metaObject);
  5. }

(4)application.properties 加入配置

  1. mybatis-plus.global-config.db-config.logic-delete-value=1
  2. mybatis-plus.global-config.db-config.logic-not-delete-value=0

(5)在 MybatisPlusConfig 中注册 Bean

  1. @Bean
  2. public ISqlInjector sqlInjector() {
  3. return new LogicSqlInjector();
  4. }

(6)测试逻辑删除功能
1、删除数据库某条记录
2、该条记录的deleted字段值被修改为1
3、再次查询数据库记录时,该条记录将会被过滤
控制台查询sql语句:SELECT id,name,age,email,create_time,update_time,deleted FROM user WHERE deleted=0

十二、性能分析插件

性能分析拦截器,用于输出每条 SQL 语句及其执行时间
SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题

1、配置插件
(1)参数说明
参数:maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。
参数:format: SQL是否格式化,默认false。
(2)在 MybatisPlusConfig 中配置

  1. /**
  2. * SQL 执行性能分析插件
  3. * 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
  4. */
  5. @Bean
  6. @Profile({"dev","test"})// 设置 dev test 环境开启
  7. public PerformanceInterceptor performanceInterceptor() {
  8. PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
  9. performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
  10. performanceInterceptor.setFormat(true);
  11. return performanceInterceptor;
  12. }

(3)Spring Boot 中设置dev环境

  1. #环境设置:devtestprod
  2. spring.profiles.active=dev

可以针对各环境新建不同的配置文件application-dev.propertiesapplication-test.propertiesapplication-prod.properties

十三、mp实现其他查询

1、delete
2、selectOne
3、selectCount
4、selectList
5、selectMaps
6、selectObjs
7、update