Loading

Mybatis-plus 快速入门(用Mapper)

注:以下写法均是3.0.5版本时写的!与最新版本有多处不同!

一、快速入门

官网地址:https://baomidou.com/

步骤:

  1. 创建数据库 mybatis-plus

  2. 创建 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
    
    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. 初始化项目

    快速构建一个spring boot项目。

  4. 导入相关依赖。

    <!--mybatis-plus(是自己开发的starter,不是官方的)-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    <!--MySQL数据库驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    

    说明:我们使用mybatis-plus可以节省我们大量的代码,尽量不要同时导入mybatis和mybatis-plus!版本的差异!

  5. 连接数据库

    spring:
      datasource:
        # MySQL8的驱动(和MySQL5不同)。需要增加时区的配置 serverTimezone=GMT%2B8
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=GMT%2B8
        username: root
        password: admin
    server:
      port: 8086
    
  6. mybatis-plus的优势。

    使用传统方式:

    • pojo
    • dao
    • service
    • controller

    使用mybatis-plus:

    • pojo

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private Long id;
          private String name;
          private Integer age;
          private String email;
      }
      
    • mapper接口

      /**
       * Mapper接口
       *
       * 继承了BaseMapper,所有的方法都来自于自己的父类。我们也可以编写自己的扩展方法。
       */
      public interface UserMapper extends BaseMapper<User> {
      
      }
      
    • 使用(需要在主程序配置@MapperScan来扫描到UserMapper) @MapperScan("com.example.mybatisplus.mapper")

      @SpringBootTest
      class MybatisplusApplicationTests {
          @Autowired(required = false)
          private UserMapper userMapper;
      
          @Test
          public void testSelect(){
              System.out.println("=====查询所有的数据=====");
              //参数是一个wrapper,条件构造器,没有就填null。
              List<User> list = userMapper.selectList(null);
              list.forEach(System.out::println);
          }
      }
      

二、配置日志

用了Mybatis-plus后,这时的sql是不可见的,我们希望知道它是怎么执行的,所以需要打开日志!

# mybatis配置日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

01显示日志

三、CRUD扩展

插入操作

@Test
public void testInsert(){
    System.out.println("=====insert method test======");
    User user = new User();
    //并没有设置userId。(帮我们自动生成id)
    user.setName("弹头");
    user.setAge(23);
    user.setEmail("2238224084@qq.com");
    int insert = userMapper.insert(user);
    System.out.println(insert); //受影响的行数

    System.out.println(user);   //id会自动回填
}

02insert操作日志

数据库插入的id的默认值为:全局的唯一id

主键生成策略

雪花算法:

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。几乎可以保证几乎全球唯一!

Mybatis-Plus自 3.3.0 开始,默认使用雪花算法(ASSIGN_ID)+UUID(不含中划线)

主键自增

我们需要配置主键自增:

  1. 实体类字段上 @TableId(type = IdType.AUTO)
  2. 数据库字段一定要自增!(否则会报错!)

其余源码解释

//这是老版本的方式。新版本已经改变了。
public enum IdType {
    AUTO(0),	//数据库id自增
    NONE(1),	//未设置主键
    INPUT(2),	//手动输入
    ID_WORKER(3),	//默认的全局唯一id
    UUID(4),	//全局唯一id uuid
    ID_WORKER_STR(5);	//默认的全局唯一id,字符串方式
}

更新操作

@Test
public void testUpdate() {
    User user = new User();
    user.setId(1482682712933310466L);
    user.setName("王忠舟");
    int update = userMapper.updateById(user);

    System.out.println("受影响的行数:" + update);
}

03update操作日志

@Test
public void testUpdate() {
    User user = new User();
    user.setId(1482682712933310466L);
    user.setName("王忠舟");
    user.setAge(18);
    int update = userMapper.updateById(user);

    System.out.println("受影响的行数:" + update);
}

04自动拼接动态sql

自动填充

创建时间、修改时间!这些操作都是自动化完成的,我们不希望手动更新!

