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

MyBatis/MyBatis-Plus報錯:java.lang.NullPointerException: target is null for method size的解決方法

 更新時間:2025年11月28日 09:45:43   作者:李少兄  
在使用 MyBatis 或 MyBatis-Plus 進行數(shù)據(jù)庫操作時,開發(fā)者經(jīng)常需要根據(jù)傳入的參數(shù)動態(tài)構(gòu)建 SQL 查詢語句,然而,如果對集合參數(shù)未做充分的空值校驗,就直接調(diào)用其方法,程序在運行時會拋出經(jīng)典異常,所以本文給大家介紹了具體的解決方法,需要的朋友可以參考下

一、問題背景

在使用 MyBatisMyBatis-Plus 進行數(shù)據(jù)庫操作時,開發(fā)者經(jīng)常需要根據(jù)傳入的參數(shù)動態(tài)構(gòu)建 SQL 查詢語句。例如,根據(jù)一組用戶 ID(ids)進行批量查詢。此時,通常會在 XML 映射文件中使用 <if> 標簽配合 OGNL 表達式進行條件判斷。

然而,如果對集合參數(shù)(如 List<Long> ids)未做充分的空值校驗,就直接調(diào)用其方法(如 .size()),程序在運行時會拋出如下經(jīng)典異常:

java.lang.NullPointerException: target is null for method size
    at org.apache.ibatis.ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1620)
    at org.apache.ibatis.ognl.ASTMethod.getValueBody(ASTMethod.java:72)
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:171)
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:206)
    at org.apache.ibatis.ognl.ASTChain.getValueBody(ASTChain.java:128)
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:171)
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:206)
    at org.apache.ibatis.ognl.ASTGreater.getValueBody(ASTGreater.java:34)
    at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:171)
    at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:206)
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:408)
    at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:383)
    at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:47)
    at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:32)
    at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:34)
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:39)
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:320)
    at com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor.intercept(MybatisPlusInterceptor.java:69)
    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:59)
    at jdk.proxy2/jdk.proxy2.$Proxy183.query(Unknown Source)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154)
    ... 95 common frames omitted

? 關(guān)鍵報錯信息

java.lang.NullPointerException: target is null for method size
at org.apache.ibatis.ognl.OgnlRuntime.callMethod

只要你在項目日志或控制臺中看到上述堆棧信息,基本可以斷定:你在 MyBatis 的 OGNL 表達式中直接調(diào)用了可能為 null 的集合對象(如 ids)的 .size() 方法。

二、問題原因分析

2.1 OGNL 表達式執(zhí)行機制簡述

MyBatis 使用 OGNL(Object-Graph Navigation Language) 來解析 XML 中 <if test="..."> 等標簽內(nèi)的表達式。當你在 XML 中寫:

<if test="ids.size() > 0">

MyBatis 會在運行時嘗試執(zhí)行 ids.size()。但如果傳入的 ids 參數(shù)為 null,JVM 就無法在 null 引用上調(diào)用任何實例方法,從而拋出 NullPointerException。

2.2 典型錯誤場景示例

假設(shè)你有一個根據(jù)用戶 ID 列表查詢用戶的接口:

// Mapper 接口
List<User> selectByIds(@Param("ids") List<Long> ids);

對應(yīng)的 XML 映射文件如下:

<!-- 錯誤寫法 ? -->
<select id="selectByIds" resultType="User">
    SELECT * FROM user
    <where>
        <if test="ids.size() > 0">
            id IN
            <foreach collection="ids" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        </if>
    </where>
</select>

當調(diào)用方傳入 null 時:

userMapper.selectByIds(null); // ids = null

MyBatis 在解析 <if test="ids.size() > 0"> 時,會嘗試調(diào)用 null.size(),立即觸發(fā) NPE。

注意:即使你認為“調(diào)用方不會傳 null”,但在實際系統(tǒng)中,前端參數(shù)缺失、DTO 轉(zhuǎn)換失敗、單元測試遺漏等情況都可能導致 ids 為 null。因此,防御性編程是必須的

三、正確解決方案

方案一:在 OGNL 表達式中顯式判空(最常用且推薦)

修改 XML 中的 <if> 條件,先判斷 ids 不為 null,再判斷其大小

