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

Mybatis-Plus自動屬性填充與自定義Insert into語句順序詳解

 更新時間:2026年05月16日 09:17:52   作者:拽著尾巴的魚兒  
本文通過調(diào)試發(fā)現(xiàn),Mybatis-Plus在拼接SQL語句時未使用自定義屬性填充的值,原因是語句拼接在屬性填充之前,因此,需要在自定義SQL語句中去除`<if>`的非空判斷,直接使用`#{testFiled}`,這樣最終在進(jìn)行數(shù)據(jù)插入時,Mybatis會動態(tài)的替換掉該占位符

前言

系統(tǒng)中使用了Mybatis-Plus 自動屬性填充為實體統(tǒng)一進(jìn)行屬性的填值,在Mapper的xml 文件中 insert into 語句 使用<if test="id != null">id,</if> 進(jìn)行判斷會發(fā)現(xiàn)該屬性是空的,明明已經(jīng)為改字段進(jìn)行了屬性的自動填充,為什么Mybatis- 在拼接sql 語句時依然認(rèn)為 改屬性是空的呢;

1 問題重現(xiàn)

1.1 在實體中使用了屬性填充屬性:

 @TableField(fill = FieldFill.INSERT)
 private String testFiled;

1.2 在攔截器里進(jìn)行了屬性填充:

@Override
public void insertFill(MetaObject metaObject) {
	this.setFieldValByName("testFiled", "test", metaObject);
}

1.3 mapper xml :

 insert into ${prefix}knowledge_authority
 <trim prefix="(" suffix=")" suffixOverrides=",">
   <if test="id != null">id,</if>
   <if test="testFiled != null">test_filed,</if>
  </trim>
 <trim prefix="values (" suffix=")" suffixOverrides=",">
   <if test="id != null">#{id},</if>
    <if test="testFiled != null">#{testFiled },</if>
  </trim>

在對實體id 設(shè)置完成之后,進(jìn)行數(shù)據(jù)的插入,發(fā)現(xiàn)插入的數(shù)據(jù)中只有id 沒有testFiled 屬性;

2 推斷問題產(chǎn)生的原因

  • 原因1:屬性填充正常,但是在xml sql 語句中,在某些情況下 <if> 判斷有問題;
  • 原因2:自定義填充屬性有問題,導(dǎo)致想要填充的屬性沒有被填充值,導(dǎo)致進(jìn)行 <if> 判斷有問題;
  • 愿意3:屬性填充和 <if> 標(biāo)簽判斷都沒有問題,但是sql 拼接的時機(jī) 在屬性填充之前進(jìn)行;

<if> 標(biāo)簽只進(jìn)行簡單的空判斷,出問題的可能性不大,從原因2 入手:

使用自定義屬性填充時,會調(diào)用MybatisParameterHandler 類中的process()方法完成屬性填充的調(diào)用;

 private void process(Object parameter) {
  if (parameter != null) {
       TableInfo tableInfo = null;
       Object entity = parameter;
       if (parameter instanceof Map) {
           Map<?, ?> map = (Map)parameter;
           if (map.containsKey("et")) {
               Object et = map.get("et");
               if (et != null) {
                   entity = et;
                   tableInfo = TableInfoHelper.getTableInfo(et.getClass());
               }
           }
       } else {
           tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());
       }

       if (tableInfo != null) {
           MetaObject metaObject = this.configuration.newMetaObject(entity);
           if (SqlCommandType.INSERT == this.sqlCommandType) {
           		// 插入時 id 的填充
               this.populateKeys(tableInfo, metaObject, entity);
               // 這里會在insert 時 調(diào)用我們自己定義的攔截器進(jìn)行屬性的自動填充
               this.insertFill(metaObject, tableInfo);
           } else {
            // 這里會在update 時 調(diào)用我們自己定義的攔截器進(jìn)行屬性的自動填充
               this.updateFill(metaObject, tableInfo);
           }
       }
   }

}

通過debug 我們發(fā)現(xiàn),在插入數(shù)據(jù)時確實調(diào)用了process 方法,并對實體完成了屬性的填充,屬性填充是正常的;所以會不會是原因3 ,屬性填充的時機(jī)和sql 拼接的時機(jī)不同造成的。

如果 先進(jìn)行了sql的拼接,此時進(jìn)行 <if> 判斷時 發(fā)現(xiàn)改屬性為空,必然會跳過了該屬性的拼接,即使后面自動填充為屬性填充了數(shù)據(jù),但是由于sql已經(jīng)完成了拼接,最終執(zhí)行的sql 也是沒有該屬性的;

基于此猜想,我們將xml 中 判斷標(biāo)簽去掉,只保留占位符:

 insert into ${prefix}knowledge_authority
 <trim prefix="(" suffix=")" suffixOverrides=",">
   <if test="id != null">id,</if>
  	test_filed,
  </trim>
 <trim prefix="values (" suffix=")" suffixOverrides=",">
   <if test="id != null">#{id},</if>
     #{testFiled },
  </trim>

