对于一些数据量比较大的表,为了提高查询性能,我们一般将表拆分成多张表,常见的是根据数据量,按年分表或者按月分表;
分表虽然太高了查询性能,但是在查询的时候,如何才能查询执行分表数据呢,这里整理了一下,使用MybatisPlus设置动态表名的功能实现。
增加MyBatisPlus配置类
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
HashMap<String, TableNameHandler> map = new HashMap<>();
map.put("my_test", (sql, tableName) -> DynamicTableTreadLocal.INSTANCE.getTableName());
// 如果存在多张分表,则在下面添加多条记录
dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
return mybatisPlusInterceptor;
}
}
方法解析
MybatisPlusInterceptor
该插件是核心插件,目前代理了 Executor#query
和 Executor#update
和 StatementHandler#prepare
方法
方法属性
private List<InnerInterceptor> interceptors = new ArrayList<>();
InnerInterceptor
接口
目前已有的功能:
- 自动分页:
PaginationInnerInterceptor
- 多租户:
TenantLineInnerInterceptor
- 动态表名:
DynamicTableNameInnerInterceptor
- 乐观锁:
OptimisticLockerInnerInterceptor
- sql 性能规范:
IllegalSQLInnerInterceptor
- 防止全表更新与删除:
BlockAttackInnerInterceptor
注意事项
使用多个功能需要注意顺序关系,建议使用如下顺序
- 多租户,动态表名
- 分页,乐观锁
- sql 性能规范,防止全表更新与删除
总结: 对 sql 进行单次改造的优先放入,不对 sql 进行改造的最后放入
增加本地动态线程设置表名
public enum DynamicTableTreadLocal {
INSTANCE;
private ThreadLocal<String> tableName = new ThreadLocal<>();
public String getTableName() {
return tableName.get();
}
public void setTableName(String tableName) {
this.tableName.set(tableName);
}
public void remove() {
tableName.remove();
}
}
DynamicTableTreadLocal
类的作用是为每个线程提供一个独立的tableName
变量。
每个线程可以通过调用getTableName
方法获取自己的tableName
变量的值,也可以通过调用setTableName
方法设置自己的tableName
变量的值。
这样可以保证每个线程都有自己独立的tableName
变量,不会相互干扰。
ThreadLocal在该类中的作用
ThreadLocal
是Java中的一个类,它提供了线程本地变量的功能。
在DynamicTableTreadLocal
类中,通过创建一个ThreadLocal
对象tableName
,每个线程都可以通过这个对象来获取和设置自己的tableName
变量。
ThreadLocal
保证了每个线程都有自己独立的变量副本,不会被其他线程访问或修改。
ThreadLocal应用场景
ThreadLocal
的应用场景主要是在多线程环境下,需要为每个线程维护独立的变量副本的情况下使用。
例如,在Web应用中,每个请求都会被分配给一个线程进行处理,如果需要在处理请求的过程中保存一些状态信息,可以使用ThreadLocal
来实现。
另外,ThreadLocal
还可以用于线程池、数据库连接池等场景,保证每个线程都有自己独立的资源。
表对应的的实体类配置
@Data
@TableName("my_test")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MyTest implements Serializable {
private static final long serialVersionUID = -65257563878624647L;
@TableId("uuid")
private String uuid;
@JsonProperty("user_name")
@TableField("user_name")
private String userName;
}
在表对应的的实体类上,只需要配置没有分年月的表名@TableName("my_test")
即可,无需指定分年月的表名;
具体通过MyBatisPlusConfig
类,根据DynamicTableTreadLocal
类设置的表名自动切换要操作的表名
动态切换表的具体使用
设置表名
DynamicTableTreadLocal.INSTANCE.setTableName("mytest_202401");
获取表名
DynamicTableTreadLocal.INSTANCE.getTableName();
删除表名
DynamicTableTreadLocal.INSTANCE.remove();
最后总结
文章参考MyBatis-Plus
官方文档,根据具体业务需求修改实现
官方文档地址
https://baomidou.com/
动态表名插件
https://gitee.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-dynamic-tablename