SpringBoot快速集成Mybatis-plus常用功能

Mybatis-plus 常用功能集成

1、基本使用

  • 添加maven依賴

    • mybatis-plus 基礎依賴

      <dependency>
              <groupId>com.baomidou</groupId>
              <artifactId>mybatis-plus-boot-starter</artifactId>
              <version>3.1.0</version>
      </dependency>
      
    • 添加 代碼生成器 依賴

      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-generator</artifactId>
          <version>3.1.0</version>
      </dependency>
      
    • 添加 模板引擎 依賴 (如果需要自定義代碼生成模板), MyBatis-Plus 支持 Velocity(默認)使用其他自定義模板引擎需要額外配置

      <dependency>
          <groupId>org.apache.velocity</groupId>
          <artifactId>velocity-engine-core</artifactId>
          <version>2.2</version>
      </dependency>
      
  • application.yml配置文件中添加基礎配置

    # 基礎 DataSource Config
    spring:
      datasource:
        druid:
          url: jdbc:mysql://localhost:3306/scaffold?useUnicode=true&characterEncoding=utf8&useSSL=false
          username: mysql
          password: mysql
        driver-class-name: com.mysql.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
    
    # 更多配置
    # ====================MybatisPlus Config====================
    mybatis-plus:
      # 如果是放在src/main/java目錄下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
      # 如果是放在resource目錄 classpath*:/mapper/*Mapper.xml
      mapper-locations: classpath:/com/jasper/scaffold/**/mapper/xml/*Mapper.xml
      #實體掃描,多個package用逗號或者分號分隔
      typeAliasesPackage: com.jasper.scaffolds.*.entity
      global-config:
        key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
        #主鍵類型  0:"數據庫ID自增", 1:"用戶輸入ID",2:"全局唯一ID (數字類型唯一ID)", 3:"全局唯一ID UUID";
        id-type: 2
        #字段策略 0:"忽略判斷",1:"非 NULL 判斷"),2:"非空判斷"
        field-strategy: 2
        #駝峯下劃線轉換
        db-column-underline: true
        db-config:
          #mp2.3+ 全局表前綴 mp_
          #table-prefix: mp_
          #刷新mapper 調試神器
          #refresh-mapper: true
          #數據庫大寫下劃線轉換
          #capital-mode: true
          # 邏輯刪除配置
          logic-delete-field: mark # 全局邏輯刪除字段 實體類上有 @TableLogic 則以實體上的爲準,忽略全局
          logic-delete-value: 1
          logic-not-delete-value: 0
    
        #自定義填充策略接口實現
        #meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
      configuration:
        #配置返回數據庫(column下劃線命名&&返回java實體是駝峯命名),自動匹配無需as(沒開啓這個,SQL需要寫as: select user_id as userId)
        map-underscore-to-camel-case: true
        cache-enabled: false
        #配置JdbcTypeForNull, oracle數據庫必須配置
        jdbc-type-for-null: 'null'
    #===================================================================================
    
  • 啓動類中添加 @MapperScan 註解,掃描 Mapper 文件夾

    @SpringBootApplication
    @MapperScan("com.jasper.scaffold.*.mapper")
    public class ScaffoldApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ScaffoldApplication.class, args);
    	}
    
    }
    
    
  • 測試數據庫 scaffold Table: User

    -- 數據庫: scaffold
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user` (
      `id` bigint(20) NOT NULL COMMENT '主鍵ID',
      `name` varchar(30) DEFAULT NULL COMMENT '姓名',
      `age` int(11) DEFAULT NULL COMMENT '年齡',
      `email` varchar(50) DEFAULT NULL COMMENT '郵箱',
      `cTime` datetime DEFAULT NULL COMMENT '創建時間',
      `uTime` datetime DEFAULT NULL COMMENT '修改時間',
      `mark` tinyint(1) DEFAULT NULL COMMENT '數據有效標示位 1無效 0有效',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶表實體';
    

2、代碼生成器

  • 通過MyBatis-Plus 的代碼生成器AutoGenerator,通過數據庫表 直接快速生成tity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼

  • 單獨創建MpGenerator生成類,執行 main 方法生成指定 table 對應的模塊代碼

  • 更多屬性及配置請查看:更多配置

    // 自定義代碼生成類 demo
    public class MpGenerator {
        public static void main(String[] args) {
            //fixme 生成的java文件路徑
            String javaPath = "/xxx/git-hub/Jasper/scaffold/src/main/java";
            //fixme 包名
            String packgeName = "com.jasper.scaffold.api";
    
            //fixme mysql數據庫配置
            String dbusername = "mysql";
            String dbpassword = "mysql";
            String dbip = "localhost";
            String dbname = "scaffold";
    
            // 各種配置
            AutoGenerator mpg = new AutoGenerator();
            // 設置數據源
            mpg.setDataSource(new DataSourceConfig()
                    .setDriverName("com.mysql.jdbc.Driver")
                    // 設置數據庫類型
                    .setDbType(DbType.MYSQL)
                    .setUsername(dbusername)
                    .setPassword(dbpassword)
                    .setUrl(String.format("jdbc:mysql://%s:3306/%s?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull", dbip, dbname))
            );
    
            // 全局配置
            mpg.setGlobalConfig(new GlobalConfig()
                    // 輸出目錄
                    .setOutputDir(javaPath)
                    // 是否覆蓋
                    .setFileOverride(true)
                    // 開啓AR模式
                    .setActiveRecord(true)
                    // XML二級緩存
                    .setEnableCache(false)
                    // 生成ResultMap
                    .setBaseResultMap(true)
                    // 生成 sql片段
                    .setBaseColumnList(true)
                    // 自動打開生成後的文件夾
                    .setOpen(true)
                    // 所有文件的生成者
                    .setAuthor("codegenerator")
                    .setDateType(DateType.ONLY_DATE)
                    // 自定義文件命名,%s會自動填充表實體類名字
                    .setMapperName("%sMapper")
                    .setXmlName("%sMapper")
                    .setServiceName("I%sService")
                    .setServiceImplName("%sServiceImpl")
                    .setControllerName("%sController")
            );
    
    
            // 策略配置
            mpg.setStrategy(new StrategyConfig()
                    // fixme 添加需要生成的表
                    .setInclude("user")
                    // 實體類使用Lombok
                    .setEntityLombokModel(true)
                    // 表名生成策略,下劃線轉駝峯
                    .setNaming(NamingStrategy.underline_to_camel)
                    .setRestControllerStyle(true)
                    .setSuperControllerClass("com.jasper.scaffold.common.mvc.BaseController")
                    .setLogicDeleteFieldName("mark")
            );
    
            // 包配置
            mpg.setPackageInfo(new PackageConfig()
                    // 基本包路徑
                    .setParent(packgeName)
                    // 設置Controller包名
                    .setController("web")
                    // 設置entity包名
                    .setEntity("entity")
                    // 設置Mapper包名
                    .setMapper("mapper")
                    // 設置Service包名
                    .setService("service")
                    // 設置Service實現類包名
                    .setServiceImpl("service.impl")
                    // 設置Mapper.xml包名
                    .setXml("mapper.xml")
            );
    
            // 如果需要定製 代碼生成模板 可以 自己寫代碼模板 如: 自定義 VO模板
            List<FileOutConfig> list = new ArrayList<>();
            list.add(new FileOutConfig("/templates/codegenerator/vo.java.vm") {
                // 自定義vo輸出路徑
                @Override
                public String outputFile(TableInfo tableInfo) {
                    String voPath = javaPath.concat("/").concat(packgeName.replaceAll("[.]", "/"))
                            .concat("/").concat("vo").concat("/")
                            .concat(tableInfo.getEntityName()).concat("VO.java");
                    log.info("voPath:{}", voPath);
                    return voPath;
    
                }
            });
    
    //        list.add(new FileOutConfig("/templates/mapper.xml.vm") {
    //            // 自定義Mapper.xml輸出路徑
    //            @Override
    //            public String outputFile(TableInfo tableInfo) {
    //                return "/Users/liqingchao/work/xingyun_pmp/project_be/project_be/proj-server/src/main/java/com/jd/jacp/proj/project/mapper/xml/" + tableInfo.getEntityName() + "Mapper.xml";
    //            }
    //        });
    
            // 注入自定義配置
            mpg.setCfg(new InjectionConfig() {
                @Override
                public void initMap() {
                    // 注入自定義 Map 對象(注意需要setMap放進去)
                    Map<String, Object> map = new HashMap<>(1);
                    map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                    this.getConfig().getPackageInfo().put("Vo", mpg.getPackageInfo().getParent() + ".vo");
                    this.setMap(map);
                }
            }.setFileOutConfigList(list));
    
            mpg.execute();
        }
    }
    

3、CRUD接口

  • Mapper層 CRUD接口簡單示例: 更多接口列表詳情

    • 通用 CRUD 封裝BaseMapper接口,爲 Mybatis-Plus 啓動時自動解析實體表關係映射轉換爲 Mybatis 內部對象注入容器
    • 泛型 T 爲任意實體對象
    • 參數 Serializable 爲任意類型主鍵 Mybatis-Plus 不推薦使用複合主鍵約定每一張表都有自己的唯一 id 主鍵
    • 對象 Wrapper條件構造器
    • 方法名均爲這四種關鍵字:Insert Delete Update Select
    // 插入一條記錄
    int insert(T entity);
    // 根據 entity 條件,刪除記錄
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
    // 根據 ID 修改
    int updateById(@Param(Constants.ENTITY) T entity);
    // 根據 ID 查詢
    T selectById(Serializable id);
    // 根據 entity 條件,查詢一條記錄
    T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
    
  • Service層 CRUD接口簡單示例:

    說明:

    • 通用 Service CRUD 封裝IService接口,進一步封裝 CRUD 採用 get 查詢單行 remove 刪除 list 查詢集合 page 分頁 前綴命名方式區分 Mapper 層避免混淆,
    • 泛型 T 爲任意實體對象
    • 建議如果存在自定義通用 Service 方法的可能,請創建自己的 IBaseService 繼承 Mybatis-Plus 提供的基類
    • 對象 Wrapper條件構造器
    • 基礎接口列表詳情參見:

      基礎接口列表詳情

    • 鏈式查詢 測試

      // 鏈式查詢 普通
      QueryChainWrapper<T> query();
      // 鏈式查詢 lambda 式。注意:不支持 Kotlin
      LambdaQueryChainWrapper<T> lambdaQuery(); 
      
      @Test
      void testChainQuery() {
          // 鏈式查詢 普通
          User user = userService.query().eq("name", "jasper").one();
          // 鏈式查詢 lambda 式
          User user2 = userService.lambdaQuery().eq(User::getName, "jasper").one();
          System.out.println(user);
          System.out.println(user2);
      }
      
    • 鏈式更新 測試

      // 鏈式更改 普通
      UpdateChainWrapper<T> update();
      // 鏈式更改 lambda 式。注意:不支持 Kotlin 
      LambdaUpdateChainWrapper<T> lambdaUpdate();
      
      @Test
      void testChainUpdate() {
          // 鏈式更改 普通
          String queryVal = "zhangsan";
          userService.update().eq("name", queryVal).remove();
          // 鏈式更改 lambda 式
          User updateEntity = new User().setEmail("[email protected]");
          userService.lambdaUpdate().eq(User::getName, "lisi").update(updateEntity);
      }
      

4、分頁插件

  • 在配置類中 開啓分頁插件, 支持額外的分頁配置,如:最大單頁數量限制

    @Configuration
    @EnableTransactionManagement
    public class MybatisPlusConfig {
    
        /**
         * 分頁插件
         * @return
         */
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            return new PaginationInterceptor();
        }
    }
    
  • 測試分頁查詢

    @Test
    void testPagination() {
        Page<User> page = new Page<>(1, 5);
        page.setDesc("age");
        IPage<User> userPage = userMapper.selectPage(page, Wrappers.<User>query().gt("age", 5));
        log.error("總條數 -------------> {}", userPage.getTotal());
        log.error("當前頁數 -------------> {}", userPage.getCurrent());
        log.error("當前每頁顯示數 -------------> {}", userPage.getSize());
        List<User> records = userPage.getRecords();
        log.error("records: {}", records);
    }
    
  • 測試lambda分頁查詢

    @Test
    void lambdaPagination() {
        Page<User> page = new Page<>(1, 3);
        IPage<User> result = userMapper.selectPage(page, Wrappers.<User>lambdaQuery().ge(User::getAge, 1).orderByAsc(User::getAge));
        log.error("總條數 -------------> {}", result.getTotal());
        log.error("當前頁數 -------------> {}", result.getCurrent());
        log.error("當前每頁顯示數 -------------> {}", result.getSize());
        List<User> records = result.getRecords();
        log.error("records: {}", records);
    }
    

5、邏輯刪除

mybatis-plus 支持數據的邏輯刪除,當數據庫記錄設計是通過 某個字段來標示 數據是否有效或者是否已刪除,而不是真正的物理刪除時, 可以通過配置,將mybatis-plus默認的刪除變成邏輯刪除;並且查詢方法也會默認帶上邏輯刪除標示,即只查詢 未刪除的數據 (自己擴展的xml不會),當有需求 就是需要查詢邏輯刪除的數據時,可手寫查詢 (既然設計了邏輯刪除,一般不會查詢已刪除的數據)

  • application.yml加入配置

    mybatis-plus:
      global-config:
        db-config:
          logic-delete-field: flag  #全局邏輯刪除字段值3.3.0開始支持 flag可以修改爲自己的 邏輯標示字段
          logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
          logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
    
  • 實體類字段上加上@TableLogic註解

    /**
    * 數據有效標示位 0有效 1無效
    */
    @TableLogic
    private Boolean mark;
    

    Tip:

    • 字段支持所有數據類型(推薦使用 Integer,Boolean,LocalDateTime)
    • 如果使用LocalDateTime,建議邏輯未刪除值設置爲字符串null,邏輯刪除值只支持數據庫函數例如now()
    • 使用mp自帶方法刪除和查找都會附帶邏輯刪除功能 (自己寫的xml不會)
    • 如果實體類上有 @TableLogic 則以實體上的爲準,忽略全局。 即先查找註解再查找全局,都沒有則此表沒有邏輯刪除

