一、MyBatis-Plus动态表名插件使用
官方文档-动态表名插件:https://baomidou.com/pages/2a45ff/#dynamictablenameinnerinterceptor
官方有demo,使用起来还是蛮简单的。
1、DynamicTableNameInnerInterceptor插件源码DynamicTableNameInnerInterceptor:原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换。
重点看 changeTable方法。
- TableNameHandler tableNameHandler是一个接口。使用动态表名插件时,必须要有 TableNameHandler的实现类。
模拟使用场景:
一个 entity 对应多张表(多张表结构一致,只有表名称不同),在使用时,可以动态映射表名称。 比如:按照时间分表,某些业务冷热数据分离后数据存在不同的表中等。根据自定义的算法找到我们需要查询的表名。
下面我通过参数的方式传入动态表名来操作。
2.1 请求参数动态表名传递辅助类/**
* 请求参数动态表名传递辅助类
*/
public class RequestDynamicTableNameHelper {
/**
* 请求参数存取(表名)。请求参数自定义,官方Demo定义为ThreadLocal
*/
private static final ThreadLocal REQUEST_DATA = new ThreadLocal();
/**
* 设置请求参数
*
* @param requestData
* 请求参数-表名
*/
public static void setRequestData(String requestData) {
REQUEST_DATA.set(requestData);
}
/**
* 获取请求参数
*
* @return 请求参数-表名
*/
public static String getRequestData() {
return REQUEST_DATA.get();
}
/**
* 移除获取请求参数(表名)
*/
public static void remove() {
REQUEST_DATA.remove();
}
}
2.2 注入动态表名插件
MybatisPlusConfig配置中添加动态表名 DynamicTableNameInnerInterceptor插件。
使用多个功能插件时注意顺序关系,官方建议使用如下顺序:
- 多租户,动态表名
- 分页,乐观锁
- sql 性能规范,防止全表更新与删除
总结:对 sql 进行单次改造的优先放入,不对 sql 进行改造的最后放入。
@Configuration
public class MyBatisPlusConfig {
/**
* 注册插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//1.添加动态表名插件
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> { //匿名内部类
String requestTableName = RequestDynamicTableNameHelper.getRequestData();
// 如果不为空,使用动态表名。
if(StringUtils.isNotBlank(requestTableName)){
RequestDynamicTableNameHelper.remove();
return requestTableName;
}
return tableName;
});
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
// 2.添加分页插件
PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
// 设置数据库方言类型
pageInterceptor.setDbType(DbType.MYSQL);
// 下面配置根据需求自行设置
// 设置请求的页面大于最大页后操作,true调回到首页,false继续请求。默认false
pageInterceptor.setOverflow(false);
// 单页分页条数限制,默认无限制
pageInterceptor.setMaxLimit(500L);
interceptor.addInnerInterceptor(pageInterceptor);
//3.乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
2.3 service类
创建三个表:
DROP TABLE IF EXISTS `t_dynamic_demo`;
CREATE TABLE t_dynamic_demo
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
DROP TABLE IF EXISTS `t_dynamic_demo_1`;
CREATE TABLE t_dynamic_demo_1
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
DROP TABLE IF EXISTS `t_dynamic_demo_2`;
CREATE TABLE t_dynamic_demo_2
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
mapper和实体类与之前的单表写法是一样。这里只需要生成 t_dynamic_demo表的代码即可。
然后在 service类中我们加入 动态表名请求的方法即可。
public interface DynamicDemoService extends IService {
void save(String dynamicTableName, DynamicDemoDO dynamicDemoDO);
void updateById(String dynamicTableName, DynamicDemoDO dynamicDemoDO);
void deleteById(String dynamicTableName, Long id);
DynamicDemoDO getById(String dynamicTableName, Long id);
}
@Service
public class DynamicDemoServiceImpl extends ServiceImpl implements DynamicDemoService {
@Autowired
private DynamicDemoMapper dynamicDemoMapper;
@Override
public void save(String dynamicTableName, DynamicDemoDO dynamicDemoDO) {
//将动态表名放到请求参数中(表名)
if(StringUtils.isNotBlank(dynamicTableName)){
RequestDynamicTableNameHelper.setRequestData(dynamicTableName);
}
//和以前使用一样
dynamicDemoMapper.insert(dynamicDemoDO);
}
@Override
public void updateById(String dynamicTableName, DynamicDemoDO dynamicDemoDO) {
if(StringUtils.isNotBlank(dynamicTableName)){
RequestDynamicTableNameHelper.setRequestData(dynamicTableName);
}
dynamicDemoMapper.updateById(dynamicDemoDO);
}
@Override
public void deleteById(String dynamicTableName, Long id) {
if(StringUtils.isNotBlank(dynamicTableName)){
RequestDynamicTableNameHelper.setRequestData(dynamicTableName);
}
dynamicDemoMapper.deleteById(id);
}
@Override
public DynamicDemoDO getById(String dynamicTableName, Long id) {
if(StringUtils.isNotBlank(dynamicTableName)){
RequestDynamicTableNameHelper.setRequestData(dynamicTableName);
}
return dynamicDemoMapper.selectById(id);
}
}
2.4 测试类
@Autowired
private DynamicDemoService dynamicDemoService;
@Test
public void testSave(){
DynamicDemoDO dynamicDemoDO = new DynamicDemoDO();
dynamicDemoDO.setName("dynamicDemoDO");
dynamicDemoDO.setAge(18);
dynamicDemoDO.setEmail("setEmail 111");
dynamicDemoService.save(dynamicDemoDO);
/**
* 使用动态表名时,数据库的id自增,同一个请求不能同时插入同一个表的两条记录。
* Duplicate entry '1' for key 't_dynamic_demo.PRIMARY'
*/
//dynamicDemoService.save(dynamicDemoDO);
dynamicDemoDO.setName("dynamicDemoDO_111");
dynamicDemoService.save("t_dynamic_demo_1", dynamicDemoDO);
dynamicDemoDO.setName("dynamicDemoDO_222");
dynamicDemoService.save("t_dynamic_demo_2", dynamicDemoDO);
}
@Test
public void testUpdateById(){
DynamicDemoDO dynamicDemoDO = new DynamicDemoDO();
dynamicDemoDO.setId(1L);
dynamicDemoDO.setName("dynamicDemoDO_11111_update");
dynamicDemoDO.setEmail("setEmail 111update");
dynamicDemoService.save("t_dynamic_demo_1", dynamicDemoDO);
}
@Test
public void testDeleteById(){
dynamicDemoService.getById("t_dynamic_demo_1", 1L);
}
@Test
public void testGetById(){
DynamicDemoDO t_dynamic_demo_1 = dynamicDemoService.getById("t_dynamic_demo_1", 1L);
DynamicDemoDO t_dynamic_demo_2 = dynamicDemoService.getById("t_dynamic_demo_2", 1L);
System.out.println("t_dynamic_demo_1 ->" + t_dynamic_demo_1);
System.out.println("t_dynamic_demo_2 ->" + t_dynamic_demo_1);
}
1)保存 2)获取
注意:上面测试时,连续保存两条记录,报错了。但是我在使用下面方式时操作OK的。
@Override
public void batchSaveDynamicDemo(String dynamicTableName, UserDO userDO, List dynamicDemoList) {
int insert = userMapper.insert(userDO);
for (DynamicDemoDO dynamicDemoDO : dynamicDemoList) {
if(StringUtils.isNotBlank(dynamicTableName)){
RequestDynamicTableNameHelper.setRequestData(dynamicTableName);
}
dynamicDemoService.save(dynamicTableName, dynamicDemoDO);
}
}
– 求知若饥,虚心若愚。