此時在次進(jìn)行插入,發(fā)現(xiàn)插入成功,并且testFiled 屬性也是有值的;

3 從Mybatis-Plus 代碼層面查看sql 語句的拼接

3.1 先看下sql 拼接的流程:

MybatisParameterHandler 是 Mybatis 中用于處理數(shù)據(jù)庫操作參數(shù)的接口,它的實現(xiàn)類 DefaultParameterHandler 負(fù)責(zé)將 Java 對象轉(zhuǎn)換為 JDBC 預(yù)處理語句需要的參數(shù)值,以及將參數(shù)值設(shè)置到預(yù)處理語句中。其中,BoundSql 對象就是用于封裝 SQL 語句和對應(yīng)的參數(shù)值的。

BoundSql 對象的賦值過程主要由 SqlSource 和 ParameterMapping 來完成,具體流程如下:

  • Mybatis 在執(zhí)行 SQL 語句之前,會根據(jù) Mapper 接口定義的方法和傳入的參數(shù)生成 MappedStatement 對象。在 MappedStatement 中包含了 SQL 語句、參數(shù)映射信息等相關(guān)的元數(shù)據(jù)。
  • MappedStatement 負(fù)責(zé)生成 BoundSql 對象。在生成 BoundSql 對象時,Mybatis 會首先根據(jù) SQL 語句和參數(shù)信息生成一個 StaticSqlSource 對象,然后再通過它生成一個 DynamicSqlSource 對象。DynamicSqlSource 會根據(jù)傳入的參數(shù)信息和 Mapper 接口定義的 SQL 語句,動態(tài)生成最終的 SQL 語句和參數(shù)值。這個過程中,ParameterMapping 負(fù)責(zé)將 Java 對象中的屬性值和 SQL 語句中的占位符做映射關(guān)聯(lián),SqlSource 負(fù)責(zé)根據(jù)參數(shù)信息和 SQL 語句生成 BoundSql 對象。
  • 生成 BoundSql 對象后,Mybatis 會通過 ParameterHandler 將 BoundSql 對象中的 SQL 語句和參數(shù)值設(shè)置到 JDBC 預(yù)處理語句中。默認(rèn)的 ParameterHandler 實現(xiàn)類是 DefaultParameterHandler,它會通過反射獲取 PreparedStatement 對象,并調(diào)用 setXxx() 方法將參數(shù)值設(shè)置到預(yù)處理語句中。在設(shè)置參數(shù)值的過程中,DefaultParameterHandler 會根據(jù) ParameterMapping 中的信息獲得 Java 對象中對應(yīng)屬性的值,并將其賦值給 BoundSql 對象中對應(yīng)的參數(shù)占位符。
  • 綜上所述,BoundSql 對象的賦值過程主要由 SqlSource 和 ParameterMapping 來完成。它們會根據(jù)傳入的參數(shù)信息和 Mapper 接口定義的 SQL 語句,動態(tài)生成最終的 SQL 語句和參數(shù)值,并將它們設(shè)置到 BoundSql 對象中。

3.2 MybatisParameterHandler 中的 BoundSql boundSql:

public class MybatisParameterHandler implements ParameterHandler {
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private final BoundSql boundSql;
    private final Configuration configuration;
    private final SqlCommandType sqlCommandType;

    public MybatisParameterHandler(MappedStatement mappedStatement, Object parameter, BoundSql boundSql) {
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.mappedStatement = mappedStatement;
        // 拼接好的sql
        this.boundSql = boundSql;
        this.configuration = mappedStatement.getConfiguration();
        this.sqlCommandType = mappedStatement.getSqlCommandType();
        // 主鍵id 和 屬性的自動填充
        this.parameterObject = this.processParameter(parameter);
    }
}

可以看到在創(chuàng)建MybatisParameterHandler 對象時,boundSql 已經(jīng)完成了sql 的解析和拼接,然后在this.processParameter(parameter) 方法完成了主鍵id 和 屬性的自動填充,從構(gòu)造方法可以看到,boundSql 的拼接是先于processParameter(parameter) 屬性填充的方法的,這就解釋了為什么我們明明已經(jīng)為改屬性進(jìn)行了填充,為什么 最終自定義的insert into 語句 標(biāo)簽判斷是空的,本質(zhì)就是因為兩者的順序問題;

3.3 sql 語句的拼接:

進(jìn)入DynamicSqlSource 類getBoundSql 方法:

public class DynamicSqlSource implements SqlSource {
    private final Configuration configuration;
    private final SqlNode rootSqlNode;

    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
    }

    public BoundSql getBoundSql(Object parameterObject) {
    	// 參數(shù)解析
        DynamicContext context = new DynamicContext(this.configuration, parameterObject);
       	// sql 拼接
        this.rootSqlNode.apply(context);
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        // 占位符拼接
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        // sql 拼接
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        context.getBindings().forEach(boundSql::setAdditionalParameter);
        return boundSql;
    }
}

this.rootSqlNode.apply(context):

