Mybatis 工作原理以及拓展方法

1、 Mybatis的工作流程

  • 1、读取配置文件,根据XML配置文件创建Conifuration
  • 2、根据配置信息 创建SqlSessionFactory,SqlSessionFactory的生命周期是程序级,程序运行的时候建立起来,程序结束的时候消亡
  • 3、SqlSessionFactory 创建 SqlSession,SqlSession是过程级,一个方法开始时建立,方法结束应该关闭
  • 4、获取mapper接口的代理对象(DefaultSqlSession.getMapper()拿到Mapper接口对应的MapperProxy)
  • 5、执行增删改查方法
    5.1 调用DefaultSqlSession的增删改查(Executor);
    5.2 会创建一个StatementHandler对象(同时也会创建出ParameterHandler和ResultSetHandler);
    5.3 调用StatementHandler预编译参数以及设置参数值(使用ParameterHandler来给sql设置参数);
    5.4 调用StatementHandler的增删改查方法;
    5.5 ResultSetHandler封装结果;
image.png

2、Mybatis 核心对象

  • Configuration: MyBatis所有的配置信息都维持在Configuration对象之中
  • SqlSession: 负责和数据库进行交互,实现完成常对数据操作的增删改查功能
  • Executor: Mybatis执行器,调度的核心,负责SQL语句的生成和维护
  • StatementHandler :封装并操作Jdbc statement,例如设置参数,将statement结果集转化为List集合
  • ParameterHandler: 负责将用户传递的参数转换为Jdbc statement所需要的参数进行传递
  • ResultSetHandler: 负责将Jdbc返回的ResultSet结果集对象转换为List类型的集合
  • TypeHandler: 负责将java数据类型和Jdbc数据类型之间的转换和映射
  • MappedStatement: MappedStatement维护了一条mapper.xml文件里面 select 、update、delete、insert节点的封装
  • BoundSql: 表示动态生成的SQL语句以及相应的参数信息

3、Mybatis拦截器

在很多业务场景下我们需要去拦截sql,达到不入侵原有代码业务处理一些东西,比如:分页操作,数据权限过滤操作,SQL执行时间性能监控等等,Mybatis拦截器设计的思路是为了供用户灵活的实现自己的逻辑,而不动mybatis固有的逻辑。通过Mybatis拦截器我们能拦截某些方法的调用,我们可以选择在这些被拦截方法执行前后加上我们自己的逻辑,达到丰富方法的效果;也可以在执行这些方法的时候拦截住,转而实现自己设计的方法,而最后不执行被拦截的方法。

在mybatis中可被拦截的类型有四种(按照拦截顺序):

  • Executor:拦截执行器的方法;
  • ParameterHandler:拦截参数的处理;
  • ResultHandler:拦截结果集的处理;
  • StatementHandler:拦截Sql语法构建的处理;

拦截器一般在业务处理中用于:

  • 1、分页查询
  • 2、多租户添加条件过滤,即数据权限过滤操作
  • 3、对返回结果,过滤掉审计字段,敏感字段(比如手机号)
  • 4、对返回结果中的加密数据进行解密
  • 5、对新增数据自动添加创建人,创建时间,更新时间,更新人 ,对更新数据自动新增更新时间,更新人
  • 6、打印SQL执行日志、执行时间

3.1 分页拦截器

mybatisplus 分页拦截器 PaginationInnerInterceptor

    // 使用
   Page<Produce> producePage = new Page<>(1,1);
   Page<Produce> page = produceService.selectList(producePage);

拦截器的核心如下图,当我们的select方法的入参 属于(instanceof) IPage时(如上面的 producePage ),
才会执行分页;或者是当入参为Map时,查询map中是否存在属于(instanceof) IPage 的对象。

image.png

优点:通过Page<>泛型,使用简单,性能好;

缺点:如果项目中有很多以Map<String, Object>作为入参的查询方法,就算不是分页接口,每次都会被拦截过滤,影响性能,由于是对于高并发接口。
对于在Mapper 入参中使用 @Param 的查询方法,也会被拦截到,这会导致性能问题。

