Mybatis 工作原理以及拓展方法

1、 Mybatis的工作流程

  • 1、讀取配置文件,根據(jù)XML配置文件創(chuàng)建Conifuration
  • 2、根據(jù)配置信息 創(chuàng)建SqlSessionFactory,SqlSessionFactory的生命周期是程序級,程序運行的時候建立起來,程序結束的時候消亡
  • 3、SqlSessionFactory 創(chuàng)建 SqlSession,SqlSession是過程級,一個方法開始時建立,方法結束應該關閉
  • 4、獲取mapper接口的代理對象(DefaultSqlSession.getMapper()拿到Mapper接口對應的MapperProxy)
  • 5、執(zhí)行增刪改查方法
    5.1 調(diào)用DefaultSqlSession的增刪改查(Executor);
    5.2 會創(chuàng)建一個StatementHandler對象(同時也會創(chuàng)建出ParameterHandler和ResultSetHandler);
    5.3 調(diào)用StatementHandler預編譯參數(shù)以及設置參數(shù)值(使用ParameterHandler來給sql設置參數(shù));
    5.4 調(diào)用StatementHandler的增刪改查方法;
    5.5 ResultSetHandler封裝結果;
image.png

2、Mybatis 核心對象

  • Configuration: MyBatis所有的配置信息都維持在Configuration對象之中
  • SqlSession: 負責和數(shù)據(jù)庫進行交互,實現(xiàn)完成常對數(shù)據(jù)操作的增刪改查功能
  • Executor: Mybatis執(zhí)行器,調(diào)度的核心,負責SQL語句的生成和維護
  • StatementHandler :封裝并操作Jdbc statement,例如設置參數(shù),將statement結果集轉(zhuǎn)化為List集合
  • ParameterHandler: 負責將用戶傳遞的參數(shù)轉(zhuǎn)換為Jdbc statement所需要的參數(shù)進行傳遞
  • ResultSetHandler: 負責將Jdbc返回的ResultSet結果集對象轉(zhuǎn)換為List類型的集合
  • TypeHandler: 負責將java數(shù)據(jù)類型和Jdbc數(shù)據(jù)類型之間的轉(zhuǎn)換和映射
  • MappedStatement: MappedStatement維護了一條mapper.xml文件里面 select 、update、delete、insert節(jié)點的封裝
  • BoundSql: 表示動態(tài)生成的SQL語句以及相應的參數(shù)信息

3、Mybatis攔截器

在很多業(yè)務場景下我們需要去攔截sql,達到不入侵原有代碼業(yè)務處理一些東西,比如:分頁操作,數(shù)據(jù)權限過濾操作,SQL執(zhí)行時間性能監(jiān)控等等,Mybatis攔截器設計的思路是為了供用戶靈活的實現(xiàn)自己的邏輯,而不動mybatis固有的邏輯。通過Mybatis攔截器我們能攔截某些方法的調(diào)用,我們可以選擇在這些被攔截方法執(zhí)行前后加上我們自己的邏輯,達到豐富方法的效果;也可以在執(zhí)行這些方法的時候攔截住,轉(zhuǎn)而實現(xiàn)自己設計的方法,而最后不執(zhí)行被攔截的方法。

在mybatis中可被攔截的類型有四種(按照攔截順序):

  • Executor:攔截執(zhí)行器的方法;
  • ParameterHandler:攔截參數(shù)的處理;
  • ResultHandler:攔截結果集的處理;
  • StatementHandler:攔截Sql語法構建的處理;

攔截器一般在業(yè)務處理中用于:

  • 1、分頁查詢
  • 2、多租戶添加條件過濾,即數(shù)據(jù)權限過濾操作
  • 3、對返回結果,過濾掉審計字段,敏感字段(比如手機號)
  • 4、對返回結果中的加密數(shù)據(jù)進行解密
  • 5、對新增數(shù)據(jù)自動添加創(chuàng)建人,創(chuàng)建時間,更新時間,更新人 ,對更新數(shù)據(jù)自動新增更新時間,更新人
  • 6、打印SQL執(zhí)行日志、執(zhí)行時間

3.1 分頁攔截器

mybatisplus 分頁攔截器 PaginationInnerInterceptor

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

攔截器的核心如下圖,當我們的select方法的入?yún)?屬于(instanceof) IPage時(如上面的 producePage ),
才會執(zhí)行分頁;或者是當入?yún)镸ap時,查詢map中是否存在屬于(instanceof) IPage 的對象。

image.png

優(yōu)點:通過Page<>泛型,使用簡單,性能好;

缺點:如果項目中有很多以Map<String, Object>作為入?yún)⒌牟樵兎椒ǎ退悴皇欠猪摻涌?,每次都會被攔截過濾,影響性能,由于是對于高并發(fā)接口。
對于在Mapper 入?yún)⒅惺褂?@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

優(yōu)點:不會校驗普通查詢;

缺點:使用ThreadLocal作為存儲,需要先 set,然后get,最后remove;對于高并發(fā)接口,會有性能問題。

總結:對比上面兩種分頁的優(yōu)缺點,還是使用github分頁攔截器(使用PageHelper)比較好,但是對于高并發(fā)接口,最好是手動寫分頁,即手動寫 select count(*)

3.2 MetaObjectHandler 自定義字段自動填充處理類

MetaObjectHandler接口是mybatisPlus為我們提供的的一個擴展接口,我們可以利用這個接口在我們插入或者更新數(shù)據(jù)的時候,為一些字段指定默認值。常見的比如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、日志攔截器 —— 用于測試環(huán)境,sql美化,sql執(zhí)行時間

在測試環(huán)境,通常我們會配置SQL日志打印,用于調(diào)試。
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]

優(yōu)點:配置簡單
缺點:SQL語句和參數(shù)分開,且結果直接打印,不直觀

2、mybatisplus 支持p6spy

image.png

優(yōu)點:配置復雜
缺點:SQL語句沒有美化

3、手動開發(fā) 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("開始執(zhí)行sql==> " + sql.split("(\\^ZHIKE\\^)")[0] + "\n" + "執(zhí)行SQL==>" + (new PrettyFormatterSqlUtil()).getPrettySql(sql.split("(\\^ZHIKE\\^)")[1]) + "\n" + "<==執(zhí)行總耗時【 " + time + " ms】\n");
                String returnStr = "";
                if (returnValue != null) {
                    returnStr = SfJsonUtil.toJsonStr(returnValue);
                }

                log.info("執(zhí)行sql結果:{}", returnStr.length() >= 2000 ? returnStr.substring(0, 2000) : returnStr);
                return returnValue;
            } catch (Throwable var17) {
                log.error("執(zhí)行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] :開始執(zhí)行sql==> com.sf.cemp.goods.mallgoods.repository.mapper.MallGoodsMapper.selectPointMallListForApp
執(zhí)行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
<==執(zhí)行總耗時【 281 ms】


2022-09-09 20:52:03.886[http-nio-9026-exec-3] MybatisLogInterceptor.intercept(88) INFO  [1a97f40126b9410aad411dc4bc327eca] :執(zhí)行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日志攔截器使用最方便

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容