MyBatis-plus

本篇基于MP 3.3.0 版本 ,spring boot 方式。

MyBatis-plus 简介:

  MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

  特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

  支持数据库

  • mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver

  • 达梦数据库 、 虚谷数据库 、 人大金仓数据库

  快速开始

  1. 建立数据库,导入数据
  2. 导入对应的依赖
  3. 编写项目
    1. 配置数据库连接
    2. 编写实体类
    3. 编写Mapper接口继承BaseMapper<T> 即可!至此所有CRUD都已完成
  4. 测试使用

  详细看官网:https://mybatis.plus

插入策略

  • 用户ID自动回填
  • 主键生成策略
    • 自增
    • 手动输入
    • UUID
    • 雪花算法
    • 唯一ID生成器
    • 唯一ID生成器(字符串类型)
  • 关于id生成的一些策略请参看:https://www.cnblogs.com/haoxinyue/p/5208136.html

 1 public enum IdType {
 2    // 自增 
 3    AUTO(0),
 4    NONE(1),
 5    // 手动输入 
 6    INPUT(2), 
 7    // 唯一id生成器,默认策略
 8    ID_WORKER(3),
 9    // uuid
10    UUID(4), 
11    // 唯一id生成器(字符串类型)
12    ID_WORKER_STR(5); }

更新策略

  阿里巴巴规范中,id、gmt_create、gmr_update !

 给数据库表增加两个字段测试

  1. 自动更新策略两种级别
    1. 数据库级别(不建议)
      1. 设置默认值
      2. 插入、更新测试
    2. 代码级别(建议)
      1. 删除更新默认值
      2. 编写实体类对应注解
      3. 编写注解的处理器

  数据库级别

  代码级别

1     @TableField(fill = FieldFill.INSERT)
2     private Date insertTime; // 创建时间
3     @TableField(fill = FieldFill.INSERT_UPDATE)
4     private Date updateTime; // 更新时间
 1 package com.coding.handler;
 2 
 3 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
 4 import org.apache.ibatis.reflection.MetaObject;
 5 import org.springframework.stereotype.Component;
 6 
 7 import java.util.Date;
 8 
 9 @Component
10 public class MyMetaObjectHandler implements MetaObjectHandler {
11     /*
12     * default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
13         if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) {
14             metaObject.setValue(fieldName, fieldVal);
15         }
16         return this;
17     }*/
18     @Override
19     public void insertFill(MetaObject metaObject) {
20         this.setFieldValByName("insertTime",new Date(),metaObject);
21         this.setFieldValByName("updateTime",new Date(),metaObject);
22     }
23 
24     @Override
25     public void updateFill(MetaObject metaObject) {
26         this.setFieldValByName("updateTime",new Date(),metaObject);
27     }
28 }

乐观锁

  • 乐观锁:非常乐观,无论什么操作都不加锁!(思考:分布式环境下怎么解决冲突问题?)
  • 悲观锁:非常悲观,无论什么操作都加锁!默认情况下都是悲观锁!

解决问题

  通常方式就是增加一个乐观锁字段(version)

  当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  1. 取出记录时,获取当前version (查询)old version = 1
  2. 更新时,带上这个version (更新)
  3. 执行更新时,set version = newVersion where version = old version
    1. 如果version不对应,就更新失败
    2. 对应,就更新成功

MP对乐观锁也进行了支持

  1. 添加version注解到字段上面
  2. 添加乐观锁插件即可
  3. 测试就自动带上了版本号

  详细见:https://mp.baomidou.com/guide/optimistic-locker-plugin.html#%E4%B8%BB%E8%A6%81%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF

查询策略

  注:分页查询需要添加以下插件:

 1 //Spring boot方式
 2 @EnableTransactionManagement
 3 @Configuration
 4 @MapperScan("com.baomidou.cloud.service.*.mapper*")
 5 public class MybatisPlusConfig {
 6 
 7     @Bean
 8     public PaginationInterceptor paginationInterceptor() {
 9         PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
10         // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
11         // paginationInterceptor.setOverflow(false);
12         // 设置最大单页限制数量,默认 500 条,-1 不受限制
13         // paginationInterceptor.setLimit(500);
14         // 开启 count 的 join 优化,只针对部分 left join
15         paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
16         return paginationInterceptor;
17     }
18 }
 1     // 查询单个
 2     @Test
 3     void testSelectById(){
 4         User user = userMapper.selectById(1L);
 5         System.out.println(user);
 6     }
 7 
 8     // 查询多个
 9     @Test