<!-- 正確寫法 ? -->
<if test="ids != null and ids.size() > 0">
    id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</if>

或者使用更語義化的 isEmpty() 方法(MyBatis 支持):

<if test="ids != null and !ids.isEmpty()">
    ...
</if>

為什么安全?
OGNL 表達式支持邏輯短路(short-circuit evaluation):

若 ids != null 為 false,則整個表達式直接為 false,不會繼續(xù)執(zhí)行 ids.size(),從而避免 NPE。

方案二:Java 層保證集合非 null(防御性初始化)

在業(yè)務(wù)層或 Controller 層,確保傳給 Mapper 的集合不為 null

public List<User> getUsers(List<Long> ids) {
    if (ids == null) {
        ids = Collections.emptyList(); // 或 new ArrayList<>()
    }
    return userMapper.selectByIds(ids);
}

這樣即使 XML 中寫了 ids.size() > 0,也不會崩潰(因為 Collections.emptyList().size() 返回 0)。
仍建議配合方案一使用,以增強代碼魯棒性和可讀性。

方案三:使用 MyBatis-Plus 的條件構(gòu)造器(徹底規(guī)避 XML 風險)

如果你使用的是 MyBatis-Plus,強烈建議優(yōu)先使用其內(nèi)置的 條件構(gòu)造器(Wrapper),它天然支持條件判斷,且類型安全、可讀性強。

MyBatis-Plus 的 QueryWrapper / LambdaQueryWrapper 方法通常提供一個 boolean 類型的第一個參數(shù),用于控制該條件是否生效。例如:

public List<User> selectUsersByIds(List<Long> ids) {
    return lambdaQuery()
        .in(ids != null && !ids.isEmpty(), User::getId, ids)
        .list();
}

關(guān)鍵說明(來自 MyBatis-Plus 官方行為):

  • in(boolean condition, SFunction<T, ?> column, Collection<?> values)
  • conditiontrue 時,才將 IN 條件加入 SQL;否則忽略。
  • 如果省略 condition 參數(shù)(即只傳列和值),則默認 condition = true,條件總是生效

因此,正確的做法是在第一個參數(shù)中完成完整的判空邏輯

// ? 安全寫法
lambdaQuery()
    .eq(name != null && !name.trim().isEmpty(), User::getName, name)
    .ge(age != null && age >= 0, User::getAge, age)
    .in(ids != null && !ids.isEmpty(), User::getId, ids)
    .list();

這種方式:

  • 無需編寫 XML;
  • 避免 OGNL 表達式陷阱;
  • 編譯期即可發(fā)現(xiàn)類型錯誤;
  • 邏輯清晰,易于維護。

四、最佳實踐總結(jié)

場景推薦做法
手寫 MyBatis XML始終使用 ids != null and ids.size() > 0ids != null and !ids.isEmpty()
集合參數(shù)來源不可控(如 HTTP 請求)Java 層主動初始化:ids = Optional.ofNullable(ids).orElse(Collections.emptyList())
使用 MyBatis-Plus優(yōu)先使用 LambdaQueryWrapper,并在條件方法的第一個參數(shù)中完成判空邏輯
團隊編碼規(guī)范禁止在 test 表達式中直接調(diào)用 .size()、.isEmpty() 等方法而不判空

五、擴展知識:MyBatis 對集合的“真值”處理

MyBatis 內(nèi)部對集合類型有特殊處理規(guī)則:

一個非 null 且非空的集合在布爾上下文中被視為 true;否則為 false。

因此,以下寫法在某些情況下也能工作:

<if test="ids">
    <!-- 相當于 ids != null && ids.size() > 0 -->
</if>

但請注意:

  • 該行為依賴于 MyBatis 內(nèi)部的 OgnlOps.booleanValue() 實現(xiàn);
  • 在復雜嵌套表達式或自定義類型中可能表現(xiàn)不一致;
  • 可讀性較差,新成員可能不理解其含義。

? 結(jié)論:雖然 test="ids" 是合法的,但為了代碼清晰、可維護、可移植,強烈建議顯式寫出 ids != null and !ids.isEmpty()