阿里巴巴开发手册:所有的数据表都要配置上gmt_create(创建时间)、gmt_modified(修改时间)!而且都要自动化!

  1. 在表中新增字段 create_timeupdate_time。取消默认值、更新操作。

    05表中插入创建时间和修改时间字段

  2. 把实体类同步,并在字段属性上增加注解。

    //字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  3. 需要自定义实现类,处理这个注解。在项目根路径下新建 /handler/MyMetaObjectHandler.java,并继承 MetaObjectHandler 接口。

    @Slf4j
    @Component  //要被识别,必须放到IOC容器中。
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill ...");
            //setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
            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. 测试插入和更新操作(观察时间即可!)。

乐观锁

在面试过程中,我们经常会被问道乐观锁、悲观锁!这个其实非常简单!

乐观锁:顾名思义,十分乐观,他总是认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新值测试。

悲观锁:顾名思义,十分悲观,他总是认为会出现问题,无论干什么都会上锁!再去操作!

乐观锁实现方式(这里主要讲解乐观锁机制):

  • 取出记录时,获取当前version。
  • 更新时,带上这个version
  • 执行更新时,set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败。(如果修改之前有人已经修改了该条记录,那么 version = 2 ,不满足该条更新语句,所以就无法操作了。)

mybatis-plus使用步骤:

  1. 给数据库中增加version字段。

    06乐观锁version字段

  2. 实体类中同步相应的字段。

    @Version    //乐观锁Version注解
    private Integer version;
    
  3. 注册组件

    @Configuration
    //将主程序的扫描移动到配置文件中。
    @MapperScan("com.example.mybatisplus.mapper")
    public class MyBatisPlusConfig {
        //注册乐观锁插件
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }
    
  4. 测试乐观锁成功!先查询,后修改。

    //测试乐观锁成功
    @Test
    public void testOptimisticLocker(){
        //1、查询用户信息
        User user = userMapper.selectById(1482682712933310469L);
        //2、修改用户信息
        user.setAge(18);
        user.setEmail("2238224084@qq.com");
        //3、执行更新操作
        userMapper.updateById(user);
    }
    

    07测试乐观锁成功

  5. 测试乐观锁失败!

    //测试乐观锁失败(多线程下)
    @Test
    public void testOptimisticLocker2(){
        //线程1
        User user1 = userMapper.selectById(1482682712933310469L);
        user1.setAge(21);
        user1.setName("芜湖");
    
        //模拟另一个线程执行了插队操作。
        User user2 = userMapper.selectById(1482682712933310469L);
        user2.setAge(20);
        //线程1已经查询了,但还没来得及更新,被另一个线程查询并抢先更新了。
        userMapper.updateById(user2);
    
        //如果没有乐观锁,就会覆盖插队线程的值。
        userMapper.updateById(user1);
    }
    

    08测试乐观锁失败

查询操作

//id查询
@Test
public void testSelectById(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}

//批量查询
@Test
public void testSelectBatchIds(){
    List<Long> list = Arrays.asList(1L, 2L, 3L);
    List<User> users = userMapper.selectBatchIds(list);
    users.forEach(System.out::println);
}

