最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis自定義攔截器實(shí)現(xiàn)優(yōu)化SQL日志輸出

 更新時(shí)間:2026年03月16日 08:43:41   作者:湯姆yu  
這篇文章主要介紹了優(yōu)化MyBatisPlus SQL日志輸出的方案,針對(duì)默認(rèn)日志格式存在的不足,例如缺少時(shí)間、可讀性差、存儲(chǔ)成本高等,希望對(duì)大家有所幫助

1、背景

MyBatis Plus 通過(guò)配置文件中設(shè)置 log-impl 屬性來(lái)指定日志實(shí)現(xiàn),以打印 SQL 語(yǔ)句。

mybatis-plus:
  configuration:
    log-impl:org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    org.ylzl.eden.demo.mapper:DEBUG

打印出來(lái)的 SQL 內(nèi)容如下:

==>  Preparing: SELECT id,login,email,activated,locked,lang_key,activation_key,reset_key,reset_date,created_by,created_date,last_modified_by,last_modified_date FROM demo_user WHERE id=?
==> Parameters: 1(Long)
<==  Columns: ID, LOGIN, EMAIL, ACTIVATED, LOCKED, LANG_KEY, ACTIVATION_KEY, RESET_KEY, RESET_DATE, CREATED_BY, CREATED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE
<==  Row: 1, admin, 1813986321@qq.com, TRUE, FALSE, zh-cn, null, null, null, system, 2025-02-10 22:31:03.818, system, null
<==  Total: 1

然而,默認(rèn)的日志輸出格式存在以下不足:

  • 缺少日志時(shí)間,無(wú)法快速定位 SQL 執(zhí)行時(shí)間。
  • SQL 語(yǔ)句可讀性差,復(fù)雜的 SQL 語(yǔ)句難以閱讀。
  • 日志存儲(chǔ)成本高:SQL 模板占用較多字符,增加了日志存儲(chǔ)成本。

2、目標(biāo)

通過(guò) MyBatis 的攔截器實(shí)現(xiàn) SQL 原始語(yǔ)句的打印。

3、實(shí)現(xiàn)

首先,自定義 MyBatis 攔截器,實(shí)現(xiàn) org.apache.ibatis.plugin.Interceptor 接口。

