前言:  因为系统要服务另外一家公司,那么问题就来了。1,采用分布式系统,推翻原有的系统架构。2,业务是一致的主要是不同的公司,互补影响,那么就要从数据上做隔离,想到了分库。总之,大概思考了一个小时,决定来使用mybatis拦截器尝试来实现。大致是在现有数据库所有的业务表都 加入  companyId 字段,以此字段来作为区分和拦截的由于之前没有使用过mybatis拦截器,那么一切都从0 开始百度+查询资料;网上有很多关于拦截器的资料信息,这里贴上两处本人认为有用的参考资料 A 拦截器原理出处:https://www.jianshu.com/p/7c7b8c2c985d

1  拦截器的四种代理模式, StatementHandler是Mybatis直接和数据库执行sql脚本的对象。另外它也实现了Mybatis的一级缓存。这里我采用的是其中的一种模式用插件来实现对一级缓存的操作(禁用等等)。

2 拦截器模式定下,那么开始在spring-mybatis.xml 加入拦截器插件;

        <property name="plugins">
            <array>
                <bean class="com.wangzhixuan.commons.intercept.TestInterceptor"></bean> 
            </array>
        </property>

package com.wangzhixuan.commons.intercept;


import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.util.Properties;


import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


import com.wangzhixuan.commons.sqlparser.SelectVisitorImpl;


import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.select.Select;


@Intercepts({
        @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class, Integer.class})
})
public class TestInterceptor implements Interceptor {
    // 控制器本来就是单例,这样似乎更加合理
    protected Logger logger = LogManager.getLogger(getClass());


    private CCJSqlParserManager parserManager = new CCJSqlParserManager();


    @Override
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        if(invocation.getTarget() instanceof StatementHandler) {
            //逻辑代码区
            StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
            // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
            // 可以分离出最原始的的目标类)
            while (metaStatementHandler.hasGetter("h")) {
                Object object = metaStatementHandler.getValue("h");
                metaStatementHandler = SystemMetaObject.forObject(object);
            }
            // 分离最后一个代理对象的目标类
            while (metaStatementHandler.hasGetter("target")) {
                Object object = metaStatementHandler.getValue("target");
                metaStatementHandler = SystemMetaObject.forObject(object);
            }
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            //获取sql
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            if(sql.contains("NEXTVAL")){ // 去掉子序列函数
            return invocation.proceed();
            }
            //获取方法id
String id = mappedStatement.getId();
if (id.contains("InventoryInoutMapper") || id.contains("selectSaleReportByGmtCreate")
|| id.contains("selectBiShipReportByGmtCreate") || id.contains("selectTransNoinventoryReport")
|| id.contains("queryPurStoDetails") || id.contains("queryTaskDailyCloseIds")) { // 剔除不需要走拦截器的方法
return invocation.proceed();
}
            //获得方法类型
            SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
            if ("SELECT".equals(sqlCommandType.toString())) {  // 拦截主要是针对查询语句的
             //如果是select就将sql转成SELECT对象  
            try {
            Select select = (Select)parserManager.parse(new StringReader(sql));
            //访问各个visitor  
                    select.getSelectBody().accept(new SelectVisitorImpl());  
                    //将增强后的sql放回
                    metaStatementHandler.setValue("delegate.boundSql.sql", select.toString());
                    return invocation.proceed();
            } catch(Throwable e) {
            logger.error("拦截器异常Start");
            logger.error(e);
            logger.error("拦截器异常End");
            }
            }
        }
        return invocation.proceed();
    }


    @Override
    public Object plugin(Object target) {
        //生成代理对象
        return Plugin.wrap(target, this);
    }


    @Override
    public void setProperties(Properties properties) {
    }

}


//下面是sqlpase的具体用法 (这里我采用的是访问者模式对于所涉及的业务表进行隔离)


package com.wangzhixuan.commons.sqlparser;


import java.util.HashSet;


import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import com.google.common.collect.Sets;
import com.wangzhixuan.commons.shiro.ShiroUser;


import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.Between;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.FromItemVisitor;
import net.sf.jsqlparser.statement.select.LateralSubSelect;
import net.sf.jsqlparser.statement.select.SubJoin;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.TableFunction;
import net.sf.jsqlparser.statement.select.ValuesList;


