MyBatis-plus高级应用之主键策略与分页插件

MyBatis-plus高级应用

主键策略

在MyBatis-plus中使用@TableId注解来设置主键生成策略。

在设置好自增策略后,在新增数据时,就不用给主键属性赋值了,数据库会自动生成主键的值。

auto策略

该策略为跟随数据库表的主键策略,如果数据库表的主键设置为自增,那么主键策略就是自增。

public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

input策略

该策略表示,必须由我们手动设置id,否则无法添加数据。那么我们在新增数据时,就必须给主键赋值了。

public class User {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

ASSIGN_ID策略

当一个主键自增表的数据量很大时,需要对表进行拆分(水平分表或垂直分表),对于拆分后的数据,有三点需求:

  1. 之前表的主键有序,拆分后也要有序
  2. 虽然做了拆分,但每条数据还要保证主键唯一
  3. 主键不要直接暴露数据的数量,这样容易泄露关键信息

为了满足这三个需求,因此需要使用雪花算法。

雪花算法把一个 64 位的长整型数字(long 类型)拆分成不同 “段位”,分别存储时间戳、机器标识、序列号等信息,通过分段管理确保 ID 全局唯一,且因时间戳在前,ID 整体呈递增趋势。

雪花算法的核心结构(64 位分段):

位段 位数 作用
符号位 1 位 固定为 0(因为 ID 是正数,负数无意义)
时间戳位 41 位 存储相对时间戳(如相对于某个固定起始时间的毫秒数),可支持约 69 年的时间范围
机器 / 数据中心位 10 位 拆分(如 5 位数据中心 + 5 位机器 ID),最多支持 1024 个节点(2^10)
序列号位 12 位 同一毫秒内,同一机器生成的 ID 序号,支持每毫秒生成 4096 个 ID(2^12)
public class User {
    // 主键生成策略是雪花算法
    // 64位二进制最终转换为19位十进制,所以最后生成的id长度是19位
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

NONE策略

NONE策略表示不指定主键生成策略,此时会跟随全局生成策略。当不指定策略时也会跟随全局策略。

那如何设置全局生成策略呢?

在springboot的配置文件中设置如下:

mybatis-plus:
  global-config:
    banner: false
    db-config:
    # 设置全局的主键生成策略。如果不指定,框架默认是assign_id策略
      id-type: assign_id

ASSIGN_UUID策略

使用uuid作为主键的一种策略。

public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    private Integer age;
    private String email;
}

分页插件

分页的本质就是设置一个拦截器,通过拦截器拦截sql,通过在sql语句末尾添加limit关键字来实现分页效果。

具体配置步骤如下:

  1. 通过配置类指定一个具体数据库的分页插件,因为具体的数据库方言不同,分页语句也不同。

    @Configuration
    @MapperScan("com.ali.mapper")
    public class MybatisPlusConfig {
        /**
         * 添加分页插件
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
            // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
            return interceptor;
        }
    }
    

这里需要注意,笔者之前引入的mybatis-plus依赖里找不到MybatisPlusInterceptor类,需要引入其他版本的依赖:

   <!--mybatis-plus依赖-->
<!--    这个依赖不包含分页插件
<dependency>
       <groupId>com.baomidou</groupId>
       <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
       <version>3.5.14</version>
   </dependency>-->
 <!--  这个依赖包含分页插件-->
   <dependency>
       <groupId>com.baomidou</groupId>
       <artifactId>mybatis-plus-boot-starter</artifactId>
       <version>3.4.3.4</version>
   </dependency>

自定义分页插件

某些场景下,需要自定义sql语句来查询,此时就需要自定义分页查询。

  1. 编写Mapper接口

    @Mapper
    public interface UserMapper extends BaseMapper<User> {
        IPage<User> selectByName(IPage<User> page, String name);
    }
    
  2. 编写映射文件

    <mapper namespace="com.ali.mapper.UserMapper">
        <select id="selectByName" resultType="com.ali.domain.User">
            SELECT * FROM user where name = #{name}
        </select>
    </mapper>
    
  3. 调用测试

    void testServiceCRUD(){
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // Page类是IPage接口的实现类
        // 指定分页对象
        IPage<User> userPage = new Page<>(1,3);
        // 执行查询,查询结果已经封装到userPage对象中了
        userMapper.selectByName(userPage, "张三");
        // 获取分页查询信息
        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());
    }
    

ActiveRecord模式

ActiveRecord(活动记录,简称AR),是一种领域模型模式,特点是一个模型类对应数据库中一张表,模型类的一个实例对应表中的一条记录。这种模式只需围绕一个数据对象进行CRUD操作

实现ActiveRecord的步骤:

  1. 让实体类继承Model

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User extends Model<User> {
        @TableId
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    

Model类中提供了一些增删改查的方法。

User user = new User();
user.setAge(13);
user.setId(1L);
user.setName("test");
// 新增
user.insert();
User user2 = new User();
user2.setId(2L);
// 删除
user2.deleteById();
User user3 = new User();
user3.setAge(13);
user3.setId(4L);
user3.setName("test");
// 更新
user3.updateById();
User user4 = new User();
user4.setId(8L);
// 单条查询
User selecteUser = user4.selectById();
System.out.println(selecteUser);

SimpleQuery工具类

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

注意:使用SimpleQuery工具类 需要mybatis-plus 3.5.x 版本以上。

LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "张三");
List<Long> listIds = SimpleQuery.list(lambdaQueryWrapper, User::getId);
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "张三");
// 返回的name值是全大写。
List<String> list = SimpleQuery.list(lambdaQueryWrapper, User::getName, new Consumer<User>() {
    @Override
    public void accept(User user) {
                    // 获取name的值
        Optional.of(user.getName())
                // 将那么的值转化为全大写
                .map(String::toUpperCase)
                // 重新设置到name属性上
                .ifPresent(user::setName);
    }
});

有一个需求:结果返回一个Map ,要求key对对象的id,实体类对象为key的value。

LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "张三");
Map<Long, User> longUserMap = SimpleQuery.keyMap(lambdaQueryWrapper, User::getId);

需求2:将查询结果的任意两列作为map的key和value。

LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "张三");
// key存id ,value存name
Map<Long, User> longUserMap1 = SimpleQuery.keyMap(lambdaQueryWrapper, User::getId, User::getName);

SimpleQuery的分组方法:

LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "张三");
// key存name,以name进行分组,value存name值对应的分组数据集
Map<String, List<User>> longUserMap1 = SimpleQuery.group(lambdaQueryWrapper,User::getName);
posted @ 2026-03-11 15:04  NE_STOP  阅读(28)  评论(0)    收藏  举报