Mybatis-Plus动态表名的实现示例
作者: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 动态表名内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!