当前位置: 首页 > Web前端 > JavaScript

数据分表Mybatis Plus动态表名最优方案的探索

时间:2023-03-26 20:23:42 JavaScript

数据分表优化方案探索MybatisPlus动态表名有一种情况:数据按照月份放在不同的表中,查询数据的时候需要查询不同月份的不同表。但是我们都知道Mybatis是一个ORM持久层框架,即:实体关系映射,实体对象和数据库表之间存在一对一的映射关系。例如:@DatapublicclassStudent{privateIntegerid;私有字符串stuName;privateIntegerage;}表结构CREATETABLE`student`(`id`INT(11)NOTNULLAUTO_INCREMENT,`stu_name`VARCHAR(64)NOTNULLDEFAULT'0'COMMENT'name',`age`INT(11)NOTNULLCOMMENT'age',PRIMARYKEY(`id`))COMMENT='studenttable'COLLATE='utf8_general_ci'ENGINE=InnoDB;Student实体类和student表是一对一的关系。如果我们要将student表按照月份分表,比如:student_202206,student_202207,student_202208,会生成“一个实体类,它的Mapper需要操作多个数据库分表。这种情况在我们应该如何处理在Mybatisplus下操作数据?”其实方法有很多,我把自己实践中总结出的最优解给大家讲解一下。2、动态表名处理器接口的实现为了处理上述类似问题,mybatisplus提供了一个动态表名处理器接口TableNameHandler,我们只需要实现这个接口并应用这个接口的配置即可接口生效实现动态表名。需要注意的是:ITableNameHandler经过一段时间的实践和总结,我的实现类如下(基于mybatisplus3.4.3.2之后的版本):importcom.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;importjava.util.Arrays;importjava.util.List;/***根据月份的参数组成动态表名*/publicclassMonthTableNameHandlerimplementsTableNameHandler{//用来记录哪些表可以使用月份的动态表名处理器(即按月份划分哪些表)privateListtableNames;//构造函数,在构造动态表名handler时,传递tableNames参数publicMonthTableNameHandler(String...tableNames){this.tableNames=Arrays.asList(tableNames);}//每个请求线程维护一个月的数据,避免多线程数据冲突。所以使用ThreadLocalprivatestaticfinalThreadLocalMONTH_DATA=newThreadLocal<>();//设置请求线程的月份数据publicstaticvoidsetData(Stringmonth){MONTH_DATA.set(month);}//删除当前请求线程月份数据publicstaticvoidremoveData(){MONTH_DATA.remove();}//动态表名接口实现方法@OverridepublicStringdynamicTableName(Stringsql,StringtableName){if(this.tableNames.contains(tableName)){returntableName+"_"+MONTH_DATA.get();//给表名加上月份后缀}else{returntableName;//原样返回表名}}}上面的代码大家已经有了基本的了解,看了下面的测试过程再回头看上面代码中的注释就更容易理解了。表名处理器写好后,我们需要进行如下配置使其生效。配置内容可以像葫芦画画一样。我已经为需要注意的部分添加了注释。@Configuration@MapperScan("com.zimug")publicclassMybatisPlusConfig{@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptor拦截器=newMybatisPlusInterceptor();DynamicTableNameInnerInterceptordynamicTableNameInnerInterceptor=newDynamicTableNameInnerInterceptor();dynamicTableNameInnerInterceptor.setTableNameHandler(//可以传多个表name参数指定哪些表使用MonthTableNameHandler处理表名newMonthTableNameHandler("student","teacher"));//将表名处理为拦截器interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);//可以传多个拦截器,即:可以传多个表名处理器TableNameHandler//interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);返回拦截器;}}3.测试实现效果首先创建一个StudentMapper。StudentMapper默认只能操作student表,不能操作student_YYYYMM表。@MapperpublicinterfaceStudentMapperextendsBaseMapper{}接下来我们来写一个单元测试用例。测试用例的测试函数模拟一个请求访问的Controller或服务函数。@SpringBootTestclassDynamicTableNameTest{@ResourceprivateStudentMapperstudentMapper;@Testvoidtest(){//设置进行数据操作之前的月份(实际场景中,该参数是从请求参数中解析出来的)MonthTableNameHandler.setData("202208");studentMapper.selectById(1);//查询id=2的student_202208表//读取后烧录,移除ThreadLocal当前请求线程的数据MonthTableNameHandler.removeData();}}当我们执行这个单元测试用例时,发现控制台打印出如下信息,注意SQL部分,真的是查询student_202208表,而不是student表。这说明我们的动态表名实现成功了。