自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

盤點(diǎn) Mybatis 使用過程中遇到的坑!

開發(fā) 前端
springframework包下的@Param注解執(zhí)行時(shí)機(jī)在wrapCollection處理之前,wrapCollection對集合的特殊處理將springframework包下的@Param注解處理覆蓋掉了,所以無法解析參數(shù)set。而mybatis包下的@Param注解執(zhí)行時(shí)機(jī)在wrapCollection處理之后,程序可以正常運(yùn)行。

01、引言

大家好,我是了不起,前一段時(shí)間在工作中因?yàn)橐粋€(gè)疏忽踩了一個(gè)坑,最終通過異常棧追溯源碼解決了問題。

下面我來給大家還原一下案發(fā)現(xiàn)場,并介紹一下自己的解決思路,希望能對大家有所啟發(fā)。

02、案發(fā)現(xiàn)場

當(dāng)時(shí)的業(yè)務(wù)邏輯主要通過 Mybatis 框架來修改數(shù)據(jù),具體示例如下:

import org.springframework.data.repository.query.Param;

public interface GroupMapper {
 
 int updateGroup(@Param("oldSerial") Set<Integer> oldSerial, @Param("newSerial") int newSerial);

 int invalidGroup(@Param("set") Set<Integer> set);
}
<update id="updateGroup">
    update groupCode_table_use
    set groupCode=#{newSerial}
    where groupCode in
    <foreach collection="oldSerial" close=")" open="(" item="item" separator=",">
        #{item}
    </foreach>
</update>

<update id="invalidGroup">
    update groupCode_table
    set status='無效'
    where groupCode in
    <foreach collection="set" close=")" open="(" item="item" separator=",">
        #{item}
    </foreach>
</update>

本以為 so easy 的代碼,出現(xiàn)意外了!第一個(gè)sql語句updateGroup正常運(yùn)行,第二個(gè)sql語句invalidGroup竟然報(bào)錯(cuò)了???

報(bào)錯(cuò)信息如下:

圖片圖片

從日志上可以看出,提示set參數(shù)找不到!

明明使用了@Param("set")將參數(shù)命名為set為何找不到,完全不符合多年開發(fā)的認(rèn)知。

更加詭異的是updateGroup已使用了同樣的方式去遍歷,完全沒得問題,那么問題出現(xiàn)在了哪了?

小伙伴可以先猜一猜!

03、原因分析

百思不得其解下,我掏出了祖?zhèn)鹘^活 debug 源碼,最終發(fā)現(xiàn)原來是Mybatis對集合Set進(jìn)行了特殊處理。

案發(fā)項(xiàng)目引入的Mybatis版本是 3.5.1。部分源碼如下!

org.apache.ibatis.session.defaults.DefaultSqlSession.java

@Override
public int update(String statement, Object parameter) {
  try {
    dirty = true;
    MappedStatement ms = configuration.getMappedStatement(statement);
    //調(diào)用了wrapCollection方法對參數(shù)進(jìn)行了處理
    return executor.update(ms, wrapCollection(parameter));
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

/**
* 如果參數(shù)實(shí)現(xiàn)了Collection接口或是數(shù)組類型 wrapCollection方法會對參數(shù)進(jìn)行封裝
* 如果參數(shù)實(shí)現(xiàn)了Collection接口會封裝為含collection鍵的Map
*     如果參數(shù)又實(shí)現(xiàn)了List接口會封裝為含list鍵的Map(追增)
*     對Set集合沒有特殊處理
* 如果參數(shù)是數(shù)組類型會封裝為含array鍵的Map
*/
private Object wrapCollection(final Object object) {
  if (object instanceof Collection) {
    StrictMap<Object> map = new StrictMap<>();
    map.put("collection", object);
    if (object instanceof List) {
      map.put("list", object);
    }
    return map;
  } else if (object != null && object.getClass().isArray()) {
    StrictMap<Object> map = new StrictMap<>();
    map.put("array", object);
    return map;
  }
  return object;
}

通過以上的源碼分析,發(fā)現(xiàn)Mybatis框架對集合參數(shù)進(jìn)行了特殊處理。

這就是為什么報(bào)錯(cuò)信息中提示Available parameters are [collection]。

找到了collection錯(cuò)誤信息從哪里來問題,接下來我們分析一下set參數(shù)到了哪里去。

首先updateGroup可以正常執(zhí)行是因?yàn)樵创a中對集合的特殊處理只對單參數(shù)生效,也就是說@Param("set")注解失效是因?yàn)楸籑ybatis自家的特殊處理給覆蓋了?

這不合乎常理啊,那么問題可能出在@Param注解身上,通過排查代碼發(fā)現(xiàn)GroupMapper.java類引用的@Param注解不對!

// 代碼引入的注解類Param
import org.springframework.data.repository.query.Param;
// 期望的注解類,應(yīng)該由mybatis提供
import org.apache.ibatis.annotations.Param;

實(shí)際上,springframework包下的@Param注解執(zhí)行時(shí)機(jī)在wrapCollection處理之前,wrapCollection對集合的特殊處理將springframework包下的@Param注解處理覆蓋掉了,所以無法解析參數(shù)set。而mybatis包下的@Param注解執(zhí)行時(shí)機(jī)在wrapCollection處理之后,程序可以正常運(yùn)行。

