• mybatis-plus详细使用教程


    mybatis-plus使用教程

    欢迎关注博主公众号「Java大师」, 专注于分享Java领域干货文章http://www.javaman.cn/jszw/mybatis-plus

    什么是Mybatis-Plus

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

    mybatis-plus

    特性

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

    快速入门

    步骤

    1、创建数据库和数据库表

    (来自官网的例子)

    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)
    );
    

    2、插入表数据

    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工程,引入mubatis-plus依赖

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.1.0</version>
    </dependency>
    

    4、application.yml配置mysql数据源

    # DataSource Config
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost/mp_plus?serverTimezone=GMT%2B8&characterEncoding=UTF-8&allowMultiQueries=true
        username: root
        password: 123456
    

    5、使用mybatis-plus

    • entity

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          @TableId(type = IdType.AUTO) //采用数据库自增
          private long id;
          private String name;
          private int age;
          private String email;
      }
      
    • mapper接口

    // 在对应的Mapper上面继承基本的类 BaseMapper
    @Repository
    public interface UserMapper extends BaseMapper<User> {
    	// 所有的CRUD操作都已经编写完成了
    }
    
    • springboot测试启动类增加@MapperScan扫描注解

      @MapperScan("com.mpstudy.mp.mapper")
      
    • 测试

    @MapperScan("com.mpstudy.mp.mapper")
    @SpringBootTest
    class MpApplicationTests {
        
        // 继承了BaseMapper,所有的方法都来自己父类
        // 我们也可以编写自己的扩展方法!
        @Autowired
        UserMapper userMapper;
    
        @Test
        void contextLoads() {
            List<User> users = userMapper.selectList(null); //条件构造器先不用
            users.forEach(System.out::println);
        }
    }
    
    • 测试结果
    Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75023c53]
    User(id=1, name=Jone, age=18, email=test1@baomidou.com, createTime=null, updateTime=null)
    User(id=2, name=Jack, age=20, email=test2@baomidou.com, createTime=null, updateTime=null)
    User(id=3, name=Tom, age=28, email=test3@baomidou.com, createTime=null, updateTime=null)
    User(id=4, name=Sandy, age=21, email=test4@baomidou.com, createTime=null, updateTime=null)
    User(id=5, name=Billie, age=24, email=test5@baomidou.com, createTime=null, updateTime=null)
    

    日志配置

    将我们执行的sql打印出来,方便调试

    1、在application.yml中新增配置

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl //标准输出
    

    2、日志配置完成后,在控制台就会生成相应的日志

    Creating a new SqlSession
    SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@75023c53] was not registered for synchronization because synchronization is not active
    JDBC Connection [HikariProxyConnection@688197093 wrapping com.mysql.jdbc.JDBC4Connection@3610f277] will not be managed by Spring
    ==>  Preparing: SELECT id,name,age,email,create_time,update_time FROM user 
    ==> Parameters: 
    <==    Columns: id, name, age, email, create_time, update_time
    <==        Row: 1, Jone, 18, test1@baomidou.com, null, null
    <==        Row: 2, Jack, 20, test2@baomidou.com, null, null
    <==        Row: 3, Tom, 28, test3@baomidou.com, null, null
    <==        Row: 4, Sandy, 21, test4@baomidou.com, null, null
    <==        Row: 5, Billie, 24, test5@baomidou.com, null, null
    

    CRUD详解

    1、插入操作

    @Test
    void insert(){
        User user = new User();
        user.setName("java大师1");
        user.setAge(11);
        user.setEmail("111000@qq.com");
        int insert = userMapper.insert(user);
    }
    

    上面的的例子中没有插入用户id,在User类中已经设置了自动,也就是跟着数据库的配置走(数据库中已经设置了自增)

    //mybatis-plus支持的主键策略
    public enum IdType {
        AUTO(0), // 数据库id自增
        NONE(1), // 未设置主键
        INPUT(2), // 手动输入
        ID_WORKER(3), // 默认的全局唯一id
        UUID(4), // 全局唯一id uuid
        ID_WORKER_STR(5);  //ID_WORKER 字符串表示法
    

    2、更新操作

    @Test
    void update(){
        User user = new User();
        user.setId(6);
        user.setAge(38);
        int i = userMapper.updateById(user); //传入的是个对象
        System.out.println(i);
    }
    

    3、时间戳自动填充

    在实际开发中,我们希望所有的创建时间(create_time)和更新时间(update_time)都是自动完成,不需要人为去修改和维护

    实现方法有如下两种:

    方法一:数据库级别(数据库增加default字段)

    mybatis-plus

    方法二:代码级别

    • 编写时间戳自动填充注解

      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
      public class MyMetaObjectHandler implements MetaObjectHandler {
          @Override
          public void insertFill(MetaObject metaObject) {
              log.info("开始填充");
              this.setFieldValByName("createTime",new Date(),metaObject);
              this.setFieldValByName("updateTime",new Date(),metaObject);
          }
      
          @Override
          public void updateFill(MetaObject metaObject) {
              log.info("更新填充");
              this.setFieldValByName("updateTime",new Date(),metaObject);
          }
      }
      
    • 在实体类的字段上增加TableField注解

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
      
          @TableId(type = IdType.AUTO)
          private long id;
          private String name;
          private int age;
          private String email;
      
          //创建时间注解
          @TableField(fill = FieldFill.INSERT)
          private Date createTime;
      	//修改时间注解
          @TableField(fill = FieldFill.INSERT_UPDATE)
          private Date updateTime;
      }
      
    • 测试

      @Test
      void insert(){
          User user = new User();
          user.setName("公众号:java大师");
          user.setAge(2);
          user.setEmail("javadashi@qq.com");
          int insert = userMapper.insert(user);
      }
      

    4、删除操作

    // 测试删除
    @Test
    public void testDeleteById(){
    	userMapper.deleteById(1);
    } 
    
    // 通过id批量删除
    @Test
    public void testDeleteBatchId(){
    	userMapper.deleteBatchIds(Arrays.asList(2,3,4));
    } 
    
    // 通过map删除
    @Test
    public void testDeleteMap(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","java大师");
    userMapper.deleteByMap(map);
    

    5、逻辑删除

    相对于物理删除来说,我们需要对数据进行留档,所以需要对删除的数据也要保留,这就要用到逻辑删除

    物理删除 :从数据库中直接移除
    逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! sfyx = 0 => sfyx = 1

    • 在数据库表中增加sfyx(是否有效)字段

      mybatis-plus

    • 实体类增加属性

      @TableLogic //逻辑删除
      private Integer sfyx;
      
    • 配置逻辑删除组件并被springboot所管理

      // 逻辑删除组件!
      @Bean
      public ISqlInjector sqlInjector() {
      	return new LogicSqlInjector();
      }
      
    • 修改application.yml增加配置

      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        global-config:
          db-config:
            logic-delete-value: 0  //已删除
            logic-not-delete-value: 1  //未删除
      
    • 测试(看到虽然执行的的delete方法,但是实际的sql是update语句)

      @Test
          void testDelete(){
              int i = userMapper.deleteById(2);
          }
      
      Creating a new SqlSession
      SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@456bcb74] was not registered for synchronization because synchronization is not active
      JDBC Connection [HikariProxyConnection@66747889 wrapping com.mysql.jdbc.JDBC4Connection@4dd90166] will not be managed by Spring
      ==>  Preparing: UPDATE user SET sfyx=0 WHERE id=? AND sfyx=1 
      ==> Parameters: 2(Integer)
      <==    Updates: 1
      

    6、分页查询

    • 配置分页拦截器

      @Bean
      public PaginationInterceptor paginationInterceptor(){
          return new PaginationInterceptor();
      }
      
    • 使用Page对象

      @Test
      void testPage(){
          Page<User> page = new Page<>(1,3);
          userMapper.selectPage(page, null);
          page.getRecords().forEach(System.out::println);
          System.out.println(page.getTotal());
      }
      

    7、多表查询

    • 创建VO对象

      import com.mpstudy.mp.entity.User;
      import lombok.Data;
      
      @Data
      public class UserClassVo extends User {
          private String className;
      }
      
    • UserMapper中增加getAllUsers方法,通过select注解编写sql

    @Repository
    public interface UserMapper extends BaseMapper<User> {
        @Select("select a.*,b.name as class_name from user a,class b,user_class c where a.id=c.user_id and b.id=c.class_id ")
        List<UserClassVo> getAllUsers();
    }
    
    • 测试

      @Test
      void testGetAllUsers(){
          List<UserClassVo> allUsers = userMapper.getAllUsers();
          allUsers.forEach(System.out::println);
      }
      
    Creating a new SqlSession
    SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@47f04e4d] was not registered for synchronization because synchronization is not active
    JDBC Connection [HikariProxyConnection@843410864 wrapping com.mysql.jdbc.JDBC4Connection@5f5827d0] will not be managed by Spring
    ==>  Preparing: select a.*,b.name as class_name from user a,class b,user_class c where a.id=c.user_id and b.id=c.class_id 
    ==> Parameters: 
    <==    Columns: id, name, age, email, create_time, update_time, sfyx, class_name
    <==        Row: 2, Jack, 20, test2@baomidou.com, null, 2021-09-20 18:05:06.0, 0, 小二班
    <==        Row: 3, Tom, 28, test3@baomidou.com, null, 2021-09-20 18:04:27.0, 1, 大一班
    

    8、多表分页查询

    • 创建VO对象

      import com.mpstudy.mp.entity.User;
      import lombok.Data;
      
      @Data
      public class UserClassVo extends User {
          private String className;
      }
      
    • UserMapper中增加getUsersByPage方法,通过select注解编写sql

      @Repository
      public interface UserMapper extends BaseMapper<User> {
          //传入IPage对象和QueryWrapper条件构造器
          //sql通过${ew.customSqlSegment}去解析构造器内容
          @Select("select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id " + "${ew.customSqlSegment} ")
          IPage<UserClassVo> getUsersByPage(IPage<UserClassVo> page,@Param(Constants.WRAPPER) QueryWrapper wrapper);
      }
      
    • 测试

      @Test
      void testGetUsersByPage(){
          Page<UserClassVo> page = new Page<>(2,2);
          QueryWrapper<UserClassVo> wrapper = new QueryWrapper<>();
          wrapper.likeRight("a.name","java");
          userMapper.getUsersByPage(page,wrapper);
          page.getRecords().forEach(System.out::println);
      }
      
    Creating a new SqlSession
    SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f438904] was not registered for synchronization because synchronization is not active
    JDBC Connection [HikariProxyConnection@766089249 wrapping com.mysql.jdbc.JDBC4Connection@62cba181] will not be managed by Spring
     JsqlParserCountOptimize sql=select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id WHERE a.name LIKE ?
    ==>  Preparing: SELECT COUNT(1) FROM user a LEFT JOIN user_class b ON a.id = b.user_id LEFT JOIN class c ON b.class_id = c.id WHERE a.name LIKE ? 
    ==> Parameters: java%(String)
    <==    Columns: COUNT(1)
    <==        Row: 3
    ==>  Preparing: select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id WHERE a.name LIKE ? LIMIT ?,? 
    ==> Parameters: java%(String), 0(Long), 2(Long)
    <==    Columns: id, name, age, email, create_time, update_time, sfyx, class_name
    <==        Row: 6, java大师, 38, javadashi@qq.com, null, 2021-09-20 18:04:29.0, 1, 小二班
    <==        Row: 7, java大师, 11, javadashi@qq.com, null, 2021-09-20 18:04:29.0, 1, 大一班
    <==      Total: 2
    

    10、条件构造器

    说明:

    QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
    用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
    注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

    支持:allEq,eq,isNull等等,具体见:条件构造器 | MyBatis-Plus (baomidou.com)

    • 测试
    @Test
    void testWrapper01(){
        // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12记录
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.isNotNull("name").isNotNull("create_time").gt("age",12);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    void testWrapper02(){
        // 查询年龄18到24之间的记录
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age",18,24);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    
    @Test
    void testWrapper03(){
        // 名字中带java的记录
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.likeRight("name","java");
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    

    11、性能分析插件

    实际工作中,可能会遇到sql慢的一些情况,通过性能分析插件,可以定位到慢的sql和其运行时长

    • 导入插件

      @Bean
      @Profile({"dev"}) //开发环境运行
      public PerformanceInterceptor performanceInterceptor(){
          PerformanceInterceptor interceptor = new PerformanceInterceptor();
          interceptor.setFormat(true); //格式化sql
          interceptor.setMaxTime(20);  //设置超时时长,单位毫秒
          return interceptor;
      }
      
    • application.xml设置为dev开发模式

      # DataSource Config
      spring:
        profiles:
          active: dev
      
    • 测试(日志最后1行显示超时报错)

      org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
      ### Error querying database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException:  The SQL execution time is too large, please optimize ! 
      ### The error may exist in com/mpstudy/mp/mapper/UserMapper.java (best guess)
      ### The error may involve com.mpstudy.mp.mapper.UserMapper.getUsersByPage
      ### The error occurred while handling results
      ### SQL: select a.*,c.name as class_name from user a left join user_class b on a.id=b.user_id left join class c on b.class_id = c.id WHERE a.name LIKE ? LIMIT ?,?
      ### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException:  The SQL execution time is too large, please optimize ! 
      

    12、代码自动生成器

    dao、entity、service、controller自动生成
    AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、
    Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

    • 导入依赖

      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-generator</artifactId>
          <version>3.1.0</version>
      </dependency>
      
    • 代码生成

      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.InjectionConfig;
      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;
      import java.util.HashMap;
      import java.util.Map;
      
      //代码自动生成器
      public class CodeGenerator {
      
          public static void main(String[] args) {
              //构建一个 代码自动生成器 对象
              AutoGenerator autoGenerator = new AutoGenerator();
      
              //1、全局配置
              GlobalConfig gc = new GlobalConfig();
              String oPath = System.getProperty("user.dir");//得到当前项目的路径
              gc.setOutputDir(oPath + "/src/main/java");   //生成文件输出根目录
              gc.setOpen(false);//生成完成后不弹出文件框
              gc.setFileOverride(true);  //文件覆盖
              gc.setAuthor("ryan");// 作者
              gc.setServiceName("%sService");    //去除Service的I前綴
              gc.setIdType(IdType.ID_WORKER);
              gc.setDateType(DateType.ONLY_DATE);
              gc.setSwagger2(true);
              autoGenerator.setGlobalConfig(gc);
      
              ///2、数据源配置
              DataSourceConfig dsc = new DataSourceConfig();
              dsc.setDbType(DbType.MYSQL);   //设置数据库类型
              dsc.setUrl("jdbc:mysql://localhost:3306/mp_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");  //指定数据库
              dsc.setDriverName("com.mysql.jdbc.Driver");
              dsc.setUsername("root");
              dsc.setPassword("root");
              autoGenerator.setDataSource(dsc);
      
      
      
              //3、包的配置
              PackageConfig pc = new PackageConfig();
              pc.setModuleName("mp");
              pc.setParent("com.mpstudy");
              pc.setEntity("pojo");
              pc.setMapper("mapper");
              pc.setService("service");
              pc.setController("controller");
              autoGenerator.setPackageInfo(pc);
      
              // 4、策略配置
              StrategyConfig strategy = new StrategyConfig();
              strategy.setInclude("user","user_class","class"); // 设置要映射的表名
              strategy.setNaming(NamingStrategy.underline_to_camel);
              strategy.setColumnNaming(NamingStrategy.underline_to_camel);
              strategy.setEntityLombokModel(true); // 自动lombok;
              strategy.setLogicDeleteFieldName("deleted");
              strategy.setTablePrefix("tb_"); //去掉表的前缀
              // 自动填充配置
              TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT);
              TableFill gmtModified = new TableFill("update_time", 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);
              autoGenerator.setStrategy(strategy);
      
              //不加这个会报空指针异常
              InjectionConfig injectionConfig = new InjectionConfig() {
                  //自定义属性注入:abc
                  //在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
                  @Override
                  public void initMap() {
                      Map<String, Object> map = new HashMap<>();
                      map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                      this.setMap(map);
                  }
              };
              //自定义配置
              autoGenerator.setCfg(injectionConfig);
      
              // 执行生成
              autoGenerator.execute();
          }
      }
      
      
  • 相关阅读:
    零位扩展和符号位扩展
    JAVA虚拟机20基于栈的解释器执行过程示例
    JAVA虚拟机16方法的动态调用
    JAVA虚拟机17栈帧(局部变量表操作数栈动态连接返回地址)
    JAVA虚拟机21JAVA内存模型
    JAVA虚拟机18方法调用
    符号扩展和零位扩展
    JAVA虚拟机22原子性、可见性与有序性、先行发生原则
    MYSQL各版本下载,包括linux和windows
    【转】Android模拟器怎么配置网络连通
  • 原文地址:https://www.cnblogs.com/dalaba/p/15315355.html
Copyright © 2020-2023  润新知