github分页拦截器 PageInterceptor

  Page<Object> pageHelper = PageHelper.startPage(pageNumber, pageSize, true);
  List<MallGoods> downDateList = mallGoodManager.queryDownGoodsList();

PageHelper 在startPage时,把分页信息写入 ThreadLocal,然后在拦截器中从ThreadLocal获取Page信息,如果存在,则是分页查询;不存在,则是普通查询

image.png
image.png
image.png
image.png
image.png

优点:不会校验普通查询;

缺点:使用ThreadLocal作为存储,需要先 set,然后get,最后remove;对于高并发接口,会有性能问题。

总结:对比上面两种分页的优缺点,还是使用github分页拦截器(使用PageHelper)比较好,但是对于高并发接口,最好是手动写分页,即手动写 select count(*)

3.2 MetaObjectHandler 自定义字段自动填充处理类

MetaObjectHandler接口是mybatisPlus为我们提供的的一个扩展接口,我们可以利用这个接口在我们插入或者更新数据的时候,为一些字段指定默认值。常见的比如createTm modifyTm creator modifier

@Configuration
public class MyMetaObjectHandler implements MetaObjectHandler {
    private static final Logger log = LoggerFactory.getLogger(MyMetaObjectHandler.class);
    private static final Integer NOT_DELETED = 0;

    public MyMetaObjectHandler() {
    }

    public void insertFill(MetaObject metaObject) {
        String createDate = "createDate";
        boolean hasCreateDate = this.hasProperty(metaObject, createDate);
        if (hasCreateDate) {
            this.setFieldValByName(createDate, new Date(), metaObject);
        }

        String createTm = "createTm";
        boolean hasCreateTm = this.hasProperty(metaObject, createTm);
        if (hasCreateTm) {
            this.setFieldValByName(createTm, new Date(), metaObject);
        }

        String updateDate = "updateDate";
        boolean hasUpdateDate = this.hasProperty(metaObject, updateDate);
        if (hasUpdateDate) {
            this.setFieldValByName(updateDate, new Date(), metaObject);
        }

        String modifyTm = "modifyTm";
        boolean hasModifyTm = this.hasProperty(metaObject, modifyTm);
        if (hasModifyTm) {
            this.setFieldValByName(modifyTm, new Date(), metaObject);
        }

        String deleteFlag = "deleteFlag";
        boolean hasDeleteFlag = this.hasProperty(metaObject, deleteFlag);
        if (hasDeleteFlag) {
            this.setFieldValByName(deleteFlag, NOT_DELETED, metaObject);
        }

        String empNum = ContextHoldUtil.getEmpNum();
        if (SfStrUtil.isNotBlank(empNum)) {
            String createUser = "createUser";
            boolean hasCreateUser = this.hasProperty(metaObject, createUser);
            if (hasCreateUser) {
                this.setFieldValByName(createUser, empNum, metaObject);
            }

            String creator = "creator";
            boolean hasCreator = this.hasProperty(metaObject, creator);
            if (hasCreator) {
                this.setFieldValByName(creator, empNum, metaObject);
            }

            String updateUser = "updateUser";
            boolean hasUpdateUser = this.hasProperty(metaObject, updateUser);
            if (hasUpdateUser) {
                this.setFieldValByName(updateUser, empNum, metaObject);
            }

            String modifier = "modifier";
            boolean hasModifier = this.hasProperty(metaObject, modifier);
            if (hasModifier) {
                this.setFieldValByName(modifier, empNum, metaObject);
            }
        }

    }