public class FromItemVisitorImpl implements FromItemVisitor {


    private static Logger logger = LoggerFactory.getLogger(FromItemVisitorImpl.class);
    // 声明增强条件
    private Expression enhancedCondition;
    
    // 定义需要数据隔离的表
    private static HashSet<String> interceptTableSet = Sets.newHashSet();
    static {
    interceptTableSet.add("ITEM");//商品管理数据隔离; 同时item 表中新增  company_id 字段
        interceptTableSet.add("ITEM_SKU");//SKU管理数据隔离;
    interceptTableSet.add("INVENTORY");//SKU 管理展示库存
    interceptTableSet.add("BRAND");//品牌管理
    interceptTableSet.add("`OUTER_ORDER`");//主订单管理
    interceptTableSet.add("`OUTER_ORDER_DETAIL`");// 主订单明细管理
    interceptTableSet.add("ERP_ORDER");//子订单管理
    interceptTableSet.add("TASK_DAILY");//采购单
    interceptTableSet.add("TASK_DAILY_DETAIL");//采购单明细
    interceptTableSet.add("PURCHASE_STORAGE");//采购入库单
    interceptTableSet.add("PURCHASE_STORAGE_DETAIL");//采购入库单明细
    interceptTableSet.add("INVENTORY_AREA");//库存占用表
    interceptTableSet.add("INVENTORY_OUT");//库存记录库记录表
    interceptTableSet.add("WAREHOUSE");//仓库改造
    interceptTableSet.add("INVENTORY_INOUT");//出入库记录表     {硬编码}
        //interceptTableSet.add("`SHIPPING_ORDER`");//发货管理              {硬编码}
        //interceptTableSet.add("TASK_DAILY_ORDER");//订单生成采购单              {硬编码}
    //interceptTableSet.add("TASK_DAILY_ORDER_HISTORY");// 订单生成采购单历史记录  {硬编码}
    interceptTableSet.add("TASK_DAILY_ORDER_UNCOMPLETE"); //未完成采购表
    interceptTableSet.add("`ERP_RETURN_ORDER`"); //退单表
    interceptTableSet.add("`SELLER_TYPE`");//销售类别表
    interceptTableSet.add("`SELLER`");//销售人员表
    interceptTableSet.add("`WX_PURCHASE_USER`");//买手人员表
    interceptTableSet.add("`GOODS_CASE`");//货源信息反馈表
    interceptTableSet.add("LOGISTIC_COMPANY");//快递公司
    interceptTableSet.add("RECEIPT");//小票
    interceptTableSet.add("RECEIPT_GROUP");//小票另存表
    interceptTableSet.add("TASK_RECEIPT");//小票明细
    interceptTableSet.add("FIND_ITEM");//发现商品
    interceptTableSet.add("FIND_ITEM_SKU");//发现商品sku
    interceptTableSet.add("FILE_MANAGE");//文件管理
    }


    // FROM 表名 <----主要的就是这个,判断用户对这个表有没有权限
    @Override
    public void visit(Table tableName) {
        //判断该表是否是需要操作的表
        if (isActionTable(tableName.getFullyQualifiedName())) {
            // 声明表达式数组
            Expression[] expressions;
            // 其他情况,也就是最常用的情况,比如where   1 = 1
            Column column = new Column(new Table(tableName.getAlias()!=null?tableName.getAlias().getName():tableName.getFullyQualifiedName()), "company_id");
            if(getShiroUser()== null){
            return;
            }else {
            expressions = new Expression[] {column, new LongValue(getShiroUser().getCompanyId())};
            }
            // 根据运算符对原始数据进行拼接
            Expression operator = this.getOperator("=", expressions);
            if (this.enhancedCondition != null) {
                enhancedCondition = new AndExpression(enhancedCondition , operator);
            } else {
                enhancedCondition = operator;
            }
        }
    }


    // FROM 子查询
    @Override
    public void visit(SubSelect subSelect) {
        // 如果是子查询的话返回到select接口实现类
        subSelect.getSelectBody().accept(new SelectVisitorImpl());
    }


    // FROM subjoin
    @Override
    public void visit(SubJoin subjoin) {
        subjoin.getLeft().accept(new FromItemVisitorImpl());
        subjoin.getJoin().getRightItem().accept(new FromItemVisitorImpl());
    }


    // FROM 横向子查询
    @Override
    public void visit(LateralSubSelect lateralSubSelect) {
        lateralSubSelect.getSubSelect().getSelectBody()
                .accept(new SelectVisitorImpl());
    }


    // FROM value列表
    @Override
    public void visit(ValuesList valuesList) {
    }


    // FROM tableFunction
    @Override
    public void visit(TableFunction tableFunction) {
    }


    // 将字符串类型的运算符转换成数据库运算语句
    private Expression getOperator(String op, Expression[] exp) {
        if ("=".equals(op)) {
            EqualsTo eq = new EqualsTo();
            eq.setLeftExpression(exp[0]);
            eq.setRightExpression(exp[1]);
            return eq;
        } else if (">".equals(op)) {
            GreaterThan gt = new GreaterThan();
            gt.setLeftExpression(exp[0]);
            gt.setRightExpression(exp[1]);
            return gt;
        } else if (">=".equals(op)) {
            GreaterThanEquals geq = new GreaterThanEquals();
            geq.setLeftExpression(exp[0]);
            geq.setRightExpression(exp[1]);
            return geq;
        } else if ("<".equals(op)) {
            MinorThan mt = new MinorThan();
            mt.setLeftExpression(exp[0]);
            mt.setRightExpression(exp[1]);
            return mt;
        } else if ("<=".equals(op)) {
            MinorThanEquals leq = new MinorThanEquals();
            leq.setLeftExpression(exp[0]);
            leq.setRightExpression(exp[1]);
            return leq;
        } else if ("<>".equals(op)) {
            NotEqualsTo neq = new NotEqualsTo();
            neq.setLeftExpression(exp[0]);
            neq.setRightExpression(exp[1]);
            return neq;
        } else if ("is null".equalsIgnoreCase(op)) {
            IsNullExpression isNull = new IsNullExpression();
            isNull.setNot(false);
            isNull.setLeftExpression(exp[0]);
            return isNull;
        } else if ("is not null".equalsIgnoreCase(op)) {
            IsNullExpression isNull = new IsNullExpression();
            isNull.setNot(true);
            isNull.setLeftExpression(exp[0]);
            return isNull;
        } else if ("like".equalsIgnoreCase(op)) {
            LikeExpression like = new LikeExpression();
            like.setNot(false);
            like.setLeftExpression(exp[0]);
            like.setRightExpression(exp[1]);
            return like;
        } else if ("not like".equalsIgnoreCase(op)) {
            LikeExpression nlike = new LikeExpression();
            nlike.setNot(true);
            nlike.setLeftExpression(exp[0]);
            nlike.setRightExpression(exp[1]);
            return nlike;
        } else if ("between".equalsIgnoreCase(op)) {
            Between bt = new Between();
            bt.setNot(false);
            bt.setLeftExpression(exp[0]);
            bt.setBetweenExpressionStart(exp[1]);
            bt.setBetweenExpressionEnd(exp[2]);
            return bt;
        } else if ("not between".equalsIgnoreCase(op)) {
            Between bt = new Between();
            bt.setNot(true);
            bt.setLeftExpression(exp[0]);
            bt.setBetweenExpressionStart(exp[1]);
            bt.setBetweenExpressionEnd(exp[2]);
            return bt;
        } else {
            // 如果没有该运算符对应的语句
            return null;
        }


    }


    public Expression getEnhancedCondition() {
        return enhancedCondition;
    }


    // 判断传入的table是否是要进行操作的table
    public boolean isActionTable(String tableName) {
        boolean flag = false;
        if (interceptTableSet.contains(tableName.toUpperCase())) {
            flag = true;
        }
        return flag;
    }
    
    /**
     * 获取当前登录用户对象
     * @return {ShiroUser}
     */
    public ShiroUser getShiroUser() {
    ShiroUser one = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
        return one;
    }


}

Logo

开源、云原生的融合云平台

更多推荐