一、应用场景当你使用Mybatis进行开发时,经常会遇到这样一种情况:将数据按照月份放在不同的表中,查询数据,您需要查询不同月份的不同表。但是我们都知道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需要操作多个数据库分表。这种情况在Mybatis下应该如何操作plus下的数据呢?其实方法有很多,我把我在实践中总结出的最佳方案给大家讲解一下。2、动态表名处理器接口的实现为了处理上面提到的类似问题,mybatisplus提供了动态表名处理器接口TableNameHandler。我们只需要实现该接口,并使该接口的应用配置生效,即可实现动态表名。需要注意的是,在mybatisplus3.4版本之前,动态表名handler接口为ITableNameHandler,需要配合mybatispluspagingplugin使用才能生效。我们这里只介绍3.4版本之后的实现。这个方法在mybatisplus3.4.3.2被废除了:dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);如果看到这样实现的动态表名,也是一种过时的实现方式,在新版本中已经被删除了。经过一段时间的实践和总结,我的实现类如下(基于mybatisplus3.4.3.2之后的版本):爪哇。util.List;/***由月份参数组成的动态表名*/publicclassMonthTableNameHandlerimplementsTableNameHandler{//用于记录哪些表可以使用月份的动态表名handler(即哪些表被分为按月列出的表格)私人列表tableNames;//构造函数,在构造动态表名处理器时,传递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表。这说明我们的动态表名实现成功了。欢迎关注我的公告号:字母哥杂谈,回复003送作者专栏《docker修炼之道》30多篇优质docker文章PDF版。Antetokounmpo博客:zimug.com