springboot 整合 mybatis-plus的一些主要配置代码

柴岳
2023-12-01

前言:
mybatis-plus版本:3.5+
springboot版本:2.4.2

1.引入springboot环境依赖
   省略
2.引入mybatis-plus依赖,引入数据库依赖,德鲁伊
  <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!--mysql数据库链接-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!--mybati plus-boot-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
         <!--hutool开发工具-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
        </dependency>

1.多租户,查询sql拦截处理

实现 InnerInterceptor 接口,重写核心方法 beforeQuery,通过 boundSql.getSql() 方法拿到准备执行的 select语句做处理。

代码:

import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.tz4cloud.common.core.exception.ServiceException;
import com.tz4cloud.common.core.obj.TzUser;
import com.tz4cloud.common.core.utils.TzUserInfoUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
 * mybatis-plus sql拦截器
 *
 * @author wrx
 * @since 2022/9/21 17:42
 */
@Slf4j
public class DataScopeInterceptor implements InnerInterceptor {

    private static final List<String> IGNORE_TABLES = new ArrayList<>();

    @Autowired
    TzUserInfoUtil tzUserInfoUtil;

    static {
        //不需要权限过滤公司id的表
        IGNORE_TABLES.add("base_customs_params");
        IGNORE_TABLES.add("base_hs_element");
    }

    //拿到当前拦截的表名
    private String tableName = StrUtil.EMPTY;

    /**
     * 查询语句的前置解析操作
     *
     * @param
     * @return
     * @author  wrx
     * @date 2022/9/22 14:23
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        String buildSql = boundSql.getSql();
        try {
            Statement statement = CCJSqlParserUtil.parse(buildSql);
            Select select = (Select) statement;
            processSelectBody(select.getSelectBody());
        } catch (JSQLParserException e) {
            throw ExceptionUtils.mpe("Failed to process, Error SQL: %s", e.getCause(), buildSql);
        } catch (ServiceException be) {
            log.info(be.getMessage());
            return;
        }
        TzUser user = tzUserInfoUtil.getUser();
        if (user != null) {
            //sql拼接根据当前客户id处理。 缺点:sql查出的列一定要有creater_firm_id
            buildSql =  "select t1.* from (" + buildSql + ") t1 where t1.creater_firm_id = '" + user.getFirmId() + "'";
        }
        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
        mpBoundSql.sql(buildSql);
    }

    private void processPlainSelect(PlainSelect plainSelect) {
        FromItem fromItem = plainSelect.getFromItem();
        if (fromItem instanceof Table) {
            Table fromTable = (Table) fromItem;
            if (IGNORE_TABLES.contains(fromTable.getName())) {
                log.info(StrBuilder.create(fromTable.getName()).append("表已被忽略 不进行数据权限过滤").toString());
                throw new ServiceException();
            }
            tableName = fromTable.getName();
        } else {
            processFromItem(fromItem);
        }
    }

    private void processSelectBody(SelectBody selectBody) {
        if (selectBody == null) {
            return;
        }
        if (selectBody instanceof PlainSelect) {
            processPlainSelect((PlainSelect) selectBody);
        } else if (selectBody instanceof WithItem) {
            WithItem withItem = (WithItem) selectBody;
            processSelectBody(withItem.getSubSelect().getSelectBody());
        } else {
            SetOperationList operationList = (SetOperationList) selectBody;
            List<SelectBody> selectBodys = operationList.getSelects();
            if (CollectionUtils.isNotEmpty(selectBodys)) {
                for (SelectBody body : selectBodys) {
                    processSelectBody(body);
                }
            }
        }
    }

    private void processFromItem(FromItem fromItem) {
        if (fromItem instanceof SubJoin) {
            SubJoin subJoin = (SubJoin) fromItem;
            if (subJoin.getJoinList() != null) {
                for (Join join : subJoin.getJoinList()) {
                    processJoin(join);
                }
            }
            if (subJoin.getLeft() != null) {
                processFromItem(subJoin.getLeft());
            }
        } else if (fromItem instanceof SubSelect) {
            SubSelect subSelect = (SubSelect) fromItem;
            if (subSelect.getSelectBody() != null) {
                processSelectBody(subSelect.getSelectBody());
            }
        } else if (fromItem instanceof ValuesList) {
            log.info("Perform a subquery, if you do not give us feedback");
        } else if (fromItem instanceof LateralSubSelect) {
            LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
            if (lateralSubSelect.getSubSelect() != null) {
                SubSelect subSelect = lateralSubSelect.getSubSelect();
                if (subSelect.getSelectBody() != null) {
                    processSelectBody(subSelect.getSelectBody());
                }
            }
        }
    }

    private void processJoin(Join join) {
        if (join.getRightItem() instanceof Table) {
            Table fromTable = (Table) join.getRightItem();
            if (IGNORE_TABLES.contains(fromTable.getName())) {
                log.info(StrBuilder.create(fromTable.getName()).append("表已被忽略 不进行数据权限过滤").toString());
                throw new ServiceException();
            }
            tableName = fromTable.getName();
        }
    }

}

2.公共字段自动填充

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.tz4cloud.common.core.obj.TzUser;
import com.tz4cloud.common.core.utils.TzUserInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;


/**
 * 公共字段自动填充
 *
 * @author wrx
 * @date 2022/7/19 10:52
 */
@Slf4j
public class TzMetaObjectHandler implements MetaObjectHandler {

