1. 程式人生 > 程式設計 >Mybatis plus多租戶方案的實戰踩坑記錄

Mybatis plus多租戶方案的實戰踩坑記錄

目錄
  • (一).方案
  • (二).官方多租戶的方案的優化和坑
    • 1.分析哪些需要加多租戶,哪些不需要加
    • 2.qlparser 這個包與pagehelper 版本不對
    • 3.sql解析失敗
    • 4.忽略多租戶不生效
  • 總結

    公司的老專案要改造多租戶,於是進入了大坑,本文寫點遇到的坑以及解決方案,每次遇到問題在網上搜了好久,記錄下來,防止以後忘掉。

    (一).方案

    網上有很多方案,本文只寫最後一種,即:表增加租戶id,實現資料隔離

    方案一:增加租戶id,在每一個mapper呼叫的地方,都手工加上租戶id

    例如:

    LambdaQueryWrapper<Entity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(Entity::getTenantId,"tenantId");
    entityMapper.selectList(lambdaQueryWrapper);
    

    這種方式複雜,工作量大,且容易漏。沒有采用

    方案二:使用mp官方的多租戶外掛,此處程式碼省略,自行去官方文件上查詢

    (二).官方多租戶的方案的優化和坑

    採用了官方文件的多租戶外掛後,前期除錯很順利,crud 都自測過了,以為沒有問題了,於是就發測試環境了,但是隨著測試的深入,發現了好多問題和需要改動的地方,此處列舉:

    1.分析哪些需要加多租戶,哪些不需要加

    (1)租戶id的重寫

    官方的預設重寫方法是:

    @Override
    public Expression getTenantId() {
        return null;
    }
    

    此處需要定義你的租戶id的獲取方法

    (2)租戶欄位的定義

    private static final String TENANT_ID = "tenant_id";
    
    @Override
    public String getTenantIdColumn() {
        return TENANT_ID;
    }
    

    (3)租戶攔截

    @Override
    public boolean ignoreTable(String tableName) {
        return TenantLineHandler.super.ignoreTable(tableName);
    }
    

    此處我採用的方案是 表名攔截,程式碼如下:

    @Override
    public boolean ignoreTable(String tableName) {
    
        /**
         * 此處的list,臨時用作攔截
         * 原因是:下面的表解析方法,用的是大駝峰轉下劃線,再跟sql攔截器攔截到的表名對比,如果匹配到了,則認為該表需要多租戶拼接
         * 但是有的表沒有嚴格的按照大駝峰轉下劃線,所以這些表需要額外定義
         * @TableName 這個註解能否完成該職責,目前還未測試,以後再說。
         */
        List<String> list = new ArrayList<>();
        list.add("das_standard_operation");
        list.add("t_expert");
        list.add("t_nominate_dict");
        list.add("t_nominate_dict_history");
        list.add("t_order");
        list.add("t_standard_sort");
        list.add("t_task");
        list.add("t_task_confirm");
        list.add("das_view");
        if (list.contains(tableName)) {
            return false;
        }
        EntityTableCache instance = EntityTableCache.getInstance();
        if (null == instance || null == instance.getCacheData(tableName)) {
            //如果未初始化到,不拼接租戶id
            return true;
        }
        String entityPath = EntityTableCache.getInstance().getCacheData(tableName).toString();
        //該方法會將大駝峰轉為下劃線,並完成初始化
        return !EntityUtils.isHaveAttr(entityPath,COLUMN_TENANTID);
    }
    

    EntityUtils 方法程式碼如下 (上的一位小夥子寫的,侵權聯絡我刪)

    /**
     * 判斷實體是否有某個屬性
     *
     * @param entityPath 實體全路徑
     * @param attrName   屬性名字
     * @return boolean
     */
    public static boolean isHaveAttr(String entityPath,String attrName) {
        Optional<String> epOptional = Optional.ofNullable(entityPath);
        if (!epOptional.isPresent()) {
            return false;
        }
        try {
            Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass(epOptional.get());
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                if (attrName.equals(field.getName())) {
                    return true;
                }
            }
            return false;
        } catch (ClassNotFoundException e) {
            // log.error("SystemSqlParser->isHaveAttr類載入異常:" + e.getMessage());
            return false;
        }
    }
    

    2.jsqlparser 這個包與pagehelper 版本不對

    在自測的時候,發現update語句攔不住,結果發現jsqlparser的版本號是1.2 而1.2與2.0(mp3.4.1 的jspparser版本是2.0),通過debug發現,進到了1.2的update方法

    如圖:

    Mybatisplus多租戶方案的實戰踩坑記錄

    1.2版本是getTables(),而2.0是如圖的getTable()

    解決方法:

    父pom強制規定版本

    </dependencyManagement>
        </dependencies>
            <dependency>
                <groupId>com.github.jsqlparser</groupId>
                <artifactId>jsqlparser</artifactId>
                <version>2.0</version>
            </dependency>
            <dependency>
                <groupId>http://www.cppcns.comcom.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>5.1.10</version>
            </dependency>
        </dependencies>
    </dependencyManagement&wFYRTnCgt;
    

    3.sql解析失敗

    1.regexp

    目前發現使用到了regexp > 0 的sql 語句,解析器會報錯

    2.replace into 語句

    這個語句目前我發現也是不支援的

    上述的兩個問題到現在還沒有解決,翻閱了一些資料,問了一些同事,對這塊接觸的都比較少,目前mp最新版本使用的3.4.3.4中使用的com.github.jsqlparser:jsqlparser 是4.2版本,目前對上述的兩條仍然不支援(如果有哪位大神解決了,麻煩評論指導一下!)

    解決方法

    既然你失敗,那我就不用你好了,手動拼接,見下面忽略方法⬇️

    4.忽略多租戶不生效

    我們都知道通過註解:@InterceptorIgnore(tenantLine = "on") 可以達到該mapper語句不進行sql解析,和不進行多租戶的改造。但是在實際應用場景中發現有一個特殊的場景該註解不生效。

    例如:

    Page<Expert> page = PageHelper.startPage(param.getPageNumber(),param.getPageSize());
    List<Expert> experts = expertMapper.queryExpertList(page);
    

    這個問題真的是浪費了我好久的時間,後來通過查閱資料,我發現@InterceptorIgnore在有分頁的時候,是會失效的,但是我又想,都是mp的東西,你自己都衝突,那怎麼能行。

    然後我就發現PageHelper 這個東西,看著很奇怪。然後我就給去掉了,不進行分頁,果然可以了!註解生效了。完美,同時,mp都已經提供了分頁的東西,為什麼還要藉助com.github的呢。

    然後改造如下:

    IPage<Expert> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getPageNumber(),param.getPageSize())
    List<Expert> experts = expertMapper.queryExpertList(expertMapping.dtoToEntity(param));

    總結

    到此這篇關於Mybatisplus多租戶方案實戰踩坑的文章就介紹到這了,更多相關Mybatisplus多租戶方案內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!