//条件查询使用之一 map
@Test
public void testSelectByMap(){
    HashMap<String, Object> map = new HashMap<>();
    //自定义查询条件
    map.put("name", "王忠舟");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

分页查询

分页在网站使用的十分之多!

  • 原始的limit进行分页。

  • pageHelper第三方插件。

  • MP其实也内置了分页插件!

如何使用?

  1. 导入分页插件

    // 分页插件(旧版)
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
    
  2. 直接使用Page对象即可!

    @Configuration
    //将主程序的扫描移动到配置文件中。
    @MapperScan("com.example.mybatisplus.mapper")
    public class MyBatisPlusConfig {   
        //测试分页查询
        @Test
        public void testPage(){
            //参数1:当前页;参数2:页面数据条数。可以设置为变量从前端接收。
            Page<User> page = new Page<>(1,5);
            userMapper.selectPage(page, null);
    
            page.getRecords().forEach(System.out::println);
        }
    }
    

    09分页查询1

    //测试分页查询
    @Test
    public void testPage(){
        //参数1:当前页;参数2:页面数据条数。可以设置为变量从前端接收。
        Page<User> page = new Page<>(2,5);
        userMapper.selectPage(page, null);
    
        page.getRecords().forEach(System.out::println);
    }
    

    09分页查询2

当前页和页面数据条数可以设置为变量,从前端接收。

删除操作

//删除
@Test
public void testDelete(){
    int delete = userMapper.deleteById(1482682712933310466L);
    System.out.println("受影响的条数:"+delete);
}

//批量删除
@Test
public void testDeleteBatchIds(){
    int batchIds = userMapper.deleteBatchIds(Arrays.asList(1L, 2L));
    System.out.println("受影响的条数:"+batchIds);
}

//通过map 删除
@Test
public void testDeleteByMap(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "弹头蛋头");
    int delete = userMapper.deleteByMap(map);
    System.out.println("受影响的条数:"+delete);
}

逻辑删除

我们在工作中会遇到一些问题:逻辑删除!

物理删除:从数据库中直接删除

逻辑删除:没有真正从数据库中删除,而是通过一个变量来让他失效。deleted = 0 ==> deleted = 1

管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!

步骤:

  1. 在数据表中增加一个 deleted 字段。

    10逻辑删除字段

  2. 在实体类中同步属性。

    @TableLogic //逻辑删除
    private Integer deleted;
    
  3. config中注册组件。

    @Configuration
    //将主程序的扫描移动到配置文件中。
    @MapperScan("com.example.mybatisplus.mapper")
    public class MyBatisPlusConfig {
        //逻辑删除(旧版)
        @Bean
        public ISqlInjector sqlInjector() {
            return new LogicSqlInjector();
        }
    }
    
  4. 配置文件中配置逻辑删除

    mybatis-plus:
      global-config:
        db-config:
          # 配置逻辑删除
          logic-delete-value: 1
          logic-not-delete-value: 0
    
  5. 测试删除,再执行查询操作。查看查询结果和表中的结果。

    逻辑删除实际上是更新操作,并不是删除操作

    11逻辑删除

以上的所有CRUD操作及其扩展操作,我们都必须精通掌握。会大大提高工作和写项目的效率!

性能分析插件

我们在平时的开发中,会遇到一些慢sql。测试!

MP也提供性能分析插件,如果超过这个时间就停止运行!

使用:

  1. 导入插件

    //SQL执行效率插件
    @Bean
    @Profile({"dev", "test"})   //设置 dev test 环境开启,保证我们的效率
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        //设置sql执行的最大时间,如果超过了则不执行。(ms/毫秒)
        performanceInterceptor.setMaxTime(100);
        //开启sql格式化
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    }
    
  2. 在配置文件中配置环境为dev 或者 test 环境。

    spring:
      profiles:
        # 开发环境
        active: dev
    
  3. 测试使用

    @Test
    public void testSelect() {
        System.out.println("=====selectAll method test=====");
        //参数是一个wrapper,条件构造器,没有就填null。
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
    

    12性能分析插件

    使用性能分析插件,可以帮助我们提高效率!

条件构造器

十分重要:Wrapper

我们写一些复杂的sql就可以使用它来替代!

使用:

  • 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于20的数据,记住查看输出的sql进行分析。

    @Test
    public void Test(){
        //查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于20
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.isNotNull("name").isNotNull("email").ge("age", 20);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }
    

    13条件构造器

  • 查询名字为“弹头”的数据(使用selectOne),记住查看输出的sql进行分析。

    @Test
    public void testSelectOne(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", "弹头");
        //只查询一个的话可以用selectOne,若超过一个或为0个的话会报错。
        System.out.println(userMapper.selectOne(wrapper));
    }
    

    14selectOne报错

  • 查询年龄在 20 - 30 岁之间的用户的数量,记住查看输出的sql进行分析。

    //查询年龄在 20 - 30 岁之间的用户的数量
    @Test
    public void testBetweenAnd(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //区间
        wrapper.between("age", 20, 30);
        //查询结果数
        Integer count = userMapper.selectCount(wrapper);
        System.out.println(count);
    }
    

    15selectCount

  • 模糊查询,记住查看输出的sql进行分析。

    @Test
    public void testLike(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
            .notLike("email", "qq")
            .likeRight("email", "test4");   //Right和Left表示 "%" 在哪边
        List<Map<String, Object>> mapList = userMapper.selectMaps(wrapper);
        mapList.forEach(System.out::println);
    }
    

    16模糊查询

  • 使用子查询,记住查看输出的sql进行分析。

    @Test
    public void testSelectObjs(){
        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);
    }
    

    17inSql查询

  • order排序查询,记住查看输出的sql进行分析。

    @Test
    public void testOrder(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("id");
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }
    

    18orderDesc