    public void updateFill(MetaObject metaObject) {
        String updateDate = "updateDate";
        boolean hasUpdateDate = this.hasProperty(metaObject, updateDate);
        if (hasUpdateDate) {
            this.setFieldValByName(updateDate, new Date(), metaObject);
        }

        String modifyTm = "modifyTm";
        boolean hasModifyTm = this.hasProperty(metaObject, modifyTm);
        if (hasModifyTm) {
            this.setFieldValByName(modifyTm, new Date(), metaObject);
        }

        String empNum = ContextHoldUtil.getEmpNum();
        if (SfStrUtil.isNotBlank(empNum)) {
            String updateUser = "updateUser";
            boolean hasUpdateUser = this.hasProperty(metaObject, updateUser);
            if (hasUpdateUser) {
                this.setFieldValByName(updateUser, empNum, metaObject);
            }

            String modifier = "modifier";
            boolean hasModifier = this.hasProperty(metaObject, modifier);
            if (hasModifier) {
                this.setFieldValByName(modifier, empNum, metaObject);
            }
        }

    }

    private boolean hasProperty(MetaObject metaObject, String fieldName) {
        return metaObject.hasGetter(fieldName) || metaObject.hasGetter("et." + fieldName);
    }
}

3.3 1、日志拦截器 —— 用于测试环境,sql美化,sql执行时间

在测试环境,通常我们会配置SQL日志打印,用于调试。
1、原生 org.apache.ibatis.logging.stdout.StdOutImpl

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@38cd0f91] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7182d8bf] will not be managed by Spring
==>  Preparing: select g.name as goodsName, g.goods_type as goodsType, g.goods_no as goodsNo, g.current_store as currentStore, gi.img_url as goodsImage, g.point_price as pointPrice, g.discount_price as discountPrice, g.rise_date as exchangeBeginTime, g.original_price as originPrice, g.category_code as categoryCode, g.sub_category_code as subCategoryCode, g.serial_num as serialNum, g.purchase_price as purchasePrice, g.exchange_mem_level as exchangeLevelLimit, g.sale_channel as saleChannel, g.label as label, g.like_amount as likeAmount, g.state as state from mall_goods g left join mall_goods_img gi on g.goods_no = gi.goods_no and gi.img_type = 1 where g.category_code = ? and g.state = 1 and g.mall = 'Point_Mall' and g.if_life_privilege in (0,2) and (g.current_store>0 or g.current_store=-1) order by g.serial_num asc,g.id desc limit ?,? 
==> Parameters: XPZQ(String), 1(Integer), 10(Integer)
<==    Columns: goodsName, goodsType, goodsNo, currentStore, goodsImage, pointPrice, discountPrice, exchangeBeginTime, originPrice, categoryCode, subCategoryCode, serialNum, purchasePrice, exchangeLevelLimit, saleChannel, label, likeAmount, state
<==        Row: 唯品会券对接_0803 , SFSJ, GOODS20220803185335009, 499996, https://fff, 2, 0, 2022-08-03 18:53:37.0, 2000, XPZQ, null, 1, 0, 0, 0, [{"type":3}], 0, 1
<==        Row: 井1, SFM, GOODS20220713112243913, 17, https://aaa, 1, 0, 2022-08-03 19:04:20.0, 100, XPZQ, null, 1, 0, 0, 0, [{"type":3}], 4, 1
<==        Row: #14, SFM, GOODS20220707181501415, 19, bbb, 10, 0, 2022-07-30 15:17:04.0, 10000, XPZQ, null, 1, 0, 0, 0, [{"type":3}], 1, 1
<==        Row: 0601版本券码导入10W张验证, SFW, GOODS20220713181357698, 500000, https:/ccc, 1, 0, 2022-07-29 15:07:13.0, 12, XPZQ, null, 2, 0, 0, 0, [{"type":3}], 0, 1
<==        Row: 礼包名称-实物xy, SFM, GOODS20220713075614668, 20, https://ddd, 1, 0, 2022-07-29 15:34:31.0, 100, XPZQ, null, 2, 0, 0, 0, [{"type":3}], 0, 1
<==        Row: #15, SFM, GOODS20220705173709700, 14, https://eee, 1, 0, 2022-07-30 11:42:00.0, 10000, XPZQ, null, 3, 0, 0, 0, [{"type":3}], 6, 1
<==      Total: 6
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@38cd0f91]

