前言
之前项目中由于几张表的数据量特别的大,在查询的时候特别耗时,所以决定对其进行分表处理。考虑到项目中用的MyBatis作为ORM框架,所以便决定使用它的插件功能来实现动态的替换查询的表来实现分表功能。
功能实现
整体的目录结构如下
TableShard :注解,用来标记需要分表的接口或者类
几个参数作为为
- enable:是否开启分表功能,默认为true
- tableNamePrefix:原表名
- value:如果是从参数中获取表后缀则为参数名,如果直接指定则为表后缀。
- filed:是否从参数中获取值(比如我们根据id来查询用户,我们的表如果是根据id来进行分表的,那么我们就应该通过id计算出需要路由的表,则需要从查询参数中获取值)
- shardStrategy:分表策略,我提供了两个分表策略一个是根据日期来分表,一个是根据id来分表。
ShardingStrategy:分表策略
为一个接口,定义了通过表前缀以及value值来分表的方法
AbstractShardingStrategy:提供了一个校验表名的方法,用于扩展。
DateShardingStrategy:根据日期来进行分表的,如果不提供具体的日期,则默认当前年月
IDShardingStrategy:根据ID来进行分表,默认分为3张表根据id来取模来。
ShardInterceptor:MyBatis插件的实现,需要实现Interceptor接口,重写intercept方法。它本质上是一个拦截器,我们需要在@Intercepts 注解中标注好我们需要拦截类已经方法跟方法的参数。
StatementHandler负责处理MyBatis与JDBC之间Statement的交互,通俗而言就是负责操作Statement对象与数据库之间的交互。prepare方法用于创建一个具体的 Statement 对象的实现类或者是 Statement 对象。
getMetaObject(Invocation: Invocation):获取是MetaObject对象,MetaObject 是 Mybatis 反射工具类,通过 MetaObject 获取和设置对象的属性值
MetaObject主要方法
方法 | 说明 |
---|---|
hasGetter(name) | 判断是否有属性 name 或 name 的 getter 方法。1. 若定义 userId,没定义 getUserId() 方法,hasGetter("userId") 则返回 true;2. 若定义方法 getUserId1(),没定义属性 userId1,hasGetter("userId1") 则返回 true。 |
getGetterNames() | 获取含有 getter 相关的属性名称。1. 若定义 userId,没定义 getUserId() 方法,则 userId 会被返回;2. 若定义方法 getUserId1(),没定义属性 userId1, 则 userId1 会被返回。 |
getGetterType(name) | 获取 getter 方法的返回类型。 |
getValue(name) | 获取属性值。 |
hasSetter(name) | 判断是否有属性 name 或 name 的 setter 方法。1. 若定义 userId,没定义 setUserId(userId) 方法,hasSetter("userId") 则返回 true;2. 若定义方法 setUserId1(userId1),没定义属性 userId1,hasSetter("userId1") 则返回 true。 |
getSetterNames() | 获取含有 setter 相关的属性名称。1. 若定义 userId,没定义 setUserId(userId) 方法,则 userId 会被返回;2. 若定义方法 setUserId1(userId1),没定义属性 userId1, 则 userId1 会被返回。 |
getSetterType(name) | 获取 setter 方法的参数类型。 |
setValue(name, value) | 设置属性值。 |
tableShard( mappedStatement: MappedStatement):mapper方法上的分表注解,如果方法上没有则从类上获取。
replaceSql(tableShard: TableShard, metaObject: MetaObject, sql: String):用于替换原本sql,通过获取到的分表注解然后获取里面的属性,根据属性执行不同的分表策略来生成新的表名,然后再替换原表名实现分表的功能。
自此分表插件就开发完成了,我们简单测试下。
测试
想要使用这个插件首先我们需要在配置文件中应用下。
我们在UserMapper中定义三相同的方法
第一个根据日期来分表,并且我们没有指定日期,则默认查询当前年月的(user_202211),第二个根据id来分表(user_1),第三个查询原表(user)
测试发现我们的分表插件成功运行了