MyBatis-plus如何设置主键策略和分页插件实现高效查询?

摘要:MyBatis-plus高级应用 主键策略 在MyBatis-plus中使用@TableId注解来设置主键生成策略。 在设置好自增策略后,在新增数据时,就不用给主键属性赋值了,数据库会自动生成主键的值。 auto策略 该策略为跟随数据库表的
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策略 当一个主键自增表的数据量很大时,需要对表进行拆分(水平分表或垂直分表),对于拆分后的数据,有三点需求: 之前表的主键有序,拆分后也要有序 虽然做了拆分,但每条数据还要保证主键唯一 主键不要直接暴露数据的数量,这样容易泄露关键信息 为了满足这三个需求,因此需要使用雪花算法。 雪花算法把一个 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关键字来实现分页效果。 具体配置步骤如下: 通过配置类指定一个具体数据库的分页插件,因为具体的数据库方言不同,分页语句也不同。 @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语句来查询,此时就需要自定义分页查询。 编写Mapper接口 @Mapper public interface UserMapper extends BaseMapper<User> { IPage<User> selectByName(IPage<User> page, String name); } 编写映射文件 <mapper namespace="com.ali.mapper.UserMapper"> <select id="selectByName" resultType="com.ali.domain.User"> SELECT * FROM user where name = #{name} </select> </mapper> 调用测试 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的步骤: 让实体类继承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);