六、如何快速定位與修復

  1. 搜索關(guān)鍵字:在項目中全局搜索 .size()、.isEmpty() 出現(xiàn)在 XML 的 test= 屬性中;
  2. 靜態(tài)檢查:可通過 SonarQube 或自定義 Checkstyle 規(guī)則禁止此類寫法;
  3. 單元測試覆蓋:編寫測試用例,傳入 null、空集合、正常集合三種情況,驗證 SQL 生成正確性;
  4. 日志監(jiān)控:在生產(chǎn)環(huán)境中監(jiān)控 NullPointerException,若堆棧包含 OgnlRuntime.callMethod,優(yōu)先排查動態(tài) SQL。

七、附錄:OGNL —— MyBatis 動態(tài) SQL 的底層引擎

很多開發(fā)者在使用 MyBatis 時知道 <if test="xxx"> 可以寫表達式,但并不清楚其背后的原理。

7.1 什么是 OGNL?

OGNL(Object-Graph Navigation Language)是一種開源的表達式語言,最初由 Drew Davidson 創(chuàng)建,現(xiàn)由 Apache Commons 維護(Apache Commons OGNL)。它允許你通過簡潔的字符串表達式訪問和操作 Java 對象圖中的屬性、方法、集合等。

MyBatis 從早期版本起就采用 OGNL 作為其動態(tài) SQL 表達式的解析引擎(替代了早期的 JSTL 和自定義語法)。

7.2 OGNL 在 MyBatis 中的應(yīng)用場景

在 MyBatis 的 XML 映射文件中,以下標簽的 testcollection 等屬性均使用 OGNL 表達式:

  • <if test="...">
  • <when test="...">
  • <choose>/<when>/<otherwise>
  • <foreach collection="...">
  • <bind name="..." value="..."/>

例如:

<if test="user.age > 18 and user.active == true">
<foreach collection="user.roles" item="role">
<bind name="likePattern" value="'%' + keyword + '%'" />

這些表達式都會被 MyBatis 交給 OGNL 引擎在運行時求值。

7.3 OGNL 表達式語法要點

