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);
