亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

使用Mybatis遇到的there is no getter 異常

zhichangterry / 1472人閱讀

摘要:像在本次的測(cè)試代碼的話,會(huì)直接在返回,不過這不是重點(diǎn),繼續(xù)往下走,會(huì)返回到的方法的這一行此時(shí)的就是一個(gè)對(duì)象了。那么為什么加了注解之后就不會(huì)拋出異常呢此時(shí)就需要注意類的方法。

在使用mybatis的時(shí)候有時(shí)候會(huì)遇到一個(gè)問題就是明明參數(shù)是正確的,但是還是會(huì)提示There is no getter XXX這個(gè)異常,但是一般的解決辦法是在mapper里面添加@Param注解來完成是別的,那么為什么會(huì)遇到這個(gè)問題呢?

以下為舉例代碼:

Mapper層代碼
public interface Pro1_Mapper {

    Pro1_Studnet insertStu(Pro1_Studnet pro1_studnet);

}
實(shí)體類代碼
public class Pro1_Studnet {

    private String stuId;

    private String stuName;

    private String stuClass;

    private String stuTeacher;

    public String getStuId() {
        return stuId;
    }

    public void setStuId(String stuId) {
        this.stuId = stuId;
    }

    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }

    public String getStuClass() {
        return stuClass;
    }

    public void setStuClass(String stuClass) {
        this.stuClass = stuClass;
    }

    public String getStuTeacher() {
        return stuTeacher;
    }

    public void setStuTeacher(String stuTeacher) {
        this.stuTeacher = stuTeacher;
    }
}
Main方法
public static void main(String[] args) {
        Logger logger = null;
        logger = Logger.getLogger(Pro1_Main.class.getName());
        logger.setLevel(Level.DEBUG);
        SqlSession sqlSession = null;
        try {
            sqlSession = study.mybatis.MybatisUtil.getSqlSessionFActory().openSession();
            Pro1_Mapper pro1_Mapper = sqlSession.getMapper(Pro1_Mapper.class);
            Pro1_Studnet pro1_studnet =new Pro1_Studnet();
            pro1_studnet.setStuName("張三");
            Pro1_Studnet pro1_studnet1 =pro1_Mapper.insertStu(pro1_studnet);
            System.out.println(pro1_studnet1.getStuClass());
            sqlSession.commit();
        } finally {
            sqlSession.close();
        }
    }
XML文件



    
        
        
        
        
    
    

如果執(zhí)行上述的代碼,你會(huì)發(fā)現(xiàn)mybatis會(huì)拋出一個(gè)異常:
There is no getter for property named "pro1_studnet" in "class study.szh.demo.project1.Pro1_Studnet"
很明顯就是說pro1_studnet這個(gè)別名沒有被mybatis正確的識(shí)別,那么將這個(gè)pro1_studnet去掉呢?

嘗試將xml文件中的pro1_studnet去掉然后只保留stuName,執(zhí)行代碼:

張三

這表明程序運(yùn)行的十分正常,但是在實(shí)際的寫法中,還有如果參數(shù)為String也會(huì)導(dǎo)致拋出getter異常,所以此次正好來分析下

分析 mybatis是如何解析mapper參數(shù)的

跟蹤源碼你會(huì)發(fā)現(xiàn)在MapperProxyinvoke處會(huì)進(jìn)行入?yún)?

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

注意此處的args,這個(gè)參數(shù)就是mapper的入?yún)ⅰ?br>

那么mybatis在這里接收到這個(gè)參數(shù)之后,它會(huì)將參數(shù)再一次進(jìn)行傳遞,此時(shí)會(huì)進(jìn)入到MapperMethodexecute方法

public Object execute(SqlSession sqlSession, Object[] args) {
    //省略無關(guān)代碼
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method "" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

因?yàn)樵?b>xml文件里面使用的是select標(biāo)簽,所以會(huì)進(jìn)入case的select,然后此時(shí)會(huì)進(jìn)入到Object param = method.convertArgsToSqlCommandParam(args); 在這里args還是Stu的實(shí)體類,并未發(fā)生變化

隨后進(jìn)入convertArgsToSqlCommandParam方法,然后經(jīng)過一個(gè)方法的跳轉(zhuǎn),最后會(huì)進(jìn)入到ParamNameResolvergetNamedParams方法,

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 param = new ParamMap();
      int i = 0;
      for (Map.Entry 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;
    }
  }

此時(shí)注意hasParamAnnotation這個(gè)判斷,這個(gè)判斷表示該參數(shù)是否含有標(biāo)簽,有的話在這里會(huì)在Map里面添加一個(gè)參數(shù),其鍵就是GENERIC_NAME_PREFIX(param) + i 的值。像在本次的測(cè)試代碼的話,會(huì)直接在return args[names.firstKey()];返回,不過這不是重點(diǎn),繼續(xù)往下走,會(huì)返回到MapperMethodexecute方法的這一行result = sqlSession.selectOne(command.getName(), param);

此時(shí)的param就是一個(gè)Stu對(duì)象了。

繼續(xù)走下去...由于mybatis的調(diào)用鏈太多,此處只會(huì)寫出需要注意的點(diǎn),可以在自己debug的時(shí)候稍微注意下。

