記錄Mybatis-plus動態(tài)表名的實(shí)現(xiàn)

實(shí)際上Mybatis-plus官方本來就有提供對動態(tài)表名的擴(kuò)展支持以及給出了具體的Sample。

具體參考官方的sample github。從github的提交歷史可以追溯到最開始給出動態(tài)表名實(shí)現(xiàn)方案的版本是3.1.1,奈何我司所用的版本剛好卡在3.1.0,差那么一個小版本,直接拿官方的例子用是會報錯的。

@Configuration
@MapperScan("com.baomidou.mybatisplus.samples.dytablename.mapper")
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
        dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
            put("user", (metaObject, sql, tableName) -> {
                String year = "_2018";
                int random = new Random().nextInt(1);
                if (random == 1) {
                    year = "_2019";
                }
                return tableName + year;
            });
        }});
        paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
        return paginationInterceptor;
    }
}

因為相關(guān)的支持,都是在3.1.1加進(jìn)去的,所幸的是,3.1.1加進(jìn)去的擴(kuò)展所依賴的核心接口并沒有改變,所以理論上是可以把3.1.1的擴(kuò)展類代碼copy到自己的項目,這樣在3.1.0的版本就可與你實(shí)現(xiàn)動態(tài)表名了。具體涉及到的擴(kuò)展類包括:

import com.baomidou.mybatisplus.extension.parsers.DynamicTableNameParser;
import com.baomidou.mybatisplus.extension.parsers.ITableNameHandler;

官方給出的只是一個隨機(jī)表名,實(shí)際業(yè)務(wù)往往還有很多問題:

  1. 如何在調(diào)用mapper方法時傳遞我到底要決定使用什么表名?
    最開始我琢磨了那個metaObject半天,期望從它入手,但并沒有一個很通用的方式,而且在后續(xù)的版本中,這個metaObject甚至被移出了這個接口。后來才想到,這里最優(yōu)的實(shí)現(xiàn)只能使用ThreadLocal了。


    image.png
  2. 如果使用ThreadLocal,那就意味著每次調(diào)用mapper方法之前都要去set一下ThreadLocal,代碼很累贅,能不能更優(yōu)雅地實(shí)現(xiàn)呢?
    很容易想到,使用AOP或者手寫InvocationHandler動態(tài)代理的方式來解決。難得有機(jī)會手寫一次AOP,終于下決心認(rèn)真去學(xué)習(xí)了一下AOP那些什么切面,連接點(diǎn),增強(qiáng)各種亂七八糟的概念,好不容易學(xué)完了,發(fā)現(xiàn)其實(shí)我根本不太用的上。用不上AOP是因為下面的原因3導(dǎo)致代理的不是bean。(有空寫寫AOP那些概念,其實(shí)很簡單就能講清楚)

  3. 使用動態(tài)代理,意味著代理的是mapper類,直接AOP代理mapper還是沒辦法拿到動態(tài)表名,怎么解決?
    我想到了先用包裝模式,把mapper和dynamicTableName包裝起來,再進(jìn)行AOP,這樣包裝類實(shí)例就得手動new沒法用AOP了(也可能有別的辦法讓實(shí)例變成bean),只能自己搞InvocationHandler了。


    image.png

    image.png
  1. 因為業(yè)務(wù)上,dynamicTableName每次都是查表獲得的,mapper+dynamicTableName的組合包裝類實(shí)際上每次都new的話屬實(shí)有點(diǎn)不優(yōu)雅,應(yīng)該搞個緩存。
    這里就千萬要注意我們的mapper方法調(diào)用是每個request多線程并發(fā)調(diào)用的,緩存必須使用ConcurrentHashMap。


    image.png

神奇吧,一下子,把面試常用的包裝模式,動態(tài)代理,并發(fā)集合,ThreadLocal都玩了一遍,所以還是很有必要記錄一下的。

具體實(shí)現(xiàn)代碼,我已經(jīng)放在github上了。

使用@Scope("prototype") + AOP + @Cacheable實(shí)現(xiàn)

今天閑著沒事,決定嘗試完全使用spring提供的機(jī)制來實(shí)現(xiàn)。修改包括一下幾個方面:

  1. 使用@Scope("prototype")+@Lookup注入替代手動new MapperWrapper()。


    image.png
  2. 使用@Cacheable+ConcurrentMapCacheManager替代手動使用ConcurrentHashMap。


    image.png

    image.png
  3. 使用AOP的@Aspect+@Around("within(MapperWrapper)")注解自動設(shè)置threadlocal。


    image.png

詳細(xì)代碼已經(jīng)放在github的aop分支。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容