mybatis-plus实现动态表名
功能背景:
实际项目运营中,每天都有近千万的数据插入到数据库的表中,分表则成为了功能优化的必然选择。我们这边采用的是根据每天的时间建立一张新的表与之前的历史表结构一样。因此展现平台中动态获取数据查询则也需要改变。
原始方法一:
根据时间获取动态拼接成数据库表名
String tableName = "数据库表名" + date;
获取到表名后我们采取笨办法通过$的办法注入到SQL语句中
SELECT * FROM ${tableName}
问题解决,但是其中的代码过于繁杂不利于重复利用。
方法二:
因此我们采取mybatis-plus提供的动态表名插件
MyBatis-Plus-动态表名插件地址
引入依赖
<dependency>
<groupid>com.baomidou</groupid>
<artifactid>mybatis-plus-boot-starter</artifactid>
<version>3.4.1.tmp</version>
</dependency>
首先我们定义:
public enum DynamicTableTreadLocal {
INSTANCE;
private ThreadLocal<String> tableName = new ThreadLocal<>();
public String getTableName() {
return tableName.get();
}
public void setTableName(String tableName) {
this.tableName.set(tableName);
}
public void remove() {
tableName.remove();
}
}
之后我们在MybatisPlusConfig中配置自己的动态表规则
private static final String DYNAMIC_TABLE_PRE = "原始数据库表名";
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
//动态表规则-生成自己需要的动态表名
put(DYNAMIC_TABLE_PRE, (metaObject, sql, tableName) -> DynamicTableTreadLocal.INSTANCE.getTableName());
}});
paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
最后我们在我们的实现类中调用次方法
//获取当前表格
String tableName = this.tableName();
List<T> list ;
try {
DynamicTableTreadLocal.INSTANCE.setTableName(tableName);
list = TDao.getPage(params);
}
由此我们通过mybatis-plus实现动态表名的功能就实现了。
其中值得注意的是DynamicTableTreadLocal最后需要我们主动remove;
因为我们在使用ThreadLocalMap使用ThreadLocal的弱引用作为key,并且ThreadLocal得外部不存在强引用,key(ThreadLocal)就会被GC回收,这样就会造成ThreadLocalMap中key为null。并且value此时存在强引用,线程退出时value的强引用才会断开。如果当前线程未结束时,key为null的value就会存在强引用链,这就导致了内存泄漏。
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,key(ThreadLocal)就会被GC回收,这样就会导致ThreadLocalMap中key为null,但是value还存在强引用,只需要线程退出,value的强引用链条才会短掉,但如果当前线程未结束,这些key为null中的value就会一直存在强引用。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
其实ThreadLocal内存泄漏是由于ThreadLocalMap的生命周期和ThreadLocal一样,因此需要手动删除key,不然就会导致内存泄漏。
finally {
DynamicTableTreadLocal.INSTANCE.remove();
}