BaseExecutorcreateCacheKey的方法
@Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

當(dāng)進(jìn)行到這一步的時(shí)候,由于mybatis的類太多了,所以這里選擇性的跳過,當(dāng)然重要的代碼還是會(huì)介紹的。

DefaultReflectorFactory的findForClass方法
@Override
  public Reflector findForClass(Class type) {
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      return new Reflector(type);
    }
  }

注意MetaObjectgetValue方法:

 public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return null;
      } else {
        return metaValue.getValue(prop.getChildren());
      }
    } else {
      return objectWrapper.get(prop);
    }
  }

這里的name的值是pro1_stu.stuName,而prop的屬性是這樣的:

這里的hasNext函數(shù)會(huì)判斷這個(gè)prop的children是不是為空,如果不是空的話就會(huì)進(jìn)入 get 方法,然后進(jìn)入到如下的方法通過返回獲取get方法。
所以當(dāng)遍歷到stuName的時(shí)候會(huì)直接return,

然后就需要注意ReflectorgetGetInvoker方法,

public Invoker getGetInvoker(String propertyName) {
    Invoker method = getMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no getter for property named "" + propertyName + "" in "" + type + """);
    }
    return method;
  }

這個(gè)propertyName就是pro1_studnet,而getMethods.get(propertyName);就是要通過反射獲取pro1_studnet方法,但是很明顯,這里是獲取不到的,所以此時(shí)就會(huì)拋出這個(gè)異常。

那么為什么加了@param注解之后就不會(huì)拋出異常呢

此時(shí)就需要注意MapWrapper類的get方法。

 @Override
  public Object get(PropertyTokenizer prop) {
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, map);
      return getCollectionValue(prop, collection);
    } else {
      return map.get(prop.getName());
    }
  }

在之前就說過,如果加了注解的話,map的結(jié)構(gòu)是{"param1","pro1_studnet","pro1_studnet",XXX對(duì)象},此時(shí)由于prop的index是null,所以會(huì)直接返回map的鍵值為pro1_studnet的對(duì)象。
而在DefaultReflectorFactoryfindForClass里面,由于所加載的實(shí)體類已經(jīng)包含了Pro1_Student,隨后在metaValue.getValue(prop.getChildren());的將stu_name傳入過去,就可以了獲取到了屬性的值了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/77091.html

相關(guān)文章

  • 基于 SpringBoot2.0+優(yōu)雅整合 SpringBoot+Mybatis

    摘要:基于最新的,是你學(xué)習(xí)的最佳指南。驅(qū)動(dòng)程序通過自動(dòng)注冊(cè),手動(dòng)加載類通常是不必要。由于加上了注解,如果轉(zhuǎn)賬中途出了意外和的錢都不會(huì)改變。三的方式項(xiàng)目結(jié)構(gòu)相比于注解的方式主要有以下幾點(diǎn)改變,非常容易實(shí)現(xiàn)。公眾號(hào)多篇文章被各大技術(shù)社區(qū)轉(zhuǎn)載。 Github 地址:https://github.com/Snailclimb/springboot-integration-examples(Sprin...

    gghyoo 評(píng)論0 收藏0
  • mybatis 為什么每次插入時(shí)候總會(huì)創(chuàng)建一個(gè)SqlSession?

    摘要:?jiǎn)栴}記錄工作環(huán)境是使用使用用的,在一次調(diào)試中。發(fā)現(xiàn)每一次插入一條數(shù)據(jù)都會(huì)創(chuàng)建一個(gè)。如圖圖問題可能的原因原因分析沒有使用緩存因?yàn)檫@個(gè)是插入,不是查詢,所以這里不存在什么緩存的問題。但是在在關(guān)閉的時(shí)候也是做了判斷。 問題記錄: 工作環(huán)境是使用spring boot,使用用的mybatis,在一次調(diào)試中。發(fā)現(xiàn)每一次插入一條 數(shù)據(jù)都會(huì)創(chuàng)建一個(gè)SqlSession。如圖: 圖1:...

    sixgo 評(píng)論0 收藏0
  • SSM框架整合

    摘要:環(huán)境工具數(shù)據(jù)庫連接工具準(zhǔn)備工作步驟新建項(xiàng)目建好基本目錄,按住配置目錄,如下圖新建幾個(gè)包,如下圖包名名稱作用數(shù)據(jù)訪問層接口與數(shù)據(jù)操作有關(guān)的都放在這里實(shí)體類一般與數(shù)據(jù)庫的表相對(duì)應(yīng),封裝層取出來的數(shù)據(jù)為一個(gè)對(duì)象,也就是我們常說的,一般只在層與層之 環(huán)境 Windows10 JDK1.8 MySql5.5 工具 Maven3.5 IDEA2017 Navicat(數(shù)據(jù)庫連接工具) 準(zhǔn)備...

    lanffy 評(píng)論0 收藏0
  • JavaScript Getters and Setters

    For the most part, in JavaScript, what you see is what you get. A value’s a value; there are no tricks. Sometimes however, you want a value that’s based on some other values: someone’s full name, for ...

    warnerwu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<