最終確定,繞了這么大一圈,原來是包導(dǎo)入錯(cuò)了,誰能想到springframework包下還有一個(gè)@Param注解,在多參數(shù)的情況下竟然可以正常使用o(╥﹏╥)o。

04、異常棧分析法

整個(gè)異常排查過程中,主要通過分析異常棧信息進(jìn)行快速定位。下面我給大家介紹一下異常棧分析法。

我們在工作中查看異常棧,往往都是業(yè)務(wù)代碼異常導(dǎo)致的,這個(gè)時(shí)候我們只需要關(guān)心是在我們編寫的哪段代碼中出了問題。

而這時(shí)好用的idea也會為溫馨的為我們做出提示,會在異常棧中將我們編寫的方法位置信息標(biāo)藍(lán)處理,這樣我們就定快速定位出現(xiàn)問題的代碼。

圖片圖片

但是一旦是底層引用的jar包出現(xiàn)了異常,僅僅是這樣查看異常棧是不夠的。下面我們就以上面案例中的異常棧來帶大家分析。

當(dāng)前異常:

圖片圖片

原始異常:

圖片圖片

分析流程:

  • 異常調(diào)用棧順序:從上至下為方法調(diào)用順序的逆序,異常由異常棧的最上面的方法拋出,整個(gè)調(diào)用的入口在最下方。
  • 尋找原始異常入口技巧:檢索異常棧中與原始異常入口(最下面)拋出位置同名的方法。
  • 尋找真正異常出現(xiàn)的位置,要找最后一個(gè) Caused by 的第一行棧幀。


"Caused by" 是 Java 異常處理機(jī)制中的一部分,它表示當(dāng)前異常是由另一個(gè)異常引起的。在 Java 中,每個(gè) Throwable 對象都可以通過 getCause() 方法獲取到原始異常,這個(gè)原始異常就是通過 "Caused by" 打印出來的。

異常棧示例代碼:

假設(shè)你在catch塊中捕獲了一個(gè)異常,并重新拋出了一個(gè)新的異常,同時(shí)保留了原始異常的信息:

public class Example {
    public static void main(String[] args) {
        try {
            methodThatThrowsException();
        } catch (Exception e) {
            throw new RuntimeException("Caught an exception", e);
        }
    }

    public static void methodThatThrowsException() throws Exception {
        throw new Exception("Original exception");
    }
}

當(dāng)你運(yùn)行上述代碼時(shí),控制臺輸出的異常棧信息通常會包含Caused by信息:

java.lang.RuntimeException: Caught an exception
    at Example.main(Example.java:7)
Caused by: java.lang.Exception: Original exception
    at Example.methodThatThrowsException(Example.java:12)
    at Example.main(Example.java:5)
  • java.lang.Exception: Original exception 是原始異常。
  • java.lang.RuntimeException: Caught an exception 是新拋出的異常,并且包含了原始異常作為其原因。
  • Caused by 信息顯示了原始異常的詳細(xì)信息,這有助于調(diào)試和理解異常的來源。

如何保留原始異常信息呢?

  • 方式一:直接傳遞原始異常:
try {
     methodThatThrowsException();
 } catch (Exception e) {
     throw new RuntimeException("Caught an exception", e);
 }
  • 方式二:使用 addSuppressed 方法:
try {
     methodThatThrowsException();
 } catch (Exception e) {
     RuntimeException newException = new RuntimeException("Caught an exception");
     newException.addSuppressed(e);
     throw newException;
 }

05、結(jié)語

學(xué)會查看分析異常棧,可以為我們工作大大提高效率,希望這篇文章給大家?guī)硎斋@,最后再送給大家一個(gè)小技巧。異常棧不僅僅可以用來排查異常哦,還可以幫助大家學(xué)習(xí)源碼,debug 源碼找不到入口怎么辦,那就創(chuàng)造一個(gè)異常!

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2016-03-23 11:03:40

2020-07-29 08:03:26

Celery異步項(xiàng)目

2010-06-07 16:51:06

rsync 使用

2011-04-13 13:54:03

HttpClient

2011-03-04 13:49:38

FileZilla

2009-12-29 14:14:22

2022-02-07 00:10:28

Docker容器開發(fā)

2023-02-28 16:26:46

推薦系統(tǒng)模塊

2009-11-02 08:56:17

2024-09-09 09:08:28

2016-12-30 11:10:32

Hadoop開發(fā)JVM

2021-11-15 15:43:28

Windows 11升級微軟

2019-10-28 14:07:29

研發(fā)管理技術(shù)

2012-07-26 10:10:27

虛擬化安全網(wǎng)絡(luò)安全

2012-07-27 10:01:13

虛擬化

2010-03-15 09:11:25

Python編程版面

2010-05-11 18:05:50

MySQL 5安裝

2009-12-10 14:19:41

配置靜態(tài)路由

2022-11-18 09:27:22

數(shù)據(jù)庫

2020-03-16 13:16:48

Kubernetes選型踩坑
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號