Mybatis-Plus版本:3.4.3.4

需求:在数据库中存在多个表结构相同,表名不同数据表 , 现需根据条件判断并选择操作哪一个数据表。在不使用动态表明的情况下, 一个表对应一个实体类,实体类又需要对应不同的Mapper、Service,明显增加了很多代码量。如果使用动态表名的方式,则只需要一个实体类就可以操作多张表,减少工作量,提升效率。

假设存在两个数据表(有些什么字段不重要,只要两个表字段相同就行):data_01、data_60

image-20211227143526299
image-20211227143526299

已知两个表字段相同, 假设有一个id(Long),一个data(String), 然后随便造几条数据。

我们先在写好实体类并指定好表名的相同部分(不指定也行,Mybatis-plus会自动解析实体类的名字):

@Data
@TableName(value = "data")
public class Data {

    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String data;
}

随后编写对应的Mapper类

@Mapper
public interface DataMapper extend BaseMapper<Data> {

}

再编写RequestDataHelper工具类(这个类用于操作一个只有当前线程能访问到的对象,来自于MybatisPlus官方示例代码):

public class RequestDataHelper {
    /**
     * 请求参数存取
     */
    private static final ThreadLocal<Map<String, Object>> REQUEST_DATA = new ThreadLocal<>();

    /**
     * 设置请求参数
     *
     * @param requestData 请求参数 MAP 对象
     */
    public static void setRequestData(Map<String, Object> requestData) {
        REQUEST_DATA.set(requestData);
    }

    /**
     * 获取请求参数
     *
     * @param param 请求参数
     * @return 请求参数 MAP 对象
     */
    public static <T> T getRequestData(String param) {
        Map<String, Object> dataMap = getRequestData();
        if (CollectionUtils.isNotEmpty(dataMap)) {
            return (T) dataMap.get(param);
        }
        return null;
    }

    /**
     * 获取请求参数
     *
     * @return 请求参数 MAP 对象
     */
    public static Map<String, Object> getRequestData() {
        return REQUEST_DATA.get();
    }
}

这个类中使用到了一个叫做ThreadLocal的类,这个类维护了一个只有当前线程能访问到的对象, 此处我们用来存取一个Map。

最后我们编写MybatisPlus的配置类:

@Configuration
public class MybatisPlusConfig {
    
     @Bean
     public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 动态表名拦截器
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
            // 获取参数方法
            Map<String, Object> paramMap = RequestDataHelper.getRequestData();
            String rate = (String) paramMap.get("rate");
            return tableName + "_" + rate;
        });
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
        return interceptor;
    }
    
}

可以看到其中使用 DynamicTableNameInnerInterceptor 类创建了一个动态表名拦截器, 随后调用setTableNameHandler 方法设置一个表名处理器 TableNameHandlerTableNameHandler 是一个interface接口,返回值是最终的表名,这里直接使用了lambda表达式来实现了这个接口。可以看到这里调用了RequestDataHelper.getRequestData() 这个方法去获取到了一个Map, 这个map就是我们用ThreadLocal维护的那个map,这个接口的实现就是根据map中我们设置的字段来拼接一个新的表名并返回。

测试,编写一个测试方法, 测试从data_01中查询数据:

@Test
public void test(){
    
    @Autowired
    private DataMapper dataMapper;
    
    RequestDataHelper.setRequestData(new HashMap<>() {{
            put("rate", "01");
        }});
    dataMapper.selectList(new QueryWrapper<>());
}

最终,测试通过,可以得到正确的数据。整个流程也就是在执行增删改查之前使用RequestDataHelper.setRequestData 来设置一个参数, 然后根据RequestDataHelper.getRequestData这个方法来获取到这个参数拼接出最后的表名。