6、通用枚舉

在一些情況下,我們希望實體的字段是固定的幾個值,比如:User用戶表性別(Gender),我們希望是固定的Male,Female 我們可能在設計數據庫字段時,採用了int(1):

  • 1: 男
  • 2: 女

如果實體使用了Integer,那麼無法保證新增時傳入的參數是1/2;此種場景就可以用通用枚舉來處理

  • application.yml中添加 配置枚舉類掃描路徑

    mybatis-plus:
      # 支持統配符 * 或者 ; 分割
      type-enums-package: com.jasper.scaffold.api.entity.enums
      ...
    
  • 先定義一個性別的枚舉類

    @Getter
    public enum GenderEnum {
    
        MALE(1, "男"),
        FEMALE(2, "女");
    
        private final int code;  // 數據庫中 存儲的爲 code
        private final String descp;
    
        GenderEnum(int code, String descp){
            this.code = code;
            this.descp = descp;
        }
    }
    
  • 定義用戶實體時,使用枚舉類型定義 性別

       /**
         * 性別
         */
        private GenderEnum gender;
    // 省略其他。。
    
  • 添加枚舉配置

    • 方式1: 使用 @EnumValue 註解枚舉屬性

      @EnumValue  //標記數據庫存的值是code
      private final int code;  
      
    • 方式2:枚舉屬性,實現 IEnum 接口

      public enum GenderEnum implements IEnum<Integer>{
      
          MALE(1, "男"),
          FEMALE(2, "女");
      
          private final int code;  // 數據庫中 存儲的爲 code
          private final String descp;
      	
          @Override
          public Integer getValue() {
              return this.code;
          }
          
          GenderEnum(int code, String descp){
              this.code = code;
              this.descp = descp;
          }
      }
      
  • 測試枚舉使用

    @Test
    void testEnum() {
        User user = new User()
            .setGender(GenderEnum.MALE)
            .setAge(12)
            .setEmail("[email protected]")
            .setName("Name_y")
            .setMark(false); // false 未刪除
        // 插入
        userMapper.insert(user);
    	// 查詢
        List<User> userList = userService.lambdaQuery().eq(User::getGender, GenderEnum.MALE).list();
        log.error("用戶數:{}", userList.size());
    }
    // 用戶數:2
    

