Mybatis攔截器實(shí)現(xiàn)數(shù)據(jù)分表
在項(xiàng)目中我們是用Mybatis + TKMapper + MYSQL存儲了一些消息日志,但是現(xiàn)在隨著業(yè)務(wù)數(shù)據(jù)暴增, 單表支撐不了這么多數(shù)據(jù). 因此決定把表做水平切分, 按照月份來給表進(jìn)行切分。這樣當(dāng)我們需要housekeep數(shù)據(jù)的時(shí)候,就可以直接drop掉表了,不論是備份還是刪除效率都會比較高
那我們就會是用到Mybaits的攔截器
Mybatis插件機(jī)制:
Mybatis支持插件(plugin), 講得通俗一點(diǎn)就是攔截器(interceptor). > 它支持ParameterHandler/StatementHandler/Executor/ResultSetHandler這四個(gè)級> 別進(jìn)行攔截.
總體概況為:
- 攔截參數(shù)的處理(ParameterHandler)
- 攔截Sql語法構(gòu)建的處理(StatementHandler)
- 攔截執(zhí)行器的方法(Executor)
- 攔截結(jié)果集的處理(ResultSetHandler)
分表注解
首先我們想要的效果是只有指定的表才需要分表,并不是全部表都需要分表,因?yàn)閿r截器是全局的,所以我們需要做特殊處理,當(dāng)只有指定的表我們才進(jìn)行攔截和處理。
第二點(diǎn),我們的分表策略目前是按照年月份來進(jìn)行分表,以后可能會按照天來分表,也就是分表策略是可變的,所以我們的代碼是可擴(kuò)展的,能夠自定義分表策略的。
基于以上兩點(diǎn),我們會構(gòu)造一個(gè)注解,作用于表上,為我們指定分表策略,以及標(biāo)記當(dāng)前表是需要做分表的。
@Target({TYPE})
@Retention(RUNTIME)
public @interface TableShard {
Class<? extends TableShardStrategy> shardStrategy();
}
分表策略接口
public interface TableShardStrategy {
String getTableShardName(String tableName);
}從方法名就可以看出來,返回的是一個(gè)分表后的表名
目前只有一個(gè)實(shí)現(xiàn)類,也就是按照年月份分表
public class DateTableShardStrategy implements TableShardStrategy {
public static final String PATTERN = "yyyyMM";
@Override
public String getTableShardName(String tableName) {
YearMonth yearMonth = Optional.ofNullable(ProcessContextHolder.get()).orElse(ProcessInfo.builder().yearMonth(TimeUtils.getYearMonth()).build()).getYearMonth();
String date = DateTimeFormatter.ofPattern(PATTERN).format(yearMonth);
return tableName + "_" + date;
}
}
具體的實(shí)現(xiàn)其實(shí)就是根據(jù)當(dāng)前實(shí)現(xiàn)獲取月份,然后在當(dāng)前的表名后面加上年月。 至于為什么中間從ProcessContextHolder.get()獲取時(shí)間呢,其實(shí)是當(dāng)有消息進(jìn)來的時(shí)候,我們會在ThreadLocal存一個(gè)當(dāng)前時(shí)間作為當(dāng)前消息的全局時(shí)間。為什么需要這么做呢,就是擔(dān)心一條消息在處理的過程中,出現(xiàn)跨月的情況,所以導(dǎo)致同一條消息的數(shù)據(jù),存儲在了兩個(gè)不同的表。所以需要維護(hù)一個(gè)全局的時(shí)間。
之后我們就可以在表的實(shí)體類上加上該注解了@TableShard(shardStrategy = DateTableShardStrategy.class)
Mybaits的攔截器實(shí)現(xiàn)
最后就是Mybatis的攔截器實(shí)現(xiàn)了。
@Intercepts({
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)
})
@Slf4j
@Component
@ConditionalOnProperty(value = "table.shard.enabled", havingValue = "true") //加上了table.shard.enabled 該配置才會生效
public class MybatisStatementInterceptor implements Interceptor {
private static final ReflectorFactory defaultReflectorFactory = new DefaultReflectorFactory();
public static final String DELEGATE_BOUND_SQL_SQL = "delegate.boundSql.sql";
public static final String DELEGATE_MAPPED_STATEMENT = "delegate.mappedStatement";
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler,
SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
defaultReflectorFactory
);
MappedStatement mappedStatement = (MappedStatement)
metaObject.getValue(DELEGATE_MAPPED_STATEMENT);
Class<?> clazz = getTableClass(mappedStatement);
if (clazz == null) {
return invocation.proceed();
}
TableShard tableShard = clazz.getAnnotation(TableShard.class); //獲取表實(shí)體類上的注解
Table table = clazz.getAnnotation(Table.class);
if (tableShard != null && table != null) { //如果注解存在就執(zhí)行分表策略
String tableName = table.name();
Class<? extends TableShardStrategy> strategyClazz = tableShard.shardStrategy();
TableShardStrategy strategy = strategyClazz.getDeclaredConstructor().newInstance();
String tableShardName = strategy.getTableShardName(tableName);
String sql = (String) metaObject.getValue(DELEGATE_BOUND_SQL_SQL);
metaObject.setValue(DELEGATE_BOUND_SQL_SQL, sql.replaceAll(tableName, tableShardName.toUpperCase())); //替換表名
}
return invocation.proceed();
}
private Class<?> getTableClass(MappedStatement mappedStatement) throws ClassNotFoundException {
String className = mappedStatement.getId();
className = className.substring(0, className.lastIndexOf('.')); //獲取到BaseMapper的實(shí)現(xiàn)類
Class<?> clazz = Class.forName(className);
if (BaseMapper.class.isAssignableFrom(clazz)) {
return (Class<?>) ((ParameterizedType) (clazz.getGenericInterfaces()[0])).getActualTypeArguments()[0]; //獲取表實(shí)體類
//public interface XXXMapper extends BaseMapper<XXX> 其實(shí)就是獲取到泛型中的具體表實(shí)體類
return null;
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
}
好了,這樣就實(shí)現(xiàn)了使用Mybatis攔截器進(jìn)行數(shù)據(jù)分表
參考
通過MyBatis攔截器實(shí)現(xiàn)增刪改查參數(shù)的加/解密(已上線項(xiàng)目)
mybatis自定義攔截器攔截sql,處理createTime,updateTime,createBy,updateBy等問題
mybatis攔截器實(shí)現(xiàn)數(shù)據(jù)庫表水平切分
到此這篇關(guān)于Mybatis攔截器實(shí)現(xiàn)數(shù)據(jù)分表的文章就介紹到這了,更多相關(guān)Mybatis攔截器分表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 編程如何使用 Class.forName() 加載類
在一些應(yīng)用中,無法事先知道使用者將加載什么類,而必須讓使用者指定類名稱以加載類,可以使用 Class的靜態(tài)forName()方法實(shí)現(xiàn)動態(tài)加載類,這篇文章主要介紹了Java編程如何使用Class.forName()加載類,需要的朋友可以參考下2022-06-06
詳解Spark?Sql在UDF中如何引用外部數(shù)據(jù)
這篇文章主要為大家介紹了詳解Spark?Sql在UDF中如何引用外部數(shù)據(jù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
用java生成html文件實(shí)現(xiàn)原理及代碼
用printStream來向html文件里輸出數(shù)據(jù),先創(chuàng)建一個(gè)StringBuilder對象,通過append方法來為其添加html語句,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下,希望對大家有所幫助2013-08-08
SpringBoot框架中Mybatis-plus的簡單使用操作匯總
這篇文章主要介紹了SpringBoot框架中Mybatis-plus的簡單使用,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
詳解Java獲取環(huán)境變量及系統(tǒng)屬性的方法
這篇文章主要介紹了詳解Java獲取環(huán)境變量及系統(tǒng)屬性的方法,講解了System.getEnv()和System.getProperties()這兩個(gè)核心方法的使用,需要的朋友可以參考下2016-05-05
基于SpringBoot?使用?Flink?收發(fā)Kafka消息的示例詳解
這篇文章主要介紹了基于SpringBoot?使用?Flink?收發(fā)Kafka消息,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
Socket結(jié)合線程池使用實(shí)現(xiàn)客戶端和服務(wù)端通信demo
這篇文章主要為大家介紹了Socket結(jié)合線程池的使用來實(shí)現(xiàn)客戶端和服務(wù)端通信實(shí)戰(zhàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
Springboot項(xiàng)目因?yàn)閗ackson版本問題啟動報(bào)錯解決方案
這篇文章主要介紹了Springboot項(xiàng)目因?yàn)閗ackson版本問題啟動報(bào)錯解決方案,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07