优点:配置简单
缺点:SQL语句和参数分开,且结果直接打印,不直观

2、mybatisplus 支持p6spy

image.png

优点:配置复杂
缺点:SQL语句没有美化

3、手动开发 MybatisLogInterceptor

@Intercepts({@Signature(
    type = Executor.class,
    method = "update",
    args = {MappedStatement.class, Object.class}
), @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
@Component
public class MybatisLogInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(MybatisLogInterceptor.class);
    private Properties properties;
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static final String DEFAULT_SQL_LOG_SHOW_SWITCH = "false";
    @Value("${sqlLogShowSwitch}")
    private String sqlLogShowSwitch;

    public MybatisLogInterceptor() {
    }

    public Object intercept(Invocation invocation) throws Throwable {
        if (StrUtil.equals(this.sqlLogShowSwitch, "false")) {
            return invocation.proceed();
        } else {
            try {
                Object[] args = invocation.getArgs();
                MappedStatement mappedStatement = (MappedStatement)args[0];
                Object parameter = null;
                if (args.length > 1) {
                    parameter = args[1];
                }

                String sqlId = mappedStatement.getId();
                BoundSql boundSql = null;
                if (args.length == 6) {
                    boundSql = (BoundSql)args[5];
                }

                if (boundSql == null) {
                    boundSql = mappedStatement.getBoundSql(parameter);
                }

                Configuration configuration = mappedStatement.getConfiguration();
                long start = System.currentTimeMillis();
                Object returnValue = invocation.proceed();
                long end = System.currentTimeMillis();
                long time = end - start;
                String sql = getSql(configuration, boundSql, sqlId, time);
                log.info("开始执行sql==> " + sql.split("(\\^ZHIKE\\^)")[0] + "\n" + "执行SQL==>" + (new PrettyFormatterSqlUtil()).getPrettySql(sql.split("(\\^ZHIKE\\^)")[1]) + "\n" + "<==执行总耗时【 " + time + " ms】\n");
                String returnStr = "";
                if (returnValue != null) {
                    returnStr = SfJsonUtil.toJsonStr(returnValue);
                }

                log.info("执行sql结果:{}", returnStr.length() >= 2000 ? returnStr.substring(0, 2000) : returnStr);
                return returnValue;
            } catch (Throwable var17) {
                log.error("执行sql报错!errMsg:{}", var17.getMessage(), var17);
                return invocation.proceed();
            }
        }
    }

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

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId, long time) {
        String sql = showSql(configuration, boundSql);
        StringBuilder builder = new StringBuilder(100);
        builder.append(sqlId);
        builder.append("^ZHIKE^");
        builder.append(sql);
        builder.append("^ZHIKE^");
        builder.append(time);
        builder.append("ms");
        return builder.toString();
    }

    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            value = "'" + SIMPLE_DATE_FORMAT.format(obj) + "'";
        } else if (obj != null) {
            value = obj.toString();
        } else {
            value = "";
        }

        return value;
    }

    public static String showSql(Configuration configuration, BoundSql boundSql) {
        try {
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
            if (parameterMappings.size() > 0 && parameterObject != null) {
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    Iterator var7 = parameterMappings.iterator();

                    while(var7.hasNext()) {
                        ParameterMapping parameterMapping = (ParameterMapping)var7.next();
                        String propertyName = parameterMapping.getProperty();
                        Object obj;
                        if (metaObject.hasGetter(propertyName)) {
                            obj = metaObject.getValue(propertyName);
                            sql = sql.replaceFirst("\\?", getParameterValue(obj));
                        } else if (boundSql.hasAdditionalParameter(propertyName)) {
                            obj = boundSql.getAdditionalParameter(propertyName);
                            sql = sql.replaceFirst("\\?", getParameterValue(obj));
                        }
                    }
                }
            }

            return sql;
        } catch (Exception var11) {
            return "";
        }
    }
}

