Mybatis-Plus动态表名的实现示例

 更新时间:2024年07月10日 11:34:24   作者:Aiir  
面对复杂多变的业务需求,动态表名的处理变得愈发重要,本文主要介绍了Mybatis-Plus动态表名的实现示例,具有一定的参考价值,感兴趣的可以了解一下

前言

在某些情况下,需要将大量数据分散到多个数据表中,这样可以提高数据库的查询效率和数据处理能力。按天分表就是一种常见的数据分表策略,它将每天的数据放到一个独立的数据表中,可以方便地进行数据查询和备份。但是,为了实现按天分表需要动态表名的支持,即在创建表时根据当前日期自动生成表名。这样可以确保每天数据都存放在不同的表中,避免数据重复或覆盖的情况。同时,动态表名还可以帮助开发人员更方便地管理数据,例如删除过期数据表等。因此,使用动态表名是按天分表的必要条件之一。

例如:t_user_20230901、t_user_20230902、t_user_20230903…

一、方案一(动态传参)

使用方法(Mapper自定义SQL)

坐标依赖

<dependency>
	<groupId>com.baomidou</groupId>
	<artifactId>mybatis-plus-boot-starter</artifactId>
	<version>3.5.1</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.28</version>
</dependency>

数据源配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

实体类

@Data
@TableName("t_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField("name")
    private String name;
}

这里的@TableName("t_user")映射表名不需要带上日期后缀

Mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
	// 方法1
	@Select("Select * from ${tableName}")
	List<User> getUserInfo(@Param("tableName") String tableName);
	// 方法2
	List<User> getUserInfoMP(String tableName);
}

MapperXML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xx.xx.mapper.UserMapper">
 <select id="getUserInfoMP" resultType="com.xx.xx.entity.User" >
        select * from ${tableName} order by phone_number ASC
 </select>
</mapper>

具体使用

@Service
public class UserServiceImpl implements UserService {

	@Autowired
    private UserMapper userMapper;
	
	void query(){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String rDay = LocalDateTime.now().format(formatter);
		String tableName = "t_user_" + rDay;
		
		List<User> list1 = userMapper.getUserInfo(tableName);
		List<User> list2 = userMapper.getUserInfoMP(tableName);
	}

}

二、方案二(DynamicTableNameInnerInterceptor插件)

使用方法(插件配置+ThreadLocal+辅助类)

配置类

@Configuration
@MapperScan(basePackages = {"com.xx.**.mapper"})
public class MybatisPlusConfig {

    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //动态表名
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        //可以传多个表名参数,指定哪些表使用DayTableNameHandler处理表名称
        dynamicTableNameInnerInterceptor.setTableNameHandler(new DayTableNameHandler("t_user"));
		//以拦截器的方式处理表名称
		//可以传递多个拦截器,即:可以传递多个表名处理器TableNameHandler
        mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
        return mybatisPlusInterceptor;
    }
}

辅助类

/**
 * 按天参数,组成动态表名
 */
public class DayTableNameHandler implements TableNameHandler {
    //用于记录哪些表可以使用该动态表名处理器(即哪些表需要分表)
    private List<String> tableNames;
    //构造函数,构造动态表名处理器的时候,传递tableNames参数
    public DayTableNameHandler(String ...tableNames) {
        this.tableNames = Arrays.asList(tableNames);
    }
    //每个请求线程维护一个day数据,避免多线程数据冲突。所以使用ThreadLocal
    private static final ThreadLocal<String> DAY_DATA = new ThreadLocal<>();
    //设置请求线程的day数据
    public static void setData(String day) {
        DAY_DATA.set(day);
    }
    //删除当前请求线程的day数据
    public static void removeData() {
        DAY_DATA.remove();
    }
    //动态表名接口实现方法
    @Override
    public String dynamicTableName(String sql, String tableName) {
        if (this.tableNames.contains(tableName)){
            return tableName + "_" + DAY_DATA.get();  //表名增加后缀
        }else{
            return tableName;   //表名原样返回
        }
    }
}

具体使用

@Service
public class UserServiceImpl implements UserService {

	@Autowired
    private UserService userService;
	
	void query(){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String rDay = LocalDateTime.now().format(formatter);
        DayTableNameHandler.setData(rDay);
        List<User> list = userService.list();
        // 用完即销毁
        DayTableNameHandler.removeData();
	}
}

