欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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)到遠(yuǎn)遠(yuǎn)超過0了,返回0明顯是不對的。

開始探究為什么?

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

Mybatis使用SelectKeyGenerator處理selectKey標(biāo)簽,從這里開始入手

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要對字段賦值的話,應(yīng)該反射調(diào)用對象Filed的set方法。

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

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)問題導(dǎo)致的屬性值為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ù)里面標(biāo)注了@Param注解,導(dǎo)致生成的是MapperMethod.ParamMap。

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

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

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

相關(guān)文章

  • java實現(xiàn)簡易的學(xué)籍管理系統(tǒng)

    java實現(xiàn)簡易的學(xué)籍管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)簡易的學(xué)籍管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 如何解決java.lang.NoClassDefFoundError:Could not initialize class java.awt.Color問題

    如何解決java.lang.NoClassDefFoundError:Could not initi

    文章講述了在Java服務(wù)器中處理圖形元素時遇到的常見問題,即需要運行X-server,通過在Tomcat/bin/catalina.sh中增加JAVA_OPTS環(huán)境變量并設(shè)置-Djava.awt.headless=true,可以解決這個問題,使服務(wù)器能夠在沒有圖形界面的情況下運行
    2024-11-11
  • Java中的排序與內(nèi)部比較器Compareable解析

    Java中的排序與內(nèi)部比較器Compareable解析

    這篇文章主要介紹了Java中的排序與內(nèi)部比較器Compareable解析,一般沒有特殊要求時,直接調(diào)用(底層默認(rèn)的升序排列)就可以得到想要的結(jié)果,所謂的 sort 方法排序底層都是基于這兩種排序,故如果需要設(shè)計成所想要的排序就需要了解底層排序原理,需要的朋友可以參考下
    2023-11-11
  • springboot2.x實現(xiàn)oauth2授權(quán)碼登陸的方法

    springboot2.x實現(xiàn)oauth2授權(quán)碼登陸的方法

    這篇文章主要介紹了springboot2.x實現(xiàn)oauth2授權(quán)碼登陸的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-08-08
  • Java中的悲觀鎖與樂觀鎖是什么

    Java中的悲觀鎖與樂觀鎖是什么

    這篇文章主要介紹了Java中的悲觀鎖與樂觀鎖是什么,幫助大家更好的理解和學(xué)習(xí)Java鎖的相關(guān)知識,感興趣的朋友可以了解下
    2020-09-09
  • JavaEE線程安全實現(xiàn)線程池方法

    JavaEE線程安全實現(xiàn)線程池方法

    這篇文章主要介紹了JavaEE線程安全實現(xiàn)線程池方法,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-06-06
  • Java中Spring的Security使用詳解

    Java中Spring的Security使用詳解

    這篇文章主要介紹了Java中Spring的Security使用詳解,在web應(yīng)用開發(fā)中,安全無疑是十分重要的,選擇Spring Security來保護web應(yīng)用是一個非常好的選擇,需要的朋友可以參考下
    2023-07-07
  • Java?Autowired注解深入分析

    Java?Autowired注解深入分析

    @Autowired注解是Spring中非常重要且常見的,接下來就簡要的介紹一下它的用法。@Autowired默認(rèn)是通過set方法,按照類型自動裝配JavaBean,set方法可省略不寫,它主要是修飾在成員變量上
    2023-01-01
  • java?socket實現(xiàn)局域網(wǎng)聊天

    java?socket實現(xiàn)局域網(wǎng)聊天

    這篇文章主要為大家詳細(xì)介紹了java?socket實現(xiàn)局域網(wǎng)聊天,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • IDEA中配置操作Git的詳細(xì)圖文教程

    IDEA中配置操作Git的詳細(xì)圖文教程

    這篇文章給大家詳細(xì)介紹在IDEA中配置Git,IDEA中操作Git的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2020-10-10

最新評論