10     void testSelectBatchIds(){
11         List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
12         users.forEach(System.out::println);
13     }
14 
15     // 查询数量
16     @Test
17     void testSelectCount(){
18         Integer count = userMapper.selectCount(null);
19         System.out.println(count);
20     }
21 
22     // 简单条件查询
23     @Test
24     void testSelectByMap(){
25         Map<String, Object> map = new HashMap<>();
26         map.put("name","zhangsan");
27         map.put("age",18);
28         List<User> users = userMapper.selectByMap(map);
29         users.forEach(System.out::println);
30     }
31 
32     // 分页查询
33     @Test
34     void testSelectPage(){
35         Page<User> page = new Page<>();
36         Page<User> userPage = userMapper.selectPage(page, null);
37         List<User> users = userPage.getRecords();
38         users.forEach(System.out::println);
39         System.out.println(userPage.getSize());
40         System.out.println(userPage.getTotal());
41         System.out.println(userPage.getPages());
42     }

删除策略

 1     // 删除一个
 2     @Test
 3     void testDeleteById(){
 4         userMapper.deleteById(1L);
 5     }
 6 
 7     // 删除多个
 8     @Test
 9     void testDeleteBatchIds(){
10         userMapper.deleteBatchIds(Arrays.asList(2L,6L));
11     }
12 
13     // 根据条件删除
14     @Test
15     void testDeleteByMap(){
16         HashMap<String, Object> map = new HashMap<>();
17         map.put("name","Tom");
18         userMapper.deleteByMap(map);
19     }

拓展

  • 逻辑删除:并不是真的从数据库中删掉了,只是增加了一个判断而已
    • 业务场景:管理员在后台可以看到被删除的记录,使用逻辑删除可以保证项目的安全和健壮性
  • 物理删除:直接从数据库中删除

逻辑删除使用测试

  1. 添加逻辑删除字段
  2. 在实体类添加注解
  3. 在配置文件进行配置
  4. 注册Bean

1     @TableLogic  // 逻辑删除字段注解
2     private Integer deleted;
1 # application.yml 加入配置(如果你的默认值和mp默认的一样,该配置可无)
2 mybatis-plus:
3   global-config:
4     db-config:
5       logic-delete-field: flag  #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
6       logic-delete-value: 1 # 逻辑已删除值(默认为 1)
7       logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
 1 // 注册 Bean(3.1.1开始不再需要这一步)
 2 
 3 import com.baomidou.mybatisplus.core.injector.ISqlInjector;
 4 import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.context.annotation.Configuration;
 7 
 8 @Configuration
 9 public class MyBatisPlusConfiguration {
10 
11     @Bean
12     public ISqlInjector sqlInjector() {
13         return new LogicSqlInjector();
14     }
15 }
1     // 逻辑删除,删除一个
2     @Test
3     void testLogicDeleteById(){
4         userMapper.deleteById(1L);
5     }

性能分析

  在开发的时候排除慢sql! 

  3.2.0版本以上参考:https://mp.baomidou.com/guide/p6spy.html

  1. 导入p6spy的maven依赖
  2. 在dev环境下配置文件进行配置
  3. spy.properties配置

  3.2.0版本以下参考:https://mp.baomidou.com/guide/performance-analysis-plugin.html

条件构造器

  我们平时写的sql,一般使用的最多的是一些条件,这些东西MP也帮助我们提供了

  更多条件查询见官网:https://mp.baomidou.com/guide/wrapper.html#abstractwrapper

 1 package com.coding;
 2 
 3 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 4 import com.coding.mapper.UserMapper;
 5 import com.coding.pojo.User;
 6 import org.junit.jupiter.api.Test;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.boot.test.context.SpringBootTest;
 9 
10 @SpringBootTest
11 public class WrapperTest {
12     @Autowired
13     private UserMapper userMapper;
14   // 仅举一例,更多条件查询见官网
15     @Test
16     void testConditionSelect(){
17         QueryWrapper<User> wrapper = new QueryWrapper<>();
18         wrapper.eq("name","zhangsan") // 姓名 = zhangsan
19                 .gt("age",18); // 年龄 > 18
20         userMapper.selectList(wrapper);
21     }
22 }

MP 一键生成 ——非常便捷

  导入依赖

 1         <!-- MP -->
 2         <dependency>
 3             <groupId>com.baomidou</groupId>
 4             <artifactId>mybatis-plus-boot-starter</artifactId>
 5         </dependency>
 6         <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
 7         <dependency>
 8             <groupId>org.apache.velocity</groupId>
 9             <artifactId>velocity-engine-core</artifactId>
