当前位置: > > > SpringBoot - MyBatis-Plus使用详解21(动态表名)

SpringBoot - MyBatis-Plus使用详解21(动态表名)

二十一、动态表名

1,业务场景

(1)有时我们希望存储或读取数据的时候能够根据情况、或根据传入参数来动态的选择对应的表。比如我们数据库里的用户信息表通过年份拆分(使用年份做后缀):

(2)而对应的实体类是不包含年份后缀的:
@Data
public class UserInfo {
    private Integer id;
    private String userName;
    private String passWord;
    private Integer age;
}

2,样例代码

(1)首先我们实现 ITableNameHandler 接口注入到 DynamicTableNameParser 处理器链中,将动态表名解析器注入到 MP 解析链(这里我们获取当前年份作为 user_info 表后缀)。
提示:动态表名的原理就是解析替换设定表名为处理器的返回表名。
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
        dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
            put("user_info", (metaObject, sql, tableName) -> {
                // metaObject 可以获取传入参数,这里实现你自己的动态规则
                // 这里我使用当前年份做后缀
                int year = Calendar.getInstance().get(Calendar.YEAR);
                return tableName + "_" + year;
            });
        }});
        paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
        return paginationInterceptor;
    }
}

(2)然后开始查询数据:
@RestController
public class HelloController {

    @Autowired
    UserInfoMapper userInfoMapper;

    @GetMapping("/test")
    public List<UserInfo> test() {
        return userInfoMapper.selectList(null);
    }
}

(3)查看控制台日志可以发现实际查询的是 user_info_2020 这张表:

附:根据参数来动态选择对应的表

(1)这里我们对动态表名解析器代码稍作修改,根据传入的参数 year 来动态决定使用的数据表:
  • 比如当 year 参数为 2019 则使用 user_info_2019 这张表。
  • 如果没有 year 参数则使用当前年份做后缀,比如 user_info_2020
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
        dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
            put("user_info", (metaObject, sql, tableName) -> {
                // 获取传入参数 year,如果有的话做为后缀,没有的话则使用当前年份作为后缀
                Object param = getParamValue("year", metaObject);
                String year = param !=null ? String.valueOf(param)
                        : String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
                return tableName + "_" + year;
            });
        }});
        paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
        return paginationInterceptor;
    }

    /**
     * 获取参数值
     */
    private Object getParamValue(String title, MetaObject metaObject){
        //获取参数
        Object originalObject = metaObject.getOriginalObject();
        JSONObject originalObjectJSON = JSON.parseObject(JSON.toJSONString(originalObject));
        JSONObject boundSql = originalObjectJSON.getJSONObject("boundSql");
        try {
            JSONObject parameterObject = boundSql.getJSONObject("parameterObject");
            return parameterObject.get(title);
        }catch (Exception e) {
            return null;
        }
    }
}

(2)修改 UserInfoMapper 接口,在定义方法中添加 year 参数:
注意:如果方法只有一个参数必须添加 @Param 注解,否则动态表名解析器中无法通过参数名获取对应的值。
public interface UserInfoMapper extends BaseMapper<UserInfo> {
    @Select("select * from user_info")
    List<UserInfo> getAll(@Param("year") int year);
}

  • 如果方法有多个参数的话,可以不用添加 @Param 注解:
public interface UserInfoMapper extends BaseMapper<UserInfo> {
    @Select("select * from user_info")
    List<UserInfo> getAll(int year, int month);
}

(3)我们调用这个 Mapper 查询时传入一个指定年份,比如 2019
@RestController
public class HelloController {

    @Autowired
    UserInfoMapper userInfoMapper;

    @GetMapping("/test")
    public List<UserInfo> test() {
        return userInfoMapper.getAll(2019);
    }
}

(4)查看控制台日志可以发现实际查询的是 user_info_2019 这张表:
评论0