SQL美化工具类

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrettyFormatterSqlUtil {
    private static final Logger log = LoggerFactory.getLogger(PrettyFormatterSqlUtil.class);
    private static final Set<String> BEGIN_CLAUSES = new HashSet();
    private static final Set<String> END_CLAUSES = new HashSet();
    private static final Set<String> LOGICAL = new HashSet();
    private static final Set<String> QUANTIFIERS = new HashSet();
    private static final Set<String> DML = new HashSet();
    private static final Set<String> MISC = new HashSet();
    public static final String WHITESPACE = " \n\r\f\t";
    static final String INDENT_STRING = "    ";
    static final String INITIAL = "\n    ";

    public PrettyFormatterSqlUtil() {
    }

    public String getPrettySql(String source) {
        return (new PrettyFormatterSqlUtil.FormatProcess(source)).perform();
    }

    static {
        BEGIN_CLAUSES.add("left");
        BEGIN_CLAUSES.add("right");
        BEGIN_CLAUSES.add("inner");
        BEGIN_CLAUSES.add("outer");
        BEGIN_CLAUSES.add("group");
        BEGIN_CLAUSES.add("order");
        END_CLAUSES.add("where");
        END_CLAUSES.add("set");
        END_CLAUSES.add("having");
        END_CLAUSES.add("join");
        END_CLAUSES.add("from");
        END_CLAUSES.add("by");
        END_CLAUSES.add("join");
        END_CLAUSES.add("into");
        END_CLAUSES.add("union");
        LOGICAL.add("and");
        LOGICAL.add("or");
        LOGICAL.add("when");
        LOGICAL.add("else");
        LOGICAL.add("end");
        QUANTIFIERS.add("in");
        QUANTIFIERS.add("all");
        QUANTIFIERS.add("exists");
        QUANTIFIERS.add("some");
        QUANTIFIERS.add("any");
        DML.add("insert");
        DML.add("update");
        DML.add("delete");
        MISC.add("select");
        MISC.add("on");
    }

    private static class FormatProcess {
        boolean beginLine = true;
        boolean afterBeginBeforeEnd = false;
        boolean afterByOrSetOrFromOrSelect = false;
        boolean afterValues = false;
        boolean afterOn = false;
        boolean afterBetween = false;
        boolean afterInsert = false;
        int inFunction = 0;
        int parensSinceSelect = 0;
        private LinkedList<Integer> parenCounts = new LinkedList();
        private LinkedList<Boolean> afterByOrFromOrSelects = new LinkedList();
        int indent = 1;
        StringBuilder result = new StringBuilder();
        StringTokenizer tokens;
        String lastToken;
        String token;
        String lcToken;

        public FormatProcess(String sql) {
            this.tokens = new StringTokenizer(sql, "()+*/-=<>'`\"[], \n\r\f\t", true);
        }

        public String perform() {
            this.result.append("\n    ");

            while(this.tokens.hasMoreTokens()) {
                this.token = this.tokens.nextToken();
                this.lcToken = this.token.toLowerCase();
                String t;
                if ("'".equals(this.token)) {
                    do {
                        t = this.tokens.nextToken();
                        this.token = this.token + t;
                    } while(!"'".equals(t) && this.tokens.hasMoreTokens());
                } else if ("\"".equals(this.token)) {
                    do {
                        t = this.tokens.nextToken();
                        this.token = this.token + t;
                    } while(!"\"".equals(t));
                }

                if (this.afterByOrSetOrFromOrSelect && ",".equals(this.token)) {
                    this.commaAfterByOrFromOrSelect();
                } else if (this.afterOn && ",".equals(this.token)) {
                    this.commaAfterOn();
                } else if ("(".equals(this.token)) {
                    this.openParen();
                } else if (")".equals(this.token)) {
                    this.closeParen();
                } else if (PrettyFormatterSqlUtil.BEGIN_CLAUSES.contains(this.lcToken)) {
                    this.beginNewClause();
                } else if (PrettyFormatterSqlUtil.END_CLAUSES.contains(this.lcToken)) {
                    this.endNewClause();
                } else if ("select".equals(this.lcToken)) {
                    this.select();
                } else if (PrettyFormatterSqlUtil.DML.contains(this.lcToken)) {
                    this.updateOrInsertOrDelete();
                } else if ("values".equals(this.lcToken)) {
                    this.values();
                } else if ("on".equals(this.lcToken)) {
                    this.on();
                } else if (this.afterBetween && "and".equals(this.lcToken)) {
                    this.misc();
                    this.afterBetween = false;
                } else if (PrettyFormatterSqlUtil.LOGICAL.contains(this.lcToken)) {
                    this.logical();
                } else if (isWhitespace(this.token)) {
                    this.white();
                } else {
                    this.misc();
                }

                if (!isWhitespace(this.token)) {
                    this.lastToken = this.lcToken;
                }
            }

            return this.result.toString();
        }

        private void commaAfterOn() {
            this.out();
            --this.indent;
            this.newline();
            this.afterOn = false;
            this.afterByOrSetOrFromOrSelect = true;
        }

        private void commaAfterByOrFromOrSelect() {
            this.out();
            this.newline();
        }

        private void logical() {
            if ("end".equals(this.lcToken)) {
                --this.indent;
            }

            this.newline();
            this.out();
            this.beginLine = false;
        }

        private void on() {
            ++this.indent;
            this.afterOn = true;
            this.newline();
            this.out();
            this.beginLine = false;
        }

        private void misc() {
            this.out();
            if ("between".equals(this.lcToken)) {
                this.afterBetween = true;
            }

            if (this.afterInsert) {
                this.newline();
                this.afterInsert = false;
            } else {
                this.beginLine = false;
                if ("case".equals(this.lcToken)) {
                    ++this.indent;
                }
            }

        }

        private void white() {
            if (!this.beginLine) {
                this.result.append(" ");
            }

        }

        private void updateOrInsertOrDelete() {
            this.out();
            ++this.indent;
            this.beginLine = false;
            if ("update".equals(this.lcToken)) {
                this.newline();
            }

            if ("insert".equals(this.lcToken)) {
                this.afterInsert = true;
            }

        }

        private void select() {
            this.out();
            ++this.indent;
            this.newline();
            this.parenCounts.addLast(this.parensSinceSelect);
            this.afterByOrFromOrSelects.addLast(this.afterByOrSetOrFromOrSelect);
            this.parensSinceSelect = 0;
            this.afterByOrSetOrFromOrSelect = true;
        }

        private void out() {
            this.result.append(this.token);
        }

        private void endNewClause() {
            if (!this.afterBeginBeforeEnd) {
                --this.indent;
                if (this.afterOn) {
                    --this.indent;
                    this.afterOn = false;
                }

                this.newline();
            }

            this.out();
            if (!"union".equals(this.lcToken)) {
                ++this.indent;
            }

            this.newline();
            this.afterBeginBeforeEnd = false;
            this.afterByOrSetOrFromOrSelect = "by".equals(this.lcToken) || "set".equals(this.lcToken) || "from".equals(this.lcToken);
        }

        private void beginNewClause() {
            if (!this.afterBeginBeforeEnd) {
                if (this.afterOn) {
                    --this.indent;
                    this.afterOn = false;
                }

                --this.indent;
                this.newline();
            }

            this.out();
            this.beginLine = false;
            this.afterBeginBeforeEnd = true;
        }

        private void values() {
            --this.indent;
            this.newline();
            this.out();
            ++this.indent;
            this.newline();
            this.afterValues = true;
        }

        private void closeParen() {
            --this.parensSinceSelect;
            if (this.parensSinceSelect < 0) {
                --this.indent;
                this.parensSinceSelect = (Integer)this.parenCounts.removeLast();
                this.afterByOrSetOrFromOrSelect = (Boolean)this.afterByOrFromOrSelects.removeLast();
            }

            if (this.inFunction > 0) {
                --this.inFunction;
                this.out();
            } else {
                if (!this.afterByOrSetOrFromOrSelect) {
                    --this.indent;
                    this.newline();
                }

                this.out();
            }

            this.beginLine = false;
        }

        private void openParen() {
            if (isFunctionName(this.lastToken) || this.inFunction > 0) {
                ++this.inFunction;
            }

            this.beginLine = false;
            if (this.inFunction > 0) {
                this.out();
            } else {
                this.out();
                if (!this.afterByOrSetOrFromOrSelect) {
                    ++this.indent;
                    this.newline();
                    this.beginLine = true;
                }
            }

            ++this.parensSinceSelect;
        }

        private static boolean isFunctionName(String tok) {
            char begin = tok.charAt(0);
            boolean isIdentifier = Character.isJavaIdentifierStart(begin) || '"' == begin;
            return isIdentifier && !PrettyFormatterSqlUtil.LOGICAL.contains(tok) && !PrettyFormatterSqlUtil.END_CLAUSES.contains(tok) && !PrettyFormatterSqlUtil.QUANTIFIERS.contains(tok) && !PrettyFormatterSqlUtil.DML.contains(tok) && !PrettyFormatterSqlUtil.MISC.contains(tok);
        }

        private static boolean isWhitespace(String token) {
            return " \n\r\f\t".indexOf(token) >= 0;
        }

        private void newline() {
            this.result.append("\n");

            for(int i = 0; i < this.indent; ++i) {
                this.result.append("    ");
            }

            this.beginLine = true;
        }
    }
}