    @Autowired
    TzUserInfoUtil tzUserInfoUtil;

    /**
     * 判断字段没有值才进行设置填充
     *
     * @param fieldName  数据库字段名称
     * @param fieldVal   要填充的字段值
     * @param metaObject 对象元数据信息
     */
    private void autoFill(String fieldName, Object fieldVal, MetaObject metaObject) {
        Object value = this.getFieldValByName(fieldName, metaObject);
        if (ObjectUtil.isNull(value) || StrUtil.isEmpty(String.valueOf(value))) {
            this.setFieldValByName(fieldName, fieldVal, metaObject);
        }
    }

    @Override
    public void insertFill(MetaObject metaObject) {
        log.debug("新增操作自动填充......");
        this.autoFill("createTime", new Date(), metaObject);
        TzUser user = tzUserInfoUtil.getUser();
        if (user != null) {
            this.autoFill("createrName", user.getUserCnName(), metaObject);
            this.autoFill("createrId", user.getUserId(), metaObject);
            this.autoFill("createrFirmId", user.getFirmId(), metaObject);
            this.autoFill("createrFirmName", user.getFirmName(), metaObject);
        }
        // 看业务要求,修改字段在新增时也要填充?
        this.updateFill(metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.debug("更新操作自动填充......");
        this.setFieldValByName("modifyTime", new Date(), metaObject);
        TzUser user = tzUserInfoUtil.getUser();
        if (user != null) {
            this.setFieldValByName("modifyName", user.getUserCnName(), metaObject);
        }
    }
}

3.全局配置类

/**
 * mybatis-plus 全局配置
 *
 * @author wrx
 * @date 2022/7/19 10:52
 */
@Configuration
public class MybatisPlusConfig {
    /**
     * 插件配置:1.阻止恶意或误操作的全表更新删除 2.分页插件
     *
     * @return MybatisPlusInterceptor
     * @author wrx
     * @date 2022/7/19 10:53
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //阻止恶意或误操作的全表更新删除
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        //权限过滤
        interceptor.addInnerInterceptor(dataScopeInterceptor());
        //分页插件
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        pageInterceptor.setMaxLimit(500L);
        // 开启 count 的 join 优化,只针对部分 left join
        pageInterceptor.setOptimizeJoin(true);
        interceptor.addInnerInterceptor(pageInterceptor);
        return interceptor;
    }

    @Bean
    public DataScopeInterceptor dataScopeInterceptor(){
        return new DataScopeInterceptor();
    }

    /**
     * 自动填充基础创建,修改人信息等
     *
     * @author wrx
     * @date 2022/7/19 10:53
     */
    @Bean
    public TzMetaObjectHandler tzMetaObjectHandler() {
        return new TzMetaObjectHandler();
    }
}

4.德鲁伊配置

/**
 * 德鲁伊内置监控页面配置  http://ip:端口/druid/index.html
 *
 * @author wrx
 * @date 2022/7/18 14:56
 */
@Configuration
public class DruidConfiguration {

    @Value("${spring.datasource.druid.config.loginUsername:admin}")
    private String loginUsername;
    @Value("${spring.datasource.druid.config.loginPassword:tz123456}")
    private String loginPassword;
    @Value("${spring.datasource.druid.config.resetEnable:true}")
    private String resetEnable;

    /**
     * druid
     * 注册一个StatViewServlet
     *
     * @return
     */
    @Bean
    public ServletRegistrationBean druidStatViewServlet() {
        //org.springframework.boot.context.embedded.ServletRegistrationBean提供类的进行注册.
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

        //白名单:
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
        //servletRegistrationBean.addInitParameter("deny","192.168.1.x");
        //登录查看信息的账号密码.
        servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
        servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
        //客户端的重置数据按钮是否有效
        servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
        return servletRegistrationBean;
    }

    /**
     * druid过滤器
     * 注册一个:filterRegistrationBean
     *
     */
    @Bean
    public FilterRegistrationBean druidStatFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        //添加过滤规则.
        filterRegistrationBean.addUrlPatterns("/*");
        //添加不需要忽略的格式信息.
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }

}
 类似资料: