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

mybatis?selectKey賦值未生效的原因分析

 更新時間:2024年02月28日 08:22:29   作者:老馬嘯西風  
這篇文章主要介紹了mybatis?selectKey賦值未生效的原因分析,selectKey 會將 SELECT LAST_INSERT_ID()的結(jié)果放入到傳入的實體類的主鍵里面,文中通過代碼示例給大家講解非常詳細,需要的朋友可以參考下

Statement配置如下:

 <insert id="insertStatemnet" parameterType="User">
       insert into user ( user_name )  
       values  (#{user#userName})
  	 <selectKey resultType="long" order="AFTER"
             keyProperty="id">
      	SELECT LAST_INSERT_ID() 
  	</selectKey>
  </insert>

Dao 層代碼如下

public interface UserMapper{
   int insert(@Param(user) User user);
}
class User{
     long id;
     String userName;

     ...getter/setter
}


User user = userMapper.insert(user);

user.getId() 獲取的結(jié)果等于0

使用Mybatis Insert User表,使用selectKey獲取LAST_INSERT_ID() ,賦值給user的id屬性,發(fā)現(xiàn)id屬性值未被set進去,user.getId() 獲取的結(jié)果等于0,會話的LAST_INSERT_ID() 已經(jīng)到遠遠超過0了,返回0明顯是不對的。

開始探究為什么?

屬性值獲取到的是0,要么是SELECT LAST_INSERT_ID() sql未執(zhí)行,使用了id long類型的默認值0,要么是執(zhí)行了但獲取到的值是0,或者是Mybatis set對象id屬性值的時候沒set進去。

Mybatis使用SelectKeyGenerator處理selectKey標簽,從這里開始入手

private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
        String[] keyProperties = keyStatement.getKeyProperties();
        final Configuration configuration = ms.getConfiguration();
        final MetaObject metaParam = configuration.newMetaObject(parameter);
        if (keyProperties != null) {
          Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
          List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
          if (values.size() == 0) {
            throw new ExecutorException("SelectKey returned no data.");            
          } else if (values.size() > 1) {
            throw new ExecutorException("SelectKey returned more than one value.");
          } else {
            MetaObject metaResult = configuration.newMetaObject(values.get(0));
            ...
            setValue(metaParam, keyProperties[0], values.get(0));
            ....
          }
        }
      }
    } catch (ExecutorException e) {
      throw e;
    } catch (Exception e) {
      throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
    }
  }

驗證發(fā)現(xiàn)keyExecutor.query獲取到的values是[100],說明SELECT LAST_INSERT_ID() 查詢沒問題,那問題必然出現(xiàn)在下面的setValue方法里面。

繼續(xù)跟進setValue,setValue最終走的是ObjectWrapper的實現(xiàn)類MapWrapper#set,

class MapWrapper{
	  @Override
	  public void set(PropertyTokenizer prop, Object value) {
	    if (prop.getIndex() != null) {
	      Object collection = resolveCollection(prop, map);
	      setCollectionValue(prop, collection, value);
	    } else {
	      map.put(prop.getName(), value);
	    }
	  }
}

該方法set只是在map里面put了一個kv,理論上Mybatis要對字段賦值的話,應該反射調(diào)用對象Filed的set方法。

于此同時看到ObjectWrapper有一個實現(xiàn)類BeanWrapper,其中的set方法是反射set字段值,剛好和我們預期的想法一致。

class BeanWrapper{
  @Override
  public void set(PropertyTokenizer prop, Object value) {
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      setCollectionValue(prop, collection, value);
    } else {
      setBeanProperty(prop, object, value);
    }
  }
}

此估計就是決策ObjectWrapper的時候出現(xiàn)問題導致的屬性值為set進去,下面是獲取ObjectWrapper實現(xiàn)的代碼片段:

private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;
    this.reflectorFactory = reflectorFactory;

    if (object instanceof ObjectWrapper) {
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } else if (object instanceof Map) {
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } else if (object instanceof Collection) {
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }

理論上要走到else邏輯的,實際object類型是MapperMethod.ParamMap類型,走到了Map分支。

MapperMethod.ParamMap類型是在MapperMethod執(zhí)行過程中轉(zhuǎn)換java對象參數(shù)到sql命令行參數(shù)生成的,具體參考ParamNameResolver#getNamedParams

 public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

因為Mapper方法參數(shù)里面標注了@Param注解,導致生成的是MapperMethod.ParamMap。

回過頭來理下,因為Mapper方法參數(shù)里面標注了@Param注解,導致生成的sql參數(shù)類型是MapperMethod.ParamMap,繼而導致獲取MetaObject的時候ObjectWrapper被錯誤決策成MapWrapper,導致setValue屬性值未set進去。

實際上還是使用不規(guī)范導致的問題,去掉方法上的@Param注解即可正常運行

以上就是mybatis selectKey賦值未生效的原因分析的詳細內(nèi)容,更多關(guān)于mybatis selectKey賦值未生效的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中關(guān)于子類覆蓋父類的拋出異常問題

    Java中關(guān)于子類覆蓋父類的拋出異常問題

    今天小編就為大家分享一篇關(guān)于Java中關(guān)于子類覆蓋父類的拋出異常問題,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Mybatis動態(tài)SQL的示例代碼

    Mybatis動態(tài)SQL的示例代碼

    本文主要介紹了Mybatis動態(tài)SQL的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • IDEA2022搭建Spring?Cloud多模塊項目的詳細過程

    IDEA2022搭建Spring?Cloud多模塊項目的詳細過程

    這篇文章主要介紹了IDEA2022搭建Spring?Cloud多模塊項目,網(wǎng)上有很多教程父模塊都是通過maven的方式創(chuàng)建的,然后子模塊是通過Spring?Initalizr方式創(chuàng)建,這種方式父模塊無法管理子模塊的依賴仲裁,需要每個子模塊自行管理,就失去了父模塊的用處了
    2022-10-10
  • elasticsearch索引index之put?mapping的設(shè)置分析

    elasticsearch索引index之put?mapping的設(shè)置分析

    這篇文章主要為大家介紹了elasticsearch索引index之put?mapping的設(shè)置分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-04-04
  • java 文件上傳到讀取文件內(nèi)容的實例

    java 文件上傳到讀取文件內(nèi)容的實例

    今天小編就為大家分享一篇java 文件上傳到讀取文件內(nèi)容的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • idea中操作git?shelve與git?stash全過程

    idea中操作git?shelve與git?stash全過程

    文章介紹了兩種代碼暫存工具:gitshelve和gitstash,gitshelve提供了靜默暫存功能,可以從shelf中取出暫存的代碼并多次取出,不需要后可以刪除shelf,gitstash則通過vcs菜單進行暫存和取出,方便在不同分支間切換時保留修改
    2025-11-11
  • Springboot中yml文件不生效原因分析及解決方案

    Springboot中yml文件不生效原因分析及解決方案

    本文介紹了SpringBoot項目中YML文件不生效的常見原因及解決方案,包括格式錯誤、文件名錯誤、配置文件位置、激活的Profile、yml文件未加載、依賴問題、環(huán)境變量覆蓋和代碼中的硬編碼配置,通過以下步驟,可以解決大部分YML文件配置不生效的問題
    2024-11-11
  • Spring Boot Jar 包部署腳本的實例講解

    Spring Boot Jar 包部署腳本的實例講解

    在本篇文章里小編給大家整理的是一篇關(guān)于Spring Boot Jar 包部署腳本的實例講解內(nèi)容,對此有興趣的朋友們可以跟著學習下。
    2021-12-12
  • Springboot Apollo配置yml的問題及解決方案

    Springboot Apollo配置yml的問題及解決方案

    這篇文章主要介紹了Springboot Apollo配置yml的問題及解決方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • Java的JDBC和橋接模式詳解

    Java的JDBC和橋接模式詳解

    下面小編就為大家?guī)硪黄狫ava的JDBC和橋接模式(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-09-09

最新評論

利川市| 探索| 拜泉县| 金沙县| 汉阴县| 永丰县| 包头市| 景谷| 元谋县| 凤山县| 锦屏县| 嘉禾县| 上杭县| 府谷县| 云浮市| 三都| 西乌| 南丰县| 江达县| 南溪县| 赤峰市| 秭归县| 永修县| 美姑县| 临泉县| 长宁县| 舟山市| 永安市| 鄂托克旗| 高安市| 凤山市| 荔波县| 盐亭县| 和平县| 泸定县| 云和县| 平原县| 成都市| 沛县| 永兴县| 武乡县|