打印结果:

2022-09-09 20:49:47.768[http-nio-9026-exec-1] MybatisLogInterceptor.intercept(81) INFO  [c3c83832b58f49579561e92bceafdec1] :开始执行sql==> com.sf.cemp.goods.mallgoods.repository.mapper.MallGoodsMapper.selectPointMallListForApp
执行SQL==>
    select
        g.name as goodsName,
        g.goods_type as goodsType,
        g.goods_no as goodsNo,
        g.current_store as currentStore,
        gi.img_url as goodsImage,
        g.point_price as pointPrice,
        g.discount_price as discountPrice,
        g.rise_date as exchangeBeginTime,
        g.original_price as originPrice,
        g.category_code as categoryCode,
        g.sub_category_code as subCategoryCode,
        g.serial_num as serialNum,
        g.purchase_price as purchasePrice,
        g.exchange_mem_level as exchangeLevelLimit,
        g.sale_channel as saleChannel,
        g.label as label,
        g.like_amount as likeAmount,
        g.state as state 
    from
        mall_goods g 
    left join
        mall_goods_img gi 
            on g.goods_no = gi.goods_no 
            and gi.img_type = 1 
    where
        g.category_code = 'XPZQ' 
        and g.state = 1 
        and g.mall = 'Point_Mall' 
        and g.if_life_privilege in (
            0,2
        ) 
        and (
            g.current_store>0 
            or g.current_store=-1
        ) 
    order by
        g.serial_num asc,
        g.id desc limit 1,
        10
<==执行总耗时【 281 ms】


2022-09-09 20:52:03.886[http-nio-9026-exec-3] MybatisLogInterceptor.intercept(88) INFO  [1a97f40126b9410aad411dc4bc327eca] :执行sql结果:[{"categoryCode":"XPZQ","currentDate":1662727923886,"currentStore":499995,"discountPrice":0,"exchangeBeginTime":1661242860000,"exchangeLevelLimit":0,"goodsImage":"https://cemp.sit.sf-express.com/packetImg/853523/202208/20220823161800408.png","goodsName":"商品预热验证-cyr","goodsNo":"GOODS20220823161825360","goodsType":"SFW","label":"[{\"type\":3}]","likeAmount":0,"originPrice":3300,"pointPrice":33,"purchasePrice":0,"saleChannel":"0","serialNum":1,"state":1}]

总结:自定义SQL日志拦截器使用最方便

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容