public class MixedSqlNode implements SqlNode {
    private final List<SqlNode> contents;

    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }

    public boolean apply(DynamicContext context) {
    	// 這里會判斷xml 中的所有屬性標(biāo)簽,只有判斷為true ,才進(jìn)行屬性的拼接
        this.contents.forEach((node) -> {
            node.apply(context);
        });
        return true;
    }
}

可以看到這里會根據(jù)標(biāo)簽不同調(diào)用不同的實現(xiàn)完成判斷:

并且會逐個進(jìn)行屬性的判斷,只有為true 才進(jìn)行屬性拼接:

以IfSqlNode 為例,可以看出只有當(dāng)屬性不為空時,才返回true 否則返回false,只有在返回true 時后續(xù)才會對改屬性進(jìn)行拼接

4 總結(jié)

Mybatis-Plus 自定義的sql 語句其BoundSql的解析和拼接是在屬性填充之前進(jìn)行的,所以如果在自定義sql 語句中使用了<if>標(biāo)簽進(jìn)行屬性的非空判斷,就不會拼接改屬性,此時需要在自定義的sql 中去除<<if>的非空判斷直接使用#{testFiled },這樣最終在進(jìn)數(shù)據(jù)插入時,Mybatis會動態(tài)的替換掉改占位符。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Intellij IDEA 與maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.PathTranslator was bound

    Intellij IDEA 與maven 版本不符 Unable to import maven project See

    這篇文章主要介紹了Intellij IDEA 與maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.PathTranslator was bound,本文通過圖文給大家分享解決方案,需要的朋友可以參考下
    2020-08-08
  • SWT(JFace)體驗之FormLayout布局

    SWT(JFace)體驗之FormLayout布局

    SWT(JFace)體驗之FormLayout布局示例代碼。
    2009-06-06
  • 如何使用新方式編寫Spring MVC接口

    如何使用新方式編寫Spring MVC接口

    這篇文章主要介紹了如何使用新方式編寫Spring MVC接口,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • springboot整合sentinel接口熔斷的實現(xiàn)示例

    springboot整合sentinel接口熔斷的實現(xiàn)示例

    為了防止慢接口導(dǎo)致的服務(wù)阻塞,可以通過添加熔斷處理來避免應(yīng)用的大量工作線程陷入阻塞,保證其他接口的正常運(yùn)行,本文介紹了如何使用Spring Boot與Sentinel進(jìn)行接口熔斷的配置與實現(xiàn),感興趣的可以了解一下
    2024-09-09
  • Java?shiro安全框架使用介紹

    Java?shiro安全框架使用介紹

    安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會出現(xiàn)問題,這篇文章主要介紹了SpringBoot安全管理Shiro框架的使用
    2022-08-08
  • SpringBoot項目實現(xiàn)短信發(fā)送接口開發(fā)的實踐

    SpringBoot項目實現(xiàn)短信發(fā)送接口開發(fā)的實踐

    本文主要介紹了SpringBoot項目實現(xiàn)短信發(fā)送接口開發(fā)的實踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Spring boot+mybatis+thymeleaf 實現(xiàn)登錄注冊增刪改查功能的示例代碼

    Spring boot+mybatis+thymeleaf 實現(xiàn)登錄注冊增刪改查功能的示例代碼

    這篇文章主要介紹了Spring boot+mybatis+thymeleaf 實現(xiàn)登錄注冊增刪改查功能的示例代碼,本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • java 如何復(fù)制非空對象屬性值

    java 如何復(fù)制非空對象屬性值

    這篇文章主要介紹了java 如何復(fù)制非空對象屬性值的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 聊聊finally中的代碼一定會執(zhí)行嗎

    聊聊finally中的代碼一定會執(zhí)行嗎

    通常在面試中,只要是疑問句一般答案都是“否定”的,因為如果是“確定”和“正?!钡?那面試官就沒有必要再問了嘛,本文想和大家聊聊finally中的代碼一定會執(zhí)行嗎?,感興趣的朋友跟著小編一起來看看吧
    2023-12-12
  • 解決2022.3.1版本中?IDEA中?XML文件提示屎黃色背景的方法

    解決2022.3.1版本中?IDEA中?XML文件提示屎黃色背景的方法

    這篇文章主要介紹了解決2022.3.1版本中?IDEA中?XML文件屎黃色背景?的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01

最新評論

苍溪县| 云梦县| 区。| 石首市| 阜平县| 四川省| 甘洛县| 白河县| 彭阳县| 泸水县| 磴口县| 弋阳县| 六安市| 柳江县| 皮山县| 山阴县| 湖口县| 周口市| 高淳县| 兰坪| 洪泽县| 乐亭县| 伊春市| 蕲春县| 如东县| 堆龙德庆县| 渝中区| 富蕴县| 定州市| 卓资县| 得荣县| 蚌埠市| 龙岩市| 长乐市| 福安市| 宁强县| 荔浦县| 北京市| 成武县| 祁门县| 根河市|