Interprétation du Code Source de BaseMultiTableInnerInterceptor

La classe BaseMultiTableInnerInterceptor, située dans le module mybatis-plus-jsqlparser-4.9 sous le package com.baomidou.mybatisplus.extension.plugins.inner, est une classe abstraite qui fournit les méthodes nécessaires pour l'analyse et la réécriture des requêtes SQL. Elle est utilisée par des plugins tels que TenantLineInnerInterceptor pour la gestion des données multi-locataires. ```

public abstract class BaseMultiTableInnerInterceptor extends JsqlParserSupport implements InnerInterceptor { protected void processSelectBody(Selection selection, String whereClause) {...} protected Expression combineExpression(Table table, Expression where, String whereClause) {...} // Autres méthodes... }


2. Flux d'Exécution
-------------------

Les méthodes `beforeQuery()` et `beforePrepare()` sont implémentées pour intercepter les requêtes avant leur exécution. Les requêtes SELECT sont traitées par `parserSingle()`, tandis que les opérations INSERT, UPDATE et DELETE sont gérées par `parserMulti()`. ```

@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object param, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    if (shouldIgnoreDataPermission(ms.getId())) return;
    PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
    mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
}

@Override
public void beforePrepare(StatementHandler sh, Connection conn, Integer transactionTimeout) {
    PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
    MappedStatement ms = mpSh.mappedStatement();
    SqlCommandType type = ms.getSqlCommandType();
    if ((type == SqlCommandType.UPDATE || type == SqlCommandType.DELETE) && !shouldIgnoreDataPermission(ms.getId())) {
        PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
        mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));
    }
}
  1. Analyse du Code Source

3.1 processSelectBody

Cette méthode analyse un objet Select et détermine comment le réécrire en fonction de sa sturcture. ```

protected void processSelectBody(Selection selectBody, String whereClause) { if (selectBody instanceof SimpleSelect) { processSimpleSelect((SimpleSelect) selectBody, whereClause); } else if (selectBody instanceof ParenthesizedSelect) { ParenthesizedSelect parenthesized = (ParenthesizedSelect) selectBody; processSelectBody(parenthesized.getSelection(), whereClause); } else if (selectBody instanceof SetOperationList) { SetOperationList setOps = (SetOperationList) selectBody; for (Selection sel : setOps.getSelects()) { processSelectBody(sel, whereClause); } } }


### 3.2 processSimpleSelect

Cette méthode traite une requête SELECT standard. ```

protected void processSimpleSelect(SimpleSelect simpleSelect, String whereClause) {
    List<selectitem> items = simpleSelect.getSelectItems();
    if (!items.isEmpty()) {
        items.forEach(item -> processSelectItem(item, whereClause));
    }

    processWhereSubSelect(simpleSelect.getWhere(), whereClause);

    FromItem fromItem = simpleSelect.getFromItem();
    List<table> tables = processFromItem(fromItem, whereClause);
    List<table> mainTables = new ArrayList<>(tables);

    List<join> joins = simpleSelect.getJoins();
    if (joins != null && !joins.isEmpty()) {
        processJoins(mainTables, joins, whereClause);
    }

    if (!mainTables.isEmpty()) {
        simpleSelect.setWhere(combineExpression(simpleSelect.getWhere(), mainTables, whereClause));
    }
}


<h3>3.3 processSelectItem</h3>

Cette méthode sert à analyser et à réécrire les éléments de sélection contenant des sous-requêtes ou des fonctions.


protected void processSelectItem(SelectItem item, String whereClause) {
    if (item instanceof SelectFunction) {
        processFunction(((SelectFunction) item).getFunction(), whereClause);
    } else if (item instanceof SelectSubquery) {
        processSelectBody(((SelectSubquery) item).getSubquery(), whereClause);
    }
}


<h3>3.4 processWhereSubSelect</h3>

Cette méthode gère les sous-requêtes présentes dans la clause WHERE.


protected void processWhereSubSelect(Expression expr, String whereClause) {
    if (expr == null) return;

    if (expr instanceof BinaryExpression) {
        BinaryExpression binaryExpr = (BinaryExpression) expr;
        processWhereSubSelect(binaryExpr.getLeftExpression(), whereClause);
        processWhereSubSelect(binaryExpr.getRightExpression(), whereClause);
    } else if (expr instanceof InExpression) {
        InExpression inExpr = (InExpression) expr;
        if (inExpr.getRightExpression() instanceof Select) {
            processSelectBody((Select) inExpr.getRightExpression(), whereClause);
        }
    } else if (expr instanceof ExistsExpression) {
        processSelectBody((Select) ((ExistsExpression) expr).getRightExpression(), whereClause);
    }
}


<h3>3.5 processJoins</h3>

Cette méthode traite les jointures entre les tables, ajoutant les clauses WHERE nécessaires.


private List<table> processJoins(List<table> mainTables, List<join> joins, String whereClause) {
    Table drivingTable = null;
    Table previousTable = null;

    if (mainTables.size() == 1) {
        drivingTable = mainTables.get(0);
        previousTable = drivingTable;
    }

    Deque<list>> onTablesStack = new LinkedList<>();
    for (Join join : joins) {
        FromItem rightItem = join.getRightItem();
        List<table> joinedTables = null;

        if (rightItem instanceof Table) {
            joinedTables = Collections.singletonList((Table) rightItem);
        } else if (rightItem instanceof ParenthesizedFromItem) {
            joinedTables = processSubJoin((ParenthesizedFromItem) rightItem, whereClause);
        }

        if (joinedTables != null) {
            Table currentJoinTable = joinedTables.get(0);
            List<table> onTables = null;

            if (join.isRightJoin()) {
                drivingTable = currentJoinTable;
                mainTables.clear();
                if (previousTable != null) {
                    onTables = Collections.singletonList(previousTable);
                }
            } else if (join.isInnerJoin()) {
                if (drivingTable == null) {
                    onTables = Collections.singletonList(currentJoinTable);
                } else {
                    onTables = Arrays.asList(drivingTable, currentJoinTable);
                }
                drivingTable = null;
                mainTables.clear();
            } else {
                onTables = Collections.singletonList(currentJoinTable);
            }

            if (drivingTable != null && !mainTables.contains(drivingTable)) {
                mainTables.add(drivingTable);
            }

            Collection<expression> originalOnExpressions = join.getOnExpressions();
            if (originalOnExpressions.size() == 1 && onTables != null) {
                List<expression> onExpressions = new ArrayList<>();
                onExpressions.add(combineExpression(originalOnExpressions.iterator().next(), onTables, whereClause));
                join.setOnExpressions(onExpressions);
                previousTable = drivingTable == null ? currentJoinTable : drivingTable;
                continue;
            }

            onTablesStack.push(onTables);
            if (originalOnExpressions.size() > 1) {
                Collection<expression> processedOnExpressions = new ArrayList<>();
                for (Expression exp : originalOnExpressions) {
                    List<table> currentTableList = onTablesStack.poll();
                    if (currentTableList == null || currentTableList.isEmpty()) {
                        processedOnExpressions.add(exp);
                    } else {
                        processedOnExpressions.add(combineExpression(exp, currentTableList, whereClause));
                    }
                }
                join.setOnExpressions(processedOnExpressions);
            }
            previousTable = currentJoinTable;
        } else {
            processOtherFromItem(rightItem, whereClause);
            previousTable = null;
        }
    }

    return mainTables;
}


<h3>3.6 builderExpression</h3>

Cette méthode construit l'expression finale en combinant les conditions existantes avec les nouvelles conditions.


protected Expression builderExpression(Expression existingExpr, List<table> tables, String whereClause) {
    if (tables == null || tables.isEmpty()) return existingExpr;

    List<expression> expressions = tables.stream()
        .map(table -> buildTableExpression(table, existingExpr, whereClause))
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

    if (expressions.isEmpty()) return existingExpr;

    Expression combinedExpression = expressions.get(0);
    if (expressions.size() > 1) {
        for (int i = 1; i < expressions.size(); i++) {
            combinedExpression = new AndExpression(combinedExpression, expressions.get(i));
        }
    }

    if (existingExpr == null) return combinedExpression;
    if (existingExpr instanceof OrExpression) {
        return new AndExpression(new Parenthesis(existingExpr), combinedExpression);
    } else {
        return new AndExpression(existingExpr, combinedExpression);
    }
}


<h3>3.7 buildTableExpression</h3>

Cette méthode doit être redéfinie par les classes filles pour définir les conditions spécifiques à chaque table.


public abstract Expression buildTableExpression(Table table, Expression existingExpr, String whereClause);


</expression></table></table></expression></expression></expression></table></table></list></join></table></table></join></table></table></selectitem>

Étiquettes: MyBatis-Plus JSQLParser SQLParsing interceptor Java

Publié le 14 septembre à 05h35