MybatisPlus分表查询
两种基础分表逻辑按日期分表这种方式通常会在表名的最后加上年月日,主要适用于按日期划分的统计数据或操作记录。在线实时展示的只有最近表中的数据,其他数据用于离线统计等。/*** 按天分表解析*/public class DaysTableNameParser implements TableNameHandler {@Overridepublic String dynamicTableName(Str
·
按日期分表
这种方式通常会在表名的最后加上年月日,主要适用于按日期划分的统计数据或操作记录。在线实时展示的只有最近表中的数据,其他数据用于离线统计等。
/**
* 按天分表解析
*/
public class DaysTableNameParser implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
String dateDay = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
return tableName + "_" + dateDay;
}
}
按id取模分表
这种方式需要一个id生成器,例如snowflake id或分布式id服务。它保证了相同id的数据都在一张表中,主要适用于保存用户基础信息,系统中的资源信息,购买记录等。当然这种分表方式扩展性较差,后期数据持续增多后需要按id大小分库再分表处理。
/**
* 按id取模分表处理器
*/
public class IdModTableNameParser implements TableNameHandler {
private Integer mod;
//使用ThreadLocal防止多线程相互影响
private static ThreadLocal<Integer> id = new ThreadLocal<Integer>();
public static void setId(Integer idValue) {
id.set(idValue);
}
IdModTableNameParser(Integer modValue) {
mod = modValue;
}
@Override
public String dynamicTableName(String sql, String tableName) {
Integer idValue = id.get();
if (idValue == null) {
throw new RuntimeException("请设置id值");
} else {
String suffix = String.valueOf(idValue % mod);
//这里清除ThreadLocal的值,防止线程复用出现问题
id.set(null);
return tableName + "_" + suffix;
}
}
}
自定义分表
业务场景:
当前业务上需要把分表的表名存储再数据库中,既不使用时间分表,也不使用id取模方式。查询时都需要知道查的那种表。
@Configuration
public class MybatisPlusConfig {
/**
* 借助 ThreadLocal 讲数据库查询的表名存储起来
*/
private static ThreadLocal<String> myTableName = new ThreadLocal<>();
public static String getThreadLocal() {
return myTableName.get();
}
public static void setThreadLocal(String str) {
myTableName.set(str);
}
public static void removeThreadLocal() {
myTableName.remove();
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 取消MyBatis Plus的最大分页500条的限制
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setMaxLimit(100000L);
DynamicTableNameInnerInterceptor myDynamicTableNameInterceptor = new DynamicTableNameInnerInterceptor();
Map<String, TableNameHandler> tableNameHandlerMap = Maps.newHashMapWithExpectedSize(1);
// 从 ThreadLocal 中取出设置进来的表名,设置到map中,其中key是基本表
tableNameHandlerMap.put("user_", (sql, table) -> getThreadLocal());
myDynamicTableNameInterceptor.setTableNameHandlerMap(tableNameHandlerMap);
interceptor.addInnerInterceptor(myDynamicTableNameInterceptor);
return interceptor;
}
}
自定义拦截器逻辑:这里使用拦截器的作用是当代码中使用了threadLocal后,需要及时remove调threadLocal,不然可能导致内存泄漏。参考:ThreadLocal的内存泄露?什么原因?如何避免? - 知乎
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 新增自定义拦截件器用于方法结束删除threadLocal中值
registry.addInterceptor(new MyInterception()).addPathPatterns("/**");
}
}
public class MyInterception implements HandlerInterceptor{
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
MybatisPlusConfig.removeThreadLocal();
}
}
通过查询数据库查询具体需要分表的表名并设置到threadLocal中
MybatisPlusConfig.setThreadLocal(tableName);
到此,可以任意使用mybatisPlus插件进行数据查询和修改,也可以自定义sql进行查询。
更多推荐
已为社区贡献1条内容
所有评论(0)