7、自動填充功能

​ 當我們在插入、修改數據庫記錄時,想要填充某個字段的值時:比如 User表的 cTimeuTime字段,當新增數據時,自動設置 cTimeuTime爲當前時間,當更新修改數據時,自動修改uTime爲當前時間,這個時候就可以使用自動填充功能

  • 實現元對象處理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler

    @Slf4j
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            log.debug("start insert fill ....");
            // 起始版本 3.3.0(推薦使用)
            // this.strictInsertFill(metaObject, "cTime", Date.class, new Date());
            this.setFieldValByName("cTime", new Date(), metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            log.debug("start update fill ....");
            // 起始版本 3.3.0(推薦使用)
            // this.strictUpdateFill(metaObject, "uTime", Date.class, new Date());
            this.setFieldValByName("uTime", new Date(), metaObject);
        }
    }
    
  • 註解填充字段 @TableField(.. fill = FieldFill.INSERT)

        /**
         * 創建時間
         */
        @TableField(value = "cTime", fill = FieldFill.INSERT)
        private Date cTime;
    
        /**
         * 修改時間
         */
        @TableField(value = "uTime", fill = FieldFill.UPDATE)
        private Date uTime;
    

    Tip:

    • 字段必須聲明TableField註解,屬性fill選擇對應策略,該聲明告知Mybatis-Plus需要預留注入SQL字段

    • 填充處理器MyMetaObjectHandler在 Spring Boot 中需要聲明@Component@Bean注入

    • 要想根據註解FieldFill.xxx字段名以及字段類型來區分必須使用父類的strictInsertFill或者strictUpdateFill方法

    • 不需要根據任何來區分可以使用父類的fillStrategy方法

    • 字段類型是 LocalDateTime時, 集成druid數據源,使用3.1.0之前版本沒問題,升級mp到3.1.1+後,運行時報錯:java.sql.SQLFeatureNotSupportedException