@Intercepts({
@Signature(method = "query", type = Executor.class, args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
 @Signature(method= "query", type = Executor.class, args= {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
 @Signature(method= "update", type = Executor.class, args= {MappedStatement.class, Object.class})
})
publicclassMybatisSqlLogInterceptorimplementsInterceptor{
privatestaticfinal Logger log = LoggerFactory.getLogger("MybatisSqlLog");
private Duration slownessThreshold = Duration.ofMillis(1000);
@Override
public Object intercept(Invocation invocation)throws Throwable {
  MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
  String mapperId = mappedStatement.getId();
  String originalSql = MybatisUtils.getSql(mappedStatement, invocation);
long start = SystemClock.now();
  Object result = invocation.proceed();
long duration = SystemClock.now() - start;
        // 當(dāng) SQL 執(zhí)行超過(guò)我們?cè)O(shè)置的閾值,轉(zhuǎn)為 WARN 級(jí)別 
if (Duration.ofMillis(duration).compareTo(slownessThreshold) < 0) {
   log.info("{} execute sql: {} ({} ms)", mapperId, originalSql, duration);
  } else {
   log.warn("{} execute sql took more than {} ms: {} ({} ms)", mapperId, slownessThreshold.toMillis(), originalSql, duration);
  }
return result;
 }
@Override
public Object plugin(Object target){
if (target instanceof Executor) {
   return Plugin.wrap(target, this);
  }
return target;
 }
    // 設(shè)置慢 SQL 閾值,單位為秒
publicvoidsetSlownessThreshold(Duration slownessThreshold){
this.slownessThreshold = slownessThreshold;
 }
}

筆者編寫(xiě)了一個(gè)工具類(lèi)負(fù)責(zé)解析 MyBatis 執(zhí)行語(yǔ)句,還原為可執(zhí)行的 SQL 內(nèi)容。

@UtilityClass
publicclassMybatisUtils{

    privatestaticfinal Pattern PARAMETER_PATTERN = Pattern.compile("\\?");

public String getSql(MappedStatement mappedStatement, Invocation invocation){
  Object parameter = null;
if (invocation.getArgs().length > 1) {
   parameter = invocation.getArgs()[1];
  }
  BoundSql boundSql = mappedStatement.getBoundSql(parameter);
  Configuration configuration = mappedStatement.getConfiguration();
return resolveSql(configuration, boundSql);
 }

privatestatic String resolveSql(Configuration configuration, BoundSql boundSql){
  Object parameterObject = boundSql.getParameterObject();
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (!parameterMappings.isEmpty() && parameterObject != null) {
   TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
   if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
    sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(resolveParameterValue(parameterObject)));

   } else {
    MetaObject metaObject = configuration.newMetaObject(parameterObject);
    Matcher matcher = PARAMETER_PATTERN.matcher(sql);
    StringBuffer sqlBuffer = new StringBuffer();
    for (ParameterMapping parameterMapping : parameterMappings) {
     String propertyName = parameterMapping.getProperty();
     Object obj = null;
     if (metaObject.hasGetter(propertyName)) {
      obj = metaObject.getValue(propertyName);
     } elseif (boundSql.hasAdditionalParameter(propertyName)) {
      obj = boundSql.getAdditionalParameter(propertyName);
     }
     if (matcher.find()) {
      matcher.appendReplacement(sqlBuffer, Matcher.quoteReplacement(resolveParameterValue(obj)));
     }
    }
    matcher.appendTail(sqlBuffer);
    sql = sqlBuffer.toString();
   }
  }
return sql;
 }

privatestatic String resolveParameterValue(Object obj){
if (obj instanceof CharSequence) {
   return"'" + obj + "'";
  }
if (obj instanceof Date) {
   DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
   return"'" + formatter.format(obj) + "'";
  }
return obj == null ? "" : String.valueOf(obj);
 }
}

將 MyBatis 攔截器設(shè)置為 Spring 自動(dòng)裝配。

@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@ConditionalOnBean(SqlSessionFactory.class)
@ConditionalOnProperty(name= "mybatis.plugin.sql-log.enabled")
@EnableConfigurationProperties({MybatisPluginProperties.class})
@RequiredArgsConstructor
@Slf4j
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Configuration(proxyBeanMethods= false)
publicclassMybatisPluginAutoConfiguration{

privatefinal MybatisPluginProperties mybatisPluginProperties;

@Bean
public MybatisSqlLogInterceptor mybatisSqlLogInterceptor(){
  MybatisSqlLogInterceptor interceptor = new MybatisSqlLogInterceptor();
  interceptor.setSlownessThreshold(mybatisPluginProperties.getSqlLog().getSlownessThreshold());
return interceptor;
 }
}

@Data
@ConfigurationProperties(prefix = "mybatis.plugin")
publicclassMybatisPluginProperties{

privatefinal SqlLog sqlLog = new SqlLog();

@Data
publicstaticclassSqlLog{

privateboolean enabled = true;

private Duration slownessThreshold = Duration.ofMillis(1000);
 }
}

當(dāng)項(xiàng)目配置了屬性 mybatis.plugin.sql-log.enabled=true 時(shí),SQL 攔截將生效,打印的內(nèi)容如下:

2024-02-10 23:03:01.845 INFO  [dev] [XNIO-1 task-1] org.ylzl.eden.demo.infrastructure.user.database.UserMapper.selectById execute sql: SELECT id,login,email,activated,locked,lang_key,activation_key,reset_key,reset_date,created_by,created_date,last_modified_by,last_modified_date FROM demo_user WHERE id=1 (10 ms)

這種日志格式比較符合我們實(shí)際的生產(chǎn)要求:提供日志時(shí)間、可運(yùn)行的 SQL、執(zhí)行耗時(shí)。

4、產(chǎn)出

團(tuán)隊(duì)引入這個(gè)組件后,在定位生產(chǎn) SQL 問(wèn)題時(shí),比原來(lái)清晰多了,并且,日志文件縮減了 30% 存儲(chǔ)成本。

到此這篇關(guān)于MyBatis自定義攔截器實(shí)現(xiàn)優(yōu)化SQL日志輸出的文章就介紹到這了,更多相關(guān)MyBatis自定義攔截器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)現(xiàn)5種限流算法及7種限流方式

    Java實(shí)現(xiàn)5種限流算法及7種限流方式

    本文主要介紹了Java實(shí)現(xiàn)5種限流算法及7種限流方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 基于多線程并發(fā)的常見(jiàn)問(wèn)題(詳解)

    基于多線程并發(fā)的常見(jiàn)問(wèn)題(詳解)

    下面小編就為大家?guī)?lái)一篇基于多線程并發(fā)的常見(jiàn)問(wèn)題(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • plantuml畫(huà)圖實(shí)現(xiàn)代碼畫(huà)時(shí)序圖UML用例圖

    plantuml畫(huà)圖實(shí)現(xiàn)代碼畫(huà)時(shí)序圖UML用例圖

    這篇文章主要為大家介紹了plantuml畫(huà)圖實(shí)現(xiàn)代碼畫(huà)時(shí)序圖示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Spring接口版本控制方案及RequestMappingHandlerMapping接口介紹(最新推薦)

    Spring接口版本控制方案及RequestMappingHandlerMapping接口介紹(最新推薦)

    RequestMappingHandlerMapping接口是Spring MVC中的一個(gè)核心組件,負(fù)責(zé)處理請(qǐng)求映射和處理器的匹配這篇文章主要介紹了Spring接口版本控制方案及RequestMappingHandlerMapping接口介紹,需要的朋友可以參考下
    2024-07-07
  • Spring Boot全局異常處理與日志監(jiān)控全解析

    Spring Boot全局異常處理與日志監(jiān)控全解析

    本文給大家介紹Spring Boot全局異常處理與日志監(jiān)控全解析,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-08-08
  • MyBatis中的配置文件詳解

    MyBatis中的配置文件詳解

    在?MyBatis?中,配置文件分為?全局配置文件(核心配置文件)和映射配置文件,本文給大家介紹MyBatis中的配置文件相關(guān)知識(shí),感興趣的朋友一起看看吧
    2023-10-10
  • 使用Spring自身提供的地址匹配工具匹配URL操作

    使用Spring自身提供的地址匹配工具匹配URL操作

    這篇文章主要介紹了使用Spring自身提供的地址匹配工具匹配URL操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • java中TestNG使用教程詳解

    java中TestNG使用教程詳解

    TestNG是Java中的一個(gè)測(cè)試框架, 類(lèi)似于JUnit 和NUnit, 本文主要介紹了java中TestNG使用教程詳解,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-12-12
  • 關(guān)于@PropertySource配置的用法解析

    關(guān)于@PropertySource配置的用法解析

    這篇文章主要介紹了關(guān)于@PropertySource配置的用法解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java集合框架 arrayblockingqueue應(yīng)用分析

    java集合框架 arrayblockingqueue應(yīng)用分析

    ArrayBlockingQueue是一個(gè)由數(shù)組支持的有界阻塞隊(duì)列。此隊(duì)列按 FIFO(先進(jìn)先出)原則對(duì)元素進(jìn)行排序。隊(duì)列的頭部 是在隊(duì)列中存在時(shí)間最長(zhǎng)的元素
    2012-11-11

最新評(píng)論

屏东县| 江安县| 土默特左旗| 新安县| 大埔县| 从江县| 麟游县| 休宁县| 咸宁市| 新津县| 商南县| 临夏县| 屏东县| 东丽区| 定州市| 若羌县| 南充市| 贵溪市| 龙岩市| 广平县| 纳雍县| 梁山县| 祁门县| 黔江区| 红桥区| 高青县| 咸阳市| 攀枝花市| 湘潭县| 包头市| 饶阳县| 陇川县| 安义县| 融水| 文水县| 漯河市| 闵行区| 新龙县| 陆川县| 普兰店市| 神木县|