zoukankan      html  css  js  c++  java
  • MyBatisPlus学习整理(一)

    MyBatisPlus学习整理(一)

    https://www.jianshu.com/p/12ec123d20e8

    本文是通过慕课网相关课程学习MyBatisPlus整理的笔记。
    MyBatisPlus入门 : - ) 老师讲的挺好的,还不会MyBatisPlus的小伙伴门可以听一下。
    MyBatisPlus官网
    MyBatisPlus源码地址

    MyBatisPlus架构图(盗用官网的,侵,删。)

     
    mybatis-plus.png

    SpringBoot第一个简单应用

    1. 数据库建表
    #创建用户表
    CREATE TABLE user (
        id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
        name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
        age INT(11) DEFAULT NULL COMMENT '年龄',
        email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
        manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
        create_time DATETIME DEFAULT NULL COMMENT '创建时间',
        CONSTRAINT manager_fk FOREIGN KEY (manager_id)
            REFERENCES user (id)
    )  ENGINE=INNODB CHARSET=UTF8;
    
    #初始化数据:
    INSERT INTO user (id, name, age, email, manager_id
        , create_time)
    VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL
            , '2019-01-11 14:20:20'),
        (1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
            , '2019-02-05 11:12:22'),
        (1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
            , '2019-02-14 08:31:16'),
        (1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
            , '2019-01-14 09:15:15'),
        (1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
            , '2019-01-14 09:48:16');
    
    1. 依赖
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.1.2</version>
            </dependency>
    
    1. springboot配置文件
    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
    logging:
      level:
        root: warn
        org.ywb.demo.dao: trace
      pattern:
        console: '%p%m%n'
    
    
    1. 创建相关包,如图:


       
      image.png
    2. 在pojo包中新建和数据库user表映射的类
    @Data
    public class User {
        private Long id;
        private String name;
        private Integer age;
        private String email;
        private String managerId;
        private LocalDateTime createTime;
    }
    
    1. 在dao包中创建mapper接口,并集成mybatisPlus的BaseMapper
    public interface UserMapper extends BaseMapper<User> {
    
    }
    
    1. 在springboot启动类添加@MapperScan扫描dao层接口
    @MapperScan("org.ywb.demo.dao")
    @SpringBootApplication
    public class MybatisPlusDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisPlusDemoApplication.class, args);
        }
    
    }
    

    8.编写测试类

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class MybatisPlusDemoApplicationTests {
    
        @Resource
        private UserMapper userMapper;
        
        @Test
        public void select(){
            List<User> users = userMapper.selectList(null);
            users.forEach(System.out::println);
        }
    
    }
    

    运行结果:


     
    image.png

    常用注解

    MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹配。

    注解名称说明
    @TableName 实体类的类名和数据库表名不一致
    @TableId 实体类的主键名称和表中主键名称不一致
    @TableField 实体类中的成员名称和表中字段名称不一致
    @Data
    @TableName("t_user")
    public class User {
        @TableId("user_id")
        private Long id;
        @TableField("real_name")
        private String name;
        private Integer age;
        private String email;
        private Long managerId;
        private LocalDateTime createTime;
    }
    

    排除实体类中非表字段

    1. 使用transient关键字修饰非表字段,但是被transient修饰后,无法进行序列化。
    2. 使用static关键字,因为我们使用的是lombok框架生成的get/set方法,所以对于静态变量,我们需要手动生成get/set方法。
    3. 使用@TableField(exist = false)注解

    CURD

    BaseMapper中封装了很多关于增删该查的方法,后期自动生成,我们直接调用接口中的相关方法即可完成相应的操作。
    BaseMapper部分代码

    public interface BaseMapper<T> extends Mapper<T> {
    
        int insert(T entity);
       
        int deleteById(Serializable id);
    
        int deleteByMap(

    插入一条记录测试:

      @Test
        public void insert(){
            User user = new User();
            user.setAge(31);
            user.setManagerId(1088250446457389058L);
            user.setCreateTime(LocalDateTime.now());
            int insert = userMapper.insert(user);
            System.out.println("影像记录数:"+insert);
        }
    
     
    image.png

    条件构造器查询

    除了BaseMapper中提供简单的增删改查方法之外,还提供了很多关于区间查询,多表连接查询,分组等等查询功能,实现的类图如下所示:

     
    image.png

    通过观察类图可知,我们需要这些功能时,只需要创建QueryWrapper对象即可。
    1. 模糊查询
    /**
         * 查询名字中包含'雨'并且年龄小于40
         * where name like '%雨%' and age < 40
         */
        
     
    image.png
    1. 嵌套查询
        /**
         * 创建日期为2019年2月14日并且直属上级姓名为王姓
         * date_format(create_time,'%Y-%m-%d') and manager_id in (select id from user where name like '王%')
         */
        
     
    image.png

    注意
    上面的日期查询使用的是占位符的形式进行查询,目的就是为了防止SQL注入的风险。
    apply方法的源码
       /**
         * 拼接 sql
         * <p>!! 会有 sql 注入风险 !!</p>
         * <p>例1: apply("id = 1")</p>
         * <p>例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")</p>
         * <p>例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())</p>
         *
         * @param condition 执行条件
         * @return children
         */
        Children apply(boolean condition, String applySql, Object... value);
    

    SQL 注入的例子:

    queryWrapper.apply("date_format(create_time,'%Y-%m-%d')=2019-02-14 or true=true")
                  .inSql("manager_id","select id from user where name like '王%'");
    
     
    sql注入例子
    1. and & or
      /**
         * 名字为王姓,(年龄小于40或者邮箱不为空)
         */
        @Test
        public void selectByWrapper3(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.likeRight("name","王").and(wq-> wq.lt("age",40).or().isNotNull("email"));
    
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
    
        }
    
    1. between & and
       /**
         * 名字为王姓,(年龄小于40,并且年龄大于20,并且邮箱不为空)
         */
        @Test
        public void selectWrapper4(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.likeRight("name", "王").and(wq -> wq.between("age", 20, 40).and(wqq -> wqq.isNotNull("email")));
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png
    1. nested
       /**
         * (年龄小于40或者邮箱不为空)并且名字为王姓
         * (age<40 or email is not null)and name like '王%'
         */
        @Test
        public void selectWrapper5(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    
            queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email")).likeRight("name","王");
    
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png
    1. in
       /**
         * 年龄为30,31,35,34的员工
         */
        @Test
        public void selectWrapper6(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    
            queryWrapper.in("age", Arrays.asList(30,31,34,35));
    
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png
    1. last 有SQL注入的风险!!!
      /**
         * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)
         * <p>例: last("limit 1")</p>
         * <p>注意只能调用一次,多次调用以最后一次为准</p>
         *
         * @param condition 执行条件
         * @param lastSql   sql语句
         * @return children
         */
        Children last(boolean condition, String lastSql);
    
       /**
         * 只返回满足条件的一条语句即可
         * limit 1
         */
        
     
    image.png
    1. 查询指定部分列
        /**
         * 查找为王姓的员工的姓名和年龄
         */
        @Test
        public void selectWrapper8(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("name","age").likeRight("name","王");
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png
    1. 使用过滤器查询指定列
       /**
         * 查询所有员工信息除了创建时间和员工ID列
         */
        @Test
        public void selectWrapper9(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.select(User.class,info->!info.getColumn().equals("create_time")
                    &&!info.getColumn().equals("manager_id"));
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png

    condition 的作用

    在我们调用的查询语句中,通过查看源码(这里以apply方法为例)可以看出,每个查询方法的第一个参数都是boolean类型的参数,重载方法中默认给我们传入的都是true。

     default Children apply(String applySql, Object... value) {
            return apply(true, applySql, value);
        }
        Children apply(boolean condition, String applySql, Object... value);
    

    这个condition的作用是为true时,执行其中的SQL条件,为false时,忽略设置的SQL条件。

    实体作为条件构造方法的参数

    在web开发中,controller层常常会传递给我们一个用户的对象,比如通过用户姓名和用户年龄查询用户列表。
    我们可以将传递过来的对象直接以构造参数的形式传递给QueryWrapper,MyBatisPlus会自动根据实体对象中的属性自动构建相应查询的SQL语句。

     @Test
        public void selectWrapper10(){
            User user = new User();
            user.setName("刘红雨");
            user.setAge(32);
            QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png

    如果想通过对象中某些属性进行模糊查询,我们可以在跟数据库表对应的实体类中相应的属性标注注解即可。
    比如我们想通过姓名进行模糊查询用户列表。

    @TableField(condition = SqlCondition.LIKE)
        private String name;
    
     @Test
        public void selectWrapper10(){
            User user = new User();
            user.setName("红");
            user.setAge(32);
            QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
            List<User> userList = userMapper.selectList(queryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png

    Lambda条件构造器

    MybatisPlus提供了4种方式创建lambda条件构造器,前三种分别是这样的

            LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
            LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
            LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();
    
    1. 查询名字中包含‘雨’并且年龄小于40的员工信息
        @Test
        public void lambdaSelect(){
            LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery();
            lambdaQueryWrapper.like(User::getName,"雨").lt(User::getAge,40);
    
            List<User> userList = userMapper.selectList(lambdaQueryWrapper);
            userList.forEach(System.out::println);
        }
    
     
    image.png

    QueryWrapper类已经提供了很强大的功能,而lambda条件构造器做的和QueryWrapper的事也是相同的为什么要冗余的存在lambda条件构造器呢?
    QueryWrapper是通过自己写表中相应的属性进行构造where条件的,容易发生拼写错误,在编译时不会报错,只有运行时才会报错,而lambda条件构造器是通过调用实体类中的方法,如果方法名称写错,直接进行报错,所以lambda的纠错功能比QueryWrapper要提前很多。
    举个例子:
    查找姓名中包含“雨”字的员工信息。
    使用QueryWrapper

    queryWrapper.like("name","雨");
    

    使用lambda

    lambdaQueryWrapper.like(User::getName,"雨");
    

    如果在拼写name的时候不小心,写成了naem,程序并不会报错,但是如果把方法名写成了getNaem程序立即报错。

    第四种lambda构造器
    细心的人都会发现无论是之前的lambda构造器还是queryWrapper,每次编写完条件构造语句后都要将对象传递给mapper 的selectList方法,比较麻烦,MyBatisPlus提供了第四种函数式编程方式,不用每次都传。

    1. 查询名字中包含“雨”字的,并且年龄大于20的员工信息
        @Test
        public void lambdaSelect(){
            List<User> userList = new LambdaQueryChainWrapper<>(userMapper).like(User::getName, "雨").ge(User::getAge, 20).list();
            userList.forEach(System.out::println);
        }
    
     
    image.png

    自定义SQL

    1. 在resources资源文件夹下新建mapper文件夹,并将mapper文件夹的路径配置到配置文件中


       
      image.png
    mybatis-plus:
      mapper-locations: mapper/*.xml
    
    1. 在mapper 文件夹中新建UserMapper.xml。
    2. 像mybatis那样在UseMapper接口中写接口,在UserMapper接口中写SQL即可。
      UserMapper
    public interface UserMapper extends BaseMapper<User> {
    
        /**
         * 查询所有用户信息
         * @return list
         */
        List<User> selectAll();
    }
    

    UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="org.ywb.demo.dao.UserMapper">
    
        <select id="selectAll" resultType="org.ywb.demo.pojo.User">
            select * from user
        </select>
    </mapper>
    

    分页查询

    MyBatis分页提供的是逻辑分页,每次将所有数据查询出来,存储到内存中,然后根据页容量,逐页返回。如果表很大,无疑是一种灾难!
    MyBatisPlus物理分页插件

    1. 新建config类,在config类中创建PaginationInterceptor对象
    @Configuration
    public class MybatisPlusConfig {
    
        @Bean
        public PaginationInterceptor paginationInterceptor(){
            return new PaginationInterceptor();
        }
    }
    
    1. 测试:查询年龄大于20 的用户信息,并以每页容量为两条分页的形式返回。
        @Test
        public void selectPage(){
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.ge("age",20);
    
            //设置当前页和页容量
            Page<User> page = new Page<>(1, 2);
    
            IPage<User> userIPage = userMapper.selectPage(page, queryWrapper);
    
            System.out.println("总页数:"+userIPage.getPages());
            System.out.println("总记录数:"+userIPage.getTotal());
            userIPage.getRecords().forEach(System.out::println);
        }
    
     
    image.png
    1. 测试:不查询总记录数,分页查询
      IPage类的构造参数提供了参数的重载,第三个参数为false时,不会查询总记录数。
    public Page(long current, long size, boolean isSearchCount) {
            this(current, size, 0, isSearchCount);
    }
    ~~·
    ## 更新
    1. 通过userMapper提供的方法更新用户信息
    ~~~java
        @Test
        public void updateTest1(){
            User user = new User();
            user.setId(1088250446457389058L);
            user.setEmail("update@email");
            int rows = userMapper.updateById(user);
            System.out.println(rows);
        }
    
     
    image.png
    1. 使用UpdateWrapper更新数据(相当于使用联合主键)
        @Test
        public void updateTest2(){
            UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("name","李艺伟").eq("age",26);
    
            User user = new User();
            user.setEmail("update2@email");
            int rows = userMapper.update(user, updateWrapper);
            System.out.println(rows);
        }
    
     
    image.png
    1. 当我们更新少量用户信息的时候,可以不用创建对象,直接通过调用set方法更新属性即可。
        @Test
        public void updateTest3(){
            UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
            updateWrapper.eq("name","李艺伟").eq("age",26).set("email","update3@email.com");
            userMapper.update(null,updateWrapper);
        }
    
     
    image.png
    1. 使用lambda更新数据
        @Test
        public void updateByLambda(){
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
            lambdaUpdateWrapper.eq(User::getName,"李艺伟").eq(User::getAge,26).set(User::getAge,27);
            userMapper.update(null,lambdaUpdateWrapper);
        }
    
     
    image.png

    删除

    删除方式和update极其类似。

    AR模式(Active Record)

    直接通过实体类完成对数据的增删改查。

    1. 实体类继承Model类
    @Data
    @EqualsAndHashCode(callSuper = false)
    public class User extends Model<User> {
        private Long id;
        @TableField(condition = SqlCondition.LIKE)
        private String name;
        private Integer age;
        private String email;
        private Long managerId;
        private LocalDateTime createTime;
    }
    

    Model类中封装了很多增删改查方法,不用使用UserMapper即可完成对数据的增删改查。

    1. 查询所有用户信息
        @Test
        public void test(){
            User user = new User();
            user.selectAll().forEach(System.out::println);
        }
    
     
    image.png

    主键策略

    MyBatisPlus的主键策略封装在IdType枚举类中。

    @Getter
    public enum IdType {
        /**
         * 数据库ID自增
         */
        AUTO(0),
        /**
         * 该类型为未设置主键类型(将跟随全局)
         */
        NONE(1),
        /**
         * 用户输入ID
         * <p>该类型可以通过自己注册自动填充插件进行填充</p>
         */
        INPUT(2),
    
        /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
        /**
         * 全局唯一ID (idWorker)
         */
        ID_WORKER(3),
        /**
         * 全局唯一ID (UUID)
         */
        UUID(4),
        /**
         * 字符串全局唯一ID (idWorker 的字符串表示)
         */
        ID_WORKER_STR(5);
    
        private final int key;
    
        IdType(int key) {
            this.key = key;
        }
    }
    
    

    在实体类中对应数据库中的主键id属性上标注注解TableId(type='xxx')即可完成主键配置。

        @TableId(type = IdType.AUTO)
        private Long id;
    

    这种配置方式的主键策略只能在该表中生效,但是其他表还需要进行配置,为了避免冗余,麻烦,MybatisPlus提供了全局配置,在配置文件中配置主键策略即可实现。

    mybatis-plus:
      mapper-locations: mapper/*.xml
      global-config:
        db-config:
          id-type: auto
    

    如果全局策略和局部策略全都设置,局部策略优先。

    基本配置

    MyBatisPlus官方文档

    mybatis-plus:
      mapper-locations: mapper/*.xml
      global-config:
        db-config:
          # 主键策略
          id-type: auto
          # 表名前缀
          table-prefix: t
          # 表名是否使用下划线间隔,默认:是
          table-underline: true
      # 添加mybatis配置文件路径
      config-location: mybatis-config.xml
      # 配置实体类包地址
      type-aliases-package: org.ywb.demo.pojo
      # 驼峰转下划线
      configuration:
        map-underscore-to-camel-case: true
    
    • 附录
    1. mybatisPlus进阶功能请戳 MyBatisPlus学习整理(二)
    2. 源码地址:https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo
     
     
    73人点赞
     
    mysql
     
     


    作者:茶还是咖啡
    链接:https://www.jianshu.com/p/12ec123d20e8
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    Anaconda+Tensorflow环境安装与配置
    计算机视觉(视频追踪检测分类、监控追踪)常用测试数据集
    迁移学习( Transfer Learning )
    matlab函数_连通区域
    GMM的EM算法实现
    对​O​p​e​n​C​V​直​方​图​的​数​据​结​构​C​v​H​i​s​t​o​g​r​a​m​的​理​解
    opencv基于混合高斯模型的图像分割
    LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
    视频测试序列(转)
    高职扩招,拿大专学历
  • 原文地址:https://www.cnblogs.com/handsome1013/p/12811634.html
Copyright © 2011-2022 走看看