表達式說明
param訪問參數(shù)對象的屬性(如 @Param("user") User useruser.name
param != null判空
list.size()調(diào)用集合的 size() 方法(?? 若 list 為 null 會拋 NPE)
list.isEmpty()調(diào)用 isEmpty()(同樣需先判空)
map['key']map.key訪問 Map
@java.lang.Math@max(a, b)調(diào)用靜態(tài)方法(需開啟 static method access)
true and false邏輯運算(支持 and/or/not,也支持 &&/`
a > b ? 'yes' : 'no'三元運算符

重要限制:出于安全考慮,MyBatis 默認禁用 OGNL 的靜態(tài)方法調(diào)用和構(gòu)造函數(shù)調(diào)用,防止表達式注入攻擊。

7.4 OGNL 的求值上下文(Context)

MyBatis 在執(zhí)行 OGNL 表達式時,會構(gòu)建一個特殊的上下文對象(ContextMap),其中包含:

  • 所有通過 @Param 注解命名的參數(shù);
  • 一些內(nèi)置變量,如 _parameter(整個參數(shù)對象)、_databaseId 等。

例如:

void update(@Param("id") Long id, @Param("name") String name);

在 XML 中可直接使用 idname,因為它們已被注入到 OGNL 上下文中。

7.5 為什么ids.size()會報 NPE?

OGNL 在執(zhí)行 ids.size() 時,會:

  1. 從上下文中獲取 ids 變量;
  2. ids == null,則 target = null;
  3. 嘗試調(diào)用 target.size() → 即 null.size() → JVM 拋出 NullPointerException
  4. MyBatis 捕獲該異常并包裝為帶有明確提示的錯誤信息:target is null for method size。

這本質(zhì)上是一個 Java 層面的方法調(diào)用失敗,而非 OGNL 本身的 bug。

7.6 如何安全使用 OGNL?

  • 永遠先判空再調(diào)方法obj != null and obj.method()
  • 避免復雜邏輯:不要在 OGNL 中寫業(yè)務(wù)邏輯,應(yīng)移至 Java 層;
  • 慎用靜態(tài)方法:除非明確配置允許;
  • 理解短路求值a != null and a.b() 是安全的,因為 a == null 時不會執(zhí)行 a.b()。

7.7 OGNL 與 SpEL 的區(qū)別

有些開發(fā)者會混淆 OGNL 和 Spring 的 SpEL(Spring Expression Language)。兩者都是表達式語言,但:

特性OGNLSpEL
所屬生態(tài)Apache / MyBatisSpring Framework
主要用途MyBatis 動態(tài) SQLSpring 配置、注解、Thymeleaf 等
靜態(tài)方法調(diào)用默認禁用(MyBatis)支持(T(java.lang.Math).random()
安全性較高(受限)需注意表達式注入

記住:MyBatis 不使用 SpEL,只使用 OGNL。

以上就是MyBatis/MyBatis-Plus報錯:java.lang.NullPointerException: target is null for method size的解決方法的詳細內(nèi)容,更多關(guān)于MyBatis/MyBatis Plus報錯target is null的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot+MyBatis簡單數(shù)據(jù)訪問應(yīng)用的實例代碼

    SpringBoot+MyBatis簡單數(shù)據(jù)訪問應(yīng)用的實例代碼

    這篇文章主要介紹了SpringBoot+MyBatis簡單數(shù)據(jù)訪問應(yīng)用的實例代碼,需要的朋友可以參考下
    2017-05-05
  • 帶你入門Java的類與對象

    帶你入門Java的類與對象

    下面小編就為大家?guī)硪黄钊肜斫釰ava 對象和類。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望能給你帶來幫助
    2021-07-07
  • java數(shù)組排列組合問題匯總

    java數(shù)組排列組合問題匯總

    這篇文章主要為大家詳細匯總了java數(shù)組排列組合問題,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Ubuntu快速安裝eclipse

    Ubuntu快速安裝eclipse

    這篇文章主要為大家詳細介紹了Ubuntu快速安裝eclipse的簡單教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • 深入了解Java線程池的原理使用及性能優(yōu)化

    深入了解Java線程池的原理使用及性能優(yōu)化

    JAVA線程池是一種管理和復用線程資源的機制,可以提高程序的效率和響應(yīng)速度。本文將介紹線程池的原理、使用方法和性能優(yōu)化技巧,幫助讀者深入了解和應(yīng)用JAVA線程池
    2023-04-04
  • IDEA全局查找關(guān)鍵字的用法解讀

    IDEA全局查找關(guān)鍵字的用法解讀

    這篇文章主要介紹了IDEA全局查找關(guān)鍵字的用法解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 導入maven項目各個注解均報錯的解決方案

    導入maven項目各個注解均報錯的解決方案

    這篇文章主要介紹了導入maven項目各個注解均報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringBoot+MQTT+apollo實現(xiàn)訂閱發(fā)布功能的示例

    SpringBoot+MQTT+apollo實現(xiàn)訂閱發(fā)布功能的示例

    這篇文章主要介紹了SpringBoot+MQTT+apollo實現(xiàn)訂閱發(fā)布功能的示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • gRPC與SpringBoot整合思路和步驟

    gRPC與SpringBoot整合思路和步驟

    在現(xiàn)代微服務(wù)架構(gòu)中,gRPC已經(jīng)成為了非常受歡迎的通信協(xié)議,與SpringBoot整合,它為開發(fā)者提供了簡潔、高效構(gòu)建分布式應(yīng)用,在整合gRPC與SpringBoot時,將gRPC的服務(wù)端和客戶端分別封裝到SpringBoot的應(yīng)用中,感興趣的朋友一起看看吧
    2023-08-08
  • java中的tostring方法的具體用法

    java中的tostring方法的具體用法

    這篇文章主要介紹了java中的tostring方法的具體用法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,下面我們來一起學習一下吧
    2019-06-06

最新評論

营口市| 辉县市| 镇平县| 石景山区| 进贤县| 恭城| 杭州市| 大竹县| 小金县| 星子县| 苏尼特右旗| 和龙市| 永靖县| 安西县| 嵩明县| 万安县| 蓬溪县| 和平区| 寿光市| 谢通门县| 元阳县| 临沂市| 松桃| 报价| 东港市| 固始县| 琼海市| 峨山| 武宁县| 宣城市| 敦煌市| 布尔津县| 泰州市| 罗定市| 惠水县| 瓦房店市| 上虞市| 承德市| 广宁县| 富民县| 金门县|