10         </dependency>    
 1 package com.coding.utils;
 2 
 3 import com.baomidou.mybatisplus.annotation.DbType;
 4 import com.baomidou.mybatisplus.annotation.FieldFill;
 5 import com.baomidou.mybatisplus.annotation.IdType;
 6 import com.baomidou.mybatisplus.generator.AutoGenerator;
 7 import com.baomidou.mybatisplus.generator.config.*;
 8 import com.baomidou.mybatisplus.generator.config.po.TableFill;
 9 import com.baomidou.mybatisplus.generator.config.rules.DateType;
10 import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
11 
12 import java.util.ArrayList;
13 import java.util.List;
14 
15 // 遇事不决就重启,遇事不决就重启,遇事不决就重启 
16 // 自动生成代码
17 public class CodeGenerator {
18 
19     public static void main(String[] args) {
20 
21         String moduleName = "edu";
22 
23         // 1.代码生成器
24         AutoGenerator mpg = new AutoGenerator();
25 
26         // 规则的配置
27         // 全局配置
28         GlobalConfig gc = new GlobalConfig();
29         // 获取当前项目路径
30         String projectPath = System.getProperty("user.dir");
31         gc.setOutputDir(projectPath + "/coding-edu-edu/src/main/java");
32         // 全局自动添加作者
33         gc.setAuthor("coding");
34         // 配置是否开启文件夹弹出
35         gc.setOpen(false);
36         // 配置是否覆盖之前生成的文件夹
37         gc.setFileOverride(false);
38         // 配置去掉service接口命名的“I” ,例:IUserService 变成UserService
39         gc.setServiceName("%sService");
40         // 配置主键策略
41         gc.setIdType(IdType.ID_WORKER_STR);
42         // 配置日期相关的 ,日期类型
43         gc.setDateType(DateType.ONLY_DATE);
44         // 开启swagger注解自动生成
45         gc.setSwagger2(true);
46         mpg.setGlobalConfig(gc);
47 
48         // 数据源配置
49         DataSourceConfig dsc = new DataSourceConfig();
50         dsc.setDriverName("com.mysql.cj.jdbc.Driver");
51         dsc.setUrl("jdbc:mysql://localhost:3306/coding_" + moduleName + "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC");
52         dsc.setUsername("root");
53         dsc.setPassword("root");
54         dsc.setDbType(DbType.MYSQL);
55         mpg.setDataSource(dsc);
56 
57 
58         // 包配置
59         PackageConfig pc = new PackageConfig();
60         pc.setModuleName(moduleName);
61         pc.setParent("com.coding");// 父路径
62         pc.setEntity("entity");
63         pc.setController("controller");
64         pc.setService("service");
65         pc.setMapper("mapper");
66         mpg.setPackageInfo(pc);
67 
68         // 策略配置
69         StrategyConfig strategy = new StrategyConfig();
70         // 要生成哪一个表对应的类
71         strategy.setInclude(moduleName + "_\w*"); // 重要!!!
72         strategy.setTablePrefix(pc.getModuleName() + "_"); // edu_ 这个前缀不生成在类名中 例:edu_name --> Name
73         // 数据库表生成到实体类 下划线转驼峰
74         strategy.setNaming(NamingStrategy.underline_to_camel);
75         // 自动生成lombok注解
76         strategy.setEntityLombokModel(true);
77         // 配置逻辑删除字段
78         strategy.setLogicDeleteFieldName("is_deleted");
79         strategy.setEntityBooleanColumnRemoveIsPrefix(true);
80         // 自动填充
81         TableFill gmt_create = new TableFill("gmt_create", FieldFill.INSERT);
82         TableFill gmt_modified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
83         List<TableFill> list = new ArrayList<>();
84         list.add(gmt_create);
85         list.add(gmt_modified);
86         strategy.setTableFillList(list);
87         // 乐观锁
88         strategy.setVersionFieldName("version");
89         // restful api
90         strategy.setRestControllerStyle(true);
91         //
92         strategy.setControllerMappingHyphenStyle(true); // 请求示例:/user/hello_name 而不是/user/helloName
93 
94         mpg.setStrategy(strategy);
95         // 2.执行生成器
96         mpg.execute();
97     }
98 
99 }

运行后的文件结构截图

  部分源码截图

  entity

  mapper

  mapper.xml

   service 接口

  service 实现类

  controller

原文地址:https://www.cnblogs.com/ShallowPen/p/12651039.html