8、SQL性能分析

​ 可以通過自帶的SQL性能分析插件,自動打印出執行的SQL以及執行時間,方便排查問題以及優化性能較慢的SQL,可以設置Profile只在 dev、test環境下啓動

  • MybatisPlusConfig啓動SQL性能分析插件

    /**
    * SQL 性能分析插件
    * @return
    */
    @Bean
    @Profile({"dev", "test"})
    public PerformanceInterceptor performanceInterceptor() {
        return new PerformanceInterceptor();
    }
    
  • 自動分析執行SQL

    Time:39 ms - ID:com.jasper.scaffold.api.mapper.UserMapper.selectList
    Execute SQL:SELECT id,name,gender,age,email,cTime,uTime,mark FROM user WHERE gender = 1
    

9、多數據源

dynamic-datasource-spring-boot-starter 是一個基於springboot的快速集成多數據源的啓動器,可實現快速切換多個數據源: 詳情

  • 引入Maven包

    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
      <version>${version}</version>
    </dependency>
    
  • 配置多個數據源

    spring:
      datasource:
        dynamic:
          primary: master #設置默認的數據源或者數據源組,默認值即爲master
          strict: false #設置嚴格模式,默認false不啓動. 啓動後在未匹配到指定數據源時候回拋出異常,不啓動會使用默認數據源.
          datasource:
            master:
              url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
            slave_1:
              url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
            slave_2:
              url: ENC(xxxxx) # 內置加密,使用請查看詳細文檔
              username: ENC(xxxxx)
              password: ENC(xxxxx)
              driver-class-name: com.mysql.jdbc.Driver
              schema: db/schema.sql # 配置則生效,自動初始化表結構
              data: db/data.sql # 配置則生效,自動初始化數據
              continue-on-error: true # 默認true,初始化失敗是否繼續
              separator: ";" # sql默認分號分隔符
              
           #......省略
           #以上會配置一個默認庫master,一個組slave下有兩個子庫slave_1,slave_2
    
  • 使用 @DS 切換數據源

    @DS 可以註解在方法上和類上,同時存在方法註解優先於類上註解。建議只註解在service實現上

  • 示例

    @Service
    @DS("slave")
    public class UserServiceImpl implements UserService {
    
      @Autowired
      private JdbcTemplate jdbcTemplate;
    
      public List<Map<String, Object>> selectAll() {
        return  jdbcTemplate.queryForList("select * from user");
      }
      
      @Override
      @DS("slave_1")
      public List<Map<String, Object>> selectByCondition() {
        return  jdbcTemplate.queryForList("select * from user where age >10");
      }
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章