JSqlParser+mybatis攔截器,實現返回替換后的sql


1.JSqlParser可用於解析sql語句,mybatis攔截器的實現網上有大部分的講解,此處不做贅述,直接上結果。

@Intercepts(
        {
                @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class })
        }
)
@Order(1)
@Component
public class DataPermissionInterceptor implements Interceptor {
    private final static Logger logger = LoggerFactory.getLogger(DataPermissionInterceptor.class);
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
        StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(handler, "delegate");
        //從當前線程獲取需要進行數據權限控制的業務
        //TODO 此處后續替換為自己的權限控制,也可以去掉,所有人員均涉及權限控制,不存在白名單用戶
        DataPermission dataPermission = DPHelper.getLocalDataPermissions();
        //判斷有沒有進行數據權限控制,是不是最高權限的管理員(這里指的是數據權限的白名單用戶)
        if (dataPermission != null && dataPermission.getAdmin() == false && !dataPermission.getTables().isEmpty()) {
            BoundSql boundSql = delegate.getBoundSql();
            //獲取真實sql
            String sql = boundSql.getSql();
            if(CCJSqlParserUtil.parse(sql) instanceof Select){
                //獲得方法類型
                Select select = (Select) CCJSqlParserUtil.parse(sql);
                //此處調用accept方法,new SelectVisitorImpl()用於sql解析
                select.getSelectBody().accept(new SelectVisitorImpl());
                //修改sql
                ReflectUtil.setFieldValue(boundSql, "sql", select.toString());
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

  2. SelectVisitorImpl類實現了SelectVisitor接口,重寫了其中的visit()方法。

SelectVisitor接口內容如下:

package net.sf.jsqlparser.statement.select;

public interface SelectVisitor {
    //PlainSelect  為普通select查詢
    void visit(PlainSelect plainSelect);

    void visit(SetOperationList setOpList);

    void visit(WithItem withItem);
}

SelectVisitorImpl類內容如下:此處做了部分優化

package com.example.mybatissqls.visitor;

import com.example.mybatissqls.sys.ColumnInfo;
import com.example.mybatissqls.sys.TableInfo;
import com.example.mybatissqls.utils.UserUtils;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.AllColumns;
import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
import net.sf.jsqlparser.statement.select.SelectItem;
import net.sf.jsqlparser.statement.select.SelectItemVisitor;
import net.sf.jsqlparser.statement.select.SelectVisitor;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.util.TablesNamesFinder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


public class SelectVisitorImpl implements SelectVisitor {

    private UserUtils userUtils = UserUtils.getBean(UserUtils.class);

    @Override
    public void visit(PlainSelect plainSelect) {
        // 訪問 select 獲取sql查詢字段,將字段替換為有權限的字段
        if (plainSelect.getSelectItems() != null) {
       // 業務代碼,獲取表和字段的權限 List
<TableInfo> tableInfo = userUtils.getTableInfo(); Map<String, TableInfo> tableMap = userUtils.getTableMap(tableInfo);
SelectItemVisitorImpl selectItemVisitor
= new SelectItemVisitorImpl(); //替換之后的列字段 List<String> columnList = new ArrayList<>();         // 解釋在下方 List<AllTableColumns> allTableColumns = new ArrayList<>(); List<SelectExpressionItem> selectExpressionItems = new ArrayList<>(); List<String> tableList = getTableList(plainSelect.toString()); if (tableList != null) { for (SelectItem item : plainSelect.getSelectItems()) { item.accept(selectItemVisitor); //設置可查詢的列 if (selectItemVisitor.getEnhancedCondition() != null) { String aliasColumn = selectItemVisitor.getEnhancedCondition().toString(); //正則校驗以'開頭,以'結尾,存在替換 String regex = "'([\\s\\S]*?)'"; Matcher matcher = Pattern.compile(regex).matcher(aliasColumn);                 // 滿足正則校驗,剔除字符串的第一位和最后一位 if (matcher.find()){ aliasColumn = aliasColumn.substring(1,aliasColumn.length()-1); }               //將字符串按照.進行分隔,前面為別名后面的字段名 String[] tableArr = aliasColumn.split("\\.");                String table = tableList.size() ==1 ? tableList.get(0):tableArr[0]; if (aliasColumn.contains("*")) { TableInfo tableInfo1 = tableMap.get(table); if (tableInfo1 != null) { List<ColumnInfo> columnInfos = tableInfo1.getColumnInfos(); //設置權限且有權限的 List<ColumnInfo> collect = columnInfos.stream() .filter(e -> e.getIsSetPermission() && e .getIsHavePermission()).collect( Collectors.toList()); //不做權限設置的,通用的 List<ColumnInfo> collect1 = columnInfos.stream() .filter(e -> !e.getIsSetPermission()).collect( Collectors.toList()); for (ColumnInfo info : collect) { selectExpressionItems.add(new SelectExpressionItem(new Column(new Table(table),info.getColumnName()))); columnList.add(table+"."+info.getColumnName()); } for (ColumnInfo info : collect1) { selectExpressionItems.add(new SelectExpressionItem(new Column(new Table(table),info.getColumnName()))); columnList.add(table+"."+info.getColumnName()); } } else { allTableColumns.add(new AllTableColumns(new Table(table))); } } else { TableInfo tableInfo1 = tableMap.get(table); boolean flag = tableArr.length == 1; if (tableInfo1 != null) { List<ColumnInfo> columnInfos = tableInfo1.getColumnInfos(); //對比字段 Map<String, ColumnInfo> collect = columnInfos.stream() .collect(Collectors.toMap(ColumnInfo::getColumnName, Function.identity())); if (flag){ ColumnInfo columnInfo = collect.get(tableArr[0]); if ((columnInfo.getIsSetPermission() && columnInfo.getIsHavePermission()) || !columnInfo.getIsSetPermission()) { selectExpressionItems.add(new SelectExpressionItem(new Column(new Table(table),tableArr[0]))); } } else { ColumnInfo columnInfo = collect.get(tableArr[1]); if ((columnInfo.getIsSetPermission() && columnInfo.getIsHavePermission()) || !columnInfo.getIsSetPermission()) { selectExpressionItems.add(new SelectExpressionItem(new Column(new Table(table),tableArr[1]))); } } } } } } }         // 替換字段 SelectItem[] list = new SelectItem[allTableColumns.size()+selectExpressionItems.size()]; plainSelect.setSelectItems(null); for (int i = 0 ; i < allTableColumns.size();i++) { list[i] = allTableColumns.get(i); plainSelect.addSelectItems(list[i]); } for(int j = 0 ; j < selectExpressionItems.size();j++){ list[j+allTableColumns.size()] = selectExpressionItems.get(j); plainSelect.addSelectItems(list[j+allTableColumns.size()]); } } // 訪問from FromItem fromItem = plainSelect.getFromItem(); FromItemVisitorImpl fromItemVisitorImpl = new FromItemVisitorImpl(); fromItem.accept(fromItemVisitorImpl); // 訪問join if (plainSelect.getJoins() != null) { for (Join join : plainSelect.getJoins()) { join.getRightItem().accept(fromItemVisitorImpl); } } // 訪問where if (plainSelect.getWhere() != null) { plainSelect.getWhere().accept(new ExpressionVisitorImpl()); } //過濾增強的條件 if (fromItemVisitorImpl.getEnhancedCondition() != null) { if (plainSelect.getWhere() != null) { Expression expr = new Parenthesis(plainSelect.getWhere()); Expression enhancedCondition = new Parenthesis(fromItemVisitorImpl.getEnhancedCondition()); AndExpression and = new AndExpression(enhancedCondition, expr); plainSelect.setWhere(and); } else { plainSelect.setWhere(fromItemVisitorImpl.getEnhancedCondition()); } } // 訪問 order by if (plainSelect.getOrderByElements() != null) { for (OrderByElement orderByElement : plainSelect .getOrderByElements()) { orderByElement.getExpression().accept( new ExpressionVisitorImpl()); } } // 訪問group by having if (plainSelect.getHaving() != null) { plainSelect.getHaving().accept(new ExpressionVisitorImpl()); } } /** * 獲取sql中的表名 * @param sql * @return */ private List<String> getTableList(String sql){ //獲取table表名,替換字段 TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); try { return tablesNamesFinder .getTableList(CCJSqlParserUtil.parse(sql)); } catch (JSQLParserException e) { e.printStackTrace(); return null; } } @Override public void visit(SetOperationList setOpList) { for (SelectBody plainSelect : setOpList.getSelects()) { plainSelect.accept(new SelectVisitorImpl()); } } @Override public void visit(WithItem withItem) { // withItem.getSelectBody().accept(new SelectVisitorImpl()); } }

 3. 從獲取字段列的方法中可以了解到

plainSelect.getSelectItems() 獲取所有字段集合,集合的類型為SelectItem,
SelectItemVisitorImpl selectItemVisitor = new SelectItemVisitorImpl();
List<AllTableColumns> allTableColumns = new ArrayList<>();
            List<SelectExpressionItem> selectExpressionItems = new ArrayList<>();

            List<String> tableList = getTableList(plainSelect.toString());
            if (tableList != null) {
                for (SelectItem item : plainSelect.getSelectItems()) {
                    item.accept(selectItemVisitor);

SelectItem接口只有一個accept方法,且參數為SelectItemVisitor。
package net.sf.jsqlparser.statement.select;

public interface SelectItem {
    void accept(SelectItemVisitor var1);
}
因此調用item.accept(selectItemVisitor);方法時新建SelectItemVisitorImpl實現類,新增屬性,作為后續增強使用。
public class SelectItemVisitorImpl implements SelectItemVisitor {

    @Override
    public void visit(AllColumns allColumns) {
        //查詢項為*  單表或者多表,不帶別名的*走這里
        enhancedCondition = new StringValue(allColumns.toString());
    }

    @Override
    public void visit(AllTableColumns allTableColumns) {
        //帶別名的*走這里
        enhancedCondition = new StringValue(allTableColumns.toString());
    }

    @Override
    public void visit(SelectExpressionItem selectExpressionItem) {
        //查詢具體字段
        enhancedCondition = selectExpressionItem.getExpression();
    }

    // 聲明增強條件
    private Expression enhancedCondition;

    public Expression getEnhancedCondition() {
        return enhancedCondition;
    }

}
SelectItemVisitorImpl具體內容如上所示,
替換sql中的字段時,創建集體的字段使用
selectExpressionItems.add(new SelectExpressionItem(new Column(new Table(table),info.getColumnName())));
創建對象.*時使用
allTableColumns.add(new AllTableColumns(new Table(table)));
最后創建SelectItem 數組,將原有sql查詢列置為null,轉換后的新增進去
SelectItem[] list = new SelectItem[allTableColumns.size()+selectExpressionItems.size()];
            plainSelect.setSelectItems(null);
            for (int i = 0 ; i < allTableColumns.size();i++) {
                list[i] = allTableColumns.get(i);
                plainSelect.addSelectItems(list[i]);
            }
            for(int j = 0 ; j < selectExpressionItems.size();j++){
                list[j+allTableColumns.size()] = selectExpressionItems.get(j);
                plainSelect.addSelectItems(list[j+allTableColumns.size()]);
            }

由此,解決了sql列替換的問題,但此處未解決直接顯示列名,而沒有匹配表名的情況,后續優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM