• [SSM]MyBatisPlus高级


    四、高级篇

    4.1主键策略

    • 主键的作用就是唯一标识,我们可以通过这个唯一标识来定位到这条数据。
    • 对于表数据中的主键,我们可以自己设计生成规则,生成主键。但是在更多的场景中,没有特殊要求的话,我们每次自己手动生成的比较麻烦,我们可以借助框架提供好的主键生成策略来生成主键,这样比较方便。
    • 在MybatisPlus中提供了一个注解,是@TableId,该注解提供了各种的主键生成策略,我们可以通过使用该注解来对于新增的数据指定主键生成的策略。那么在以后新增数据的时候,数据就会按照我们指定的主键生成策略来生成对应的主键。
    4.1.1AUTO策略
    • 该策略为跟随数据库表的主键递增策略,前提是数据库表的主键要设置为自增。
    • 实体类添加主键,指定主键生成策略。
    //设置主键自增
    @TableId(type = IdType.AUTO)
    private Long id;
    
    • 1
    • 2
    • 3
    • 插入数据
    @Test
    void primary() {
        User user = new User();
        user.setName("Mary");
        user.setAge(30);
        user.setEmail("mary@qq.com");
    
        userMapper.insert(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20230904171525148.png&pos_id=img-wn0ndijF-1694007802578)在这里插入图片描述

    4.1.2INPUT策略
    • 该策略表示,必须由我们手动的插入id,否则无法添加数据。
    • 由于不使用AUTO了,所以将数据库中的自动递增去掉。
    //手动设置主键值
    @TableId(type = IdType.INPUT)
    private Long id;
    
    • 1
    • 2
    • 3
    @Test
    void primary() {
        User user = new User();
        user.setId(8L);
        user.setName("Mary");
        user.setAge(30);
        user.setEmail("mary@qq.com");
    
        userMapper.insert(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    4.1.3ASSIGN_ID策略
    • 如果我们将来一张表的数据量很大,我们需要进行分表。
    • 常见的分表策略有两种:
    1. 水平拆分

    • 水平拆分就是将一个大的表按照数据量进行拆分
    1. 垂直拆分

    • 垂直拆分就是将一个大的表按照字段进行拆分

    • 其实对于拆分后的数据,有三点需求,以水平拆分为例:

      • 之前的表的主键是有序的,拆分后还是有序的。
      • 虽然做了表的拆分,但是每条数据还需要保证主键的唯一性
      • 主键最好不要直接暴露数据的数量,这样容易被外界知道关键信息
    • 那就需要有一种算法,能够实现这三个需求,这个算法就是雪花算法。

    • 雪花算法是由一个64位的二进制组成的,最终就是一个Long类型的数值。主要分为四部分存储

      • 1位的符号位,固定值为0
      • 41位的时间戳
      • 10位的机器码,包含5位机器id和5位服务id
      • 12位的序列号

    • 使用雪花算法可以实现有序、唯一且不直接暴露排序的数字。
    //使用雪花算法,默认为type = IdType.ASSIGN_ID,可省略
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    
    • 1
    • 2
    • 3
    @Test
    void primary() {
        User user = new User();
        user.setName("Mary");
        user.setAge(30);
        user.setEmail("mary@qq.com");
    
        userMapper.insert(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 在插入后发现一个19位长度的id,该id就是雪花算法生成的id,这是二进制的十进制表示形式。
    4.1.4NONE策略
    //不指定主键生成策略
    @TableId(type = IdType.NONE)
    private Long id;
    
    • 1
    • 2
    • 3
    • NONE策略表示不指定主键生成策略,当我们没有指定主键生成策略或者主键策略为NONE的时候,他跟随的是全局策略。
    #主键生成策略全局配置(默认为assign_id)
    mybatis-plus.global-config.db-config.id-type=assign_id
    
    • 1
    • 2
    • 全局配置中id-type是用于配置主键生成策略的,id-type默认值为IdType.ASSIGN_ID
    4.1.5ASSIGN_UUID策略
    • UUID是全局唯一标识符,定义一个字符串主键,采用32位数字组成,编码采用16进制,定义了在时间和空间都完全唯一的系统信息。
    • UUID的编码规则:
      • 1~8位采用系统时间,在系统时间上精确到毫秒级保证时间上的唯一性
      • 9~16位采用底层的IP地址,在服务器集群中的唯一性
      • 17~24位采用当前对象的HashCode值,在一个内部对象上的唯一性
      • 25~32位采用调用方法的一个随机数,在一个对象内的毫秒级的唯一性
    • 通过以上4中策略可以保证唯一性,在系统中需要用到随机数的地方都可以考虑采用UUID算法。
    • 使用UUID需要将数据库表的字段类型改为varchar(50),将实体类的属性类型改为String,并指定主键生成策略。
    //主键采用ASSIGN_UUID策略
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    
    • 1
    • 2
    • 3
    • 完成数据的添加
    @Test
    void primary() {
        User user = new User();
        user.setName("Mary");
        user.setAge(30);
        user.setEmail("mary@qq.com");
    
        userMapper.insert(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    4.2分页

    • 在MybatisPlus中的查询语句有两种实现方式
      • 通过MybatisPlus提供的方法来实现条件查询
      • 通过自定义SQL语句的方式来实现查询
    4.2.1分页插件
    • 在大部分情况下,如果我们的SQL没有这么复杂,是可以直接通过MybatisPlus提供的方法来实现查询的,在这种情况下,我们可以通过配置分页插件来实现分页效果。
    • 分页的本质就是需要设置一个拦截器,通过拦截器拦截了SQL,通过在SQL语句的结尾添加limit关键字来实现分页的效果

    1.通过配置类来指定一个具体的数据库的分页插件,自动生成分页语句

    @Configuration
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.实现分页效果查询

    @Test
    void selectPage() {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //指定分页对象,分页对象包含分页信息 IPage
        Page<User> userPage = new Page<>(2, 3);
        //执行查询
        userMapper.selectPage(userPage, lambdaQueryWrapper);
        //获取分页查询的信息
        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页显示条数:" + userPage.getSize());
        System.out.println("总页数:" + userPage.getPages());
        System.out.println("总条数:" + userPage.getTotal());
        System.out.println("分页数据:" + userPage.getRecords());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    4.2.2自定义分页插件
    • 在某些场景下,我们需要自定义SQL语句来进行查询。

    1.在UserMapper.xml映射配置文件中提供查询语句

    <mapper namespace="com.hhb.mp02.mapper.UserMapper">
        <select id="selectByName" resultType="com.hhb.mp02.domain.User" parameterType="String">
            select * from t_user where username=#{name}
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.在Mapper接口中提供对应的方法,方法中将Page对象作为参数传入

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
        Page<User> selectByName(Page<User> page, String name);
    }
    
    • 1
    • 2
    • 3
    • 4

    3.实现分页查询效果

    @Test
    void selectPage2() {
        Page<User> userPage = new Page<>(1, 2);
        userMapper.selectByName(userPage,"Mary");
        System.out.println("当前页:" + userPage.getCurrent());
        System.out.println("每页显示条数:" + userPage.getSize());
        System.out.println("总页数:" + userPage.getPages());
        System.out.println("总条数:" + userPage.getTotal());
        System.out.println("分页数据:" + userPage.getRecords());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4.3ActiveRecord模式

    1.实体类继承Model

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User extends Model<User> {
        private Long id;
        @TableField("username")
        private String name;
        private Integer age;
        private String email;
        @TableField("`desc`")
        private String desc;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 我们可以看到,Model类中提供了一些增删改查的方法,这样的话我们就可以直接使用实体类对象调用这些增删改查方法了,简化了操作的语法,但是他的底层依然是需要UserMapper的,所以持久化层接口并不能省略。

    2.测试ActiveRecord模式的增删改查

    @SpringBootTest
    public class ActiveRecordTest {
    
        //添加操作
        @Test
        void activeRecordAdd() {
            User user = new User();
            user.setName("张三");
            user.setEmail("zhang@qq.com");
            user.setAge(33);
            user.insert();
        }
    
        //删除操作
        @Test
        void activeRecordDelete() {
            User user = new User();
            user.setId(9L);
            user.deleteById();
        }
    
        //修改操作
        @Test
        void activeRecordUpdate() {
            User user = new User();
            user.setId(10L);
            user.setName("李四");
            user.setAge(26);
            user.setEmail("lisi@qq.com");
            user.updateById();
        }
    
        //查询操作
        @Test
        void activeRecordSelect() {
            User user = new User();
            user.selectAll();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4.4SimpleQuery工具类

    • SimpleQuery可以对selectList查询后的结构用Stream流进行一些封装,使其可以返回一些指定结果,简洁了API的调用。
    4.4.1list

    基于字段封装集合

    @Test
    void testList() {
        List<Long> list = SimpleQuery.list(new LambdaQueryWrapper<User>().eq(User::getName, "Mary"), User::getId);
        System.out.println(list);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    对于封装后的字段进行lambda操作

    @Test
    void testList2() {
        List<String> list = SimpleQuery.list(new LambdaQueryWrapper<User>().eq(User::getName, "Mary"), User::getName, new Consumer<User>() {
            @Override
            public void accept(User user) {
                //map转化
               Optional.of(user.getName()).map(String::toLowerCase).ifPresent(user::setName);
            }
        });
        System.out.println(list);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    4.4.2map

    将所有的对象以id=实体类的方式封装为Map集合

    @Test
    void testMap() {
        Map<Long, User> map = SimpleQuery.keyMap(new LambdaQueryWrapper<User>(), User::getId);
        System.out.println(map);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    将单个对象以id=实体类的方式封装为Map集合

    @Test
    void testMap2() {
        Map<Long, User> map = SimpleQuery.keyMap(new LambdaQueryWrapper<User>().eq(User::getId, 1L), User::getId);
        System.out.println(map);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    由id和name组成的map

    @Test
    void testMap3() {
        Map<Long, String> map = SimpleQuery.map(new LambdaQueryWrapper<User>(), User::getId, User::getName);
        System.out.println(map);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    4.4.3Group
    @Test
        void testGroup() {
            Map<String, List<User>> maps = SimpleQuery.group(new LambdaQueryWrapper<User>(), User::getName);
            for (Map.Entry<String, List<User>> stringListEntry : maps.entrySet()) {
                System.out.println(stringListEntry);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 相关阅读:
    【HDR】Deep high dynamic range imaging of dynamic scenes
    python经典小游戏:24速算(案例)
    学Python的漫画漫步进阶 -- 第十步
    做项目必读的vue3基础知识
    【苍穹外卖 | 项目日记】第一天
    SpringMVC与JavaConfig笔记整理
    mac版postman升级后数据恢复办法
    Pytorch 基于VGG-16的服饰识别(使用Fashion-MNIST数据集)
    (2022杭电多校三)1011.Taxi(曼哈顿最值+二分)
    公众号突破公司注册数量限制
  • 原文地址:https://blog.csdn.net/m0_71229255/article/details/132724173