MyBatis-Plus动态表名插件使用

一、MyBatis-Plus动态表名插件使用

官方文档-动态表名插件:https://baomidou.com/pages/2a45ff/#dynamictablenameinnerinterceptor

官方有demo,使用起来还是蛮简单的。

1、DynamicTableNameInnerInterceptor插件源码

DynamicTableNameInnerInterceptor:原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换。

重点看 changeTable方法。

  • TableNameHandler tableNameHandler是一个接口。使用动态表名插件时,必须要有 TableNameHandler的实现类。

在这里插入图片描述

2、使用

模拟使用场景:

一个 entity 对应多张表(多张表结构一致,只有表名称不同),在使用时,可以动态映射表名称。
比如:按照时间分表,某些业务冷热数据分离后数据存在不同的表中等。根据自定义的算法找到我们需要查询的表名。

下面我通过参数的方式传入动态表名来操作。

2.1 请求参数动态表名传递辅助类

/**
 * 请求参数动态表名传递辅助类
 */
public class RequestDynamicTableNameHelper {

	/**
	 * 请求参数存取(表名)。请求参数自定义,官方Demo定义为ThreadLocal<Map<String, Object>>
	 */
	private static final ThreadLocal<String> 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<DynamicDemoDO> {

    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<DynamicDemoMapper, DynamicDemoDO> 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<DynamicDemoDO> dynamicDemoList) {
        int insert = userMapper.insert(userDO);

        for (DynamicDemoDO dynamicDemoDO : dynamicDemoList) {
            if(StringUtils.isNotBlank(dynamicTableName)){
                RequestDynamicTableNameHelper.setRequestData(dynamicTableName);
            }
            dynamicDemoService.save(dynamicTableName, dynamicDemoDO);
        }
    }

– 求知若饥,虚心若愚。

  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Mybatis-Plus是Mybatis的增强工具,在开发中有很多使用场景,其中就包括动态表名插件,也就是说,在Mybatis-Plus中,动态表名插件可以实现一个SQL语句的多表关联查询。 动态表名插件Mybatis-Plus的一种增强插件,在查询过程中,通过钩子的方式,动态生成SQL语句,以实现动态表名的查询。这种方法不仅可以实现多表关联查询,而且还能避免SQL注入攻击。 使用动态表名插件,需要在Mybatis-Plus的配置文件中添加相应的配置,具体操作步骤如下: 1.在pom.xml文件中引入Mybaits-Plus和相关的依赖。 ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.2</version> </dependency> ``` 2.在application.yml文件中添加Mybatis-Plus相关的配置信息。 ```yml mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.example.model configuration: map-underscore-to-camel-case: true sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 ``` 3.编写Mapper.xml文件,使用动态表名插件。 ```xml <select id="findUserByName" resultMap="user"> SELECT * FROM ${tableName} WHERE name = #{name} </select> ``` 以上就是使用动态表名插件实现多表关联查询的方法,需要注意的是,在使用动态表名插件时,需要特别小心SQL注入攻击。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值