这里可以自行打印SQL语句验证 Select * from t_user_20230909

三、方案三(DynamicTableNameInnerInterceptor插件、省略辅助类)

这里可以直接配置DynamicTableNameInnerInterceptor插件做简单的使用,但是用法相对固定,不能根据实际情况控制实体类所映射的表名,原因是被统一拦截

使用方法(插件配置+ThreadLocal)

配置类

@Configuration
@MapperScan(basePackages = {"com.xx.**.mapper"})
public class MybatisPlusConfig {

	public static ThreadLocal<String> myTableName = new ThreadLocal<>();

    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //动态表名
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        HashMap<String, TableNameHandler> map = new HashMap<String, TableNameHandler>(2) {{
            put("t_user", (sql, tableName) -> {
                return myTableName.get();
            });
        }};
        dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);
        mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
		myTableName.remove();

        return mybatisPlusInterceptor;
    }
}

具体使用

@Service
public class UserServiceImpl implements UserService {

	@Autowired
    private UserService userService;
	
	void query(){
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String rDay = LocalDateTime.now().format(formatter);
        DayTableNameHandler.setData(rDay);
        String tableName = "t_user" + rDay;
        MybatisPlusConfig.myTableName.set(tableName);
        List<User> list = userService.list();        
	}
}

总结

三种方法可以结合实际需要选择,使用Mapper自定义SQL要注意SQL注入,使用线程池的方式要记得清理。

注意

Threadlocal 中的数据在AOP中最好自己释放掉 ,spring是用的线程池,如果不清理掉会影响线程下次使用的程序。

到此这篇关于Mybatis-Plus动态表名的实现示例的文章就介绍到这了,更多相关Mybatis-Plus 动态表名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于HTML5+js+Java实现单文件文件上传到服务器功能

    基于HTML5+js+Java实现单文件文件上传到服务器功能

    应公司要求,在HTML5页面上实现上传文件到服务器功能,对于我这样的菜鸟,真是把我难住了,最后还是请教大神搞定的,下面小编把例子分享到脚本之家平台,供大家参考
    2017-08-08
  • 使用spring的restTemplate注意点

    使用spring的restTemplate注意点

    这篇文章主要介绍了使用spring的restTemplate注意点,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Mybatis注解实现多数据源读写分离详解

    Mybatis注解实现多数据源读写分离详解

    这篇文章主要给大家介绍了关于Mybatis注解实现多数据源读写分离的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Mybatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • Mybatis注解sql时出现的一个错误及解决

    Mybatis注解sql时出现的一个错误及解决

    这篇文章主要介绍了Mybatis注解sql时出现的一个错误及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • Java 并发编程之ThreadLocal详解及实例

    Java 并发编程之ThreadLocal详解及实例

    这篇文章主要介绍了Java 并发编程之ThreadLocal详解及实例的相关资料,需要的朋友可以参考下
    2017-02-02
  • SpringBoot2.0.3打印默认数据源为 HikariDataSource (null)问题

    SpringBoot2.0.3打印默认数据源为 HikariDataSource (null)问题

    这篇文章主要介绍了SpringBoot2.0.3打印默认数据源为 HikariDataSource (null)问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • IntelliJ IDEA 2022.2.3最新激活图文教程(亲测有用永久激活)

    IntelliJ IDEA 2022.2.3最新激活图文教程(亲测有用永久激活)

    今天给大家分享一个 IDEA 2022.2.3 的激活破解教程,全文通过文字+图片的方式讲解,手把手教你如何激活破解 IDEA, 只需要几分钟即可搞定,对idea2022.2.3激活码感兴趣的朋友跟随小编一起看看吧
    2022-11-11
  • 详解基于MybatisPlus两步实现多租户方案

    详解基于MybatisPlus两步实现多租户方案

    这篇文章主要介绍了详解基于MybatisPlus两步实现多租户方案,本文分两步,通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • SpringBoot框架DataSource多数据源配置方式

    SpringBoot框架DataSource多数据源配置方式

    这篇文章主要介绍了SpringBoot框架DataSource多数据源配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • java创建以任意图片为背景的窗口

    java创建以任意图片为背景的窗口

    这篇文章主要为大家详细介绍了java创建以任意图片为背景的窗口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12

最新评论