四、代码生成器

dao、pojo、service、controller都自己去编写完成!

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

写一个代码生成器也挺复杂的,我感觉这东西适合大型项目,需要多个包的。就一个包的小型项目就自己手写也挺快的。

步骤:

  1. 还需要导入相应依赖

    <!--mybatis-plus代码生成器-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.0.5</version>
    </dependency>
    <!-- mybatis-plus代码生成器所需要的模板引擎 -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.3</version>
    </dependency>
    
  2. 编写代码

    package com.example.mybatisplus;
    
    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 CodeGenerator {
        public static void main(String[] args) {
            // 代码生成器
            AutoGenerator mpg = new AutoGenerator();
            //配置策略
    
            //1、全局配置
            GlobalConfig gc = new GlobalConfig();
            String property = System.getProperty("user.dir");   //项目路径
            gc.setOutputDir(property + "/src/main/java");   //新建文件夹的位置
            gc.setAuthor("dan");    //标注作者
            gc.setOpen(false);  //是否新建完成打开资源管理器
            gc.setFileOverride(false);  //是否覆盖
            gc.setIdType(IdType.ID_WORKER); //主键策略
            gc.setDateType(DateType.ONLY_DATE); //日期类型
            gc.setSwagger2(true);   //自动生成swagger
            mpg.setGlobalConfig(gc);
    
            //2、设置数据源(数据库配置)
            DataSourceConfig dsc = new DataSourceConfig();
            dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=GMT%2B8");
            dsc.setDriverName("com.mysql.cj.jdbc.Driver");
            dsc.setUsername("root");
            dsc.setPassword("admin");
            dsc.setDbType(DbType.MYSQL);
            mpg.setDataSource(dsc);
    
            //3、包的配置
            PackageConfig pc = new PackageConfig();
            pc.setModuleName("blog");
            pc.setParent("com.example");
            pc.setEntity("pojo");
            pc.setMapper("mapper");
            pc.setController("controller");
            pc.setService("service");
            mpg.setPackageInfo(pc);
    
            //4、策略配置
            StrategyConfig sc = new StrategyConfig();
            sc.setInclude("user");  //映射数据库中的表名
            sc.setNaming(NamingStrategy.underline_to_camel);
            sc.setColumnNaming(NamingStrategy.underline_to_camel);
            sc.setEntityLombokModel(true);  //自动Lombok。
            sc.setLogicDeleteFieldName("deleted");  //逻辑删除字段
            TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
            TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
            ArrayList<TableFill> list = new ArrayList<>();
            list.add(createTime);
            list.add(updateTime);
            sc.setTableFillList(list);
            //乐观锁
            sc.setVersionFieldName("version");  //乐观锁字段
            sc.setRestControllerStyle(true);    //驼峰命名
            mpg.setStrategy(sc);
    
            //执行
            mpg.execute();
        }
    }
    
posted @ 2022-01-17 02:20  KledKled  阅读(211)  评论(0编辑  收藏  举报