鏈接MyBatis內部SqlSession與業務接口的代理類MapperProxy

目的
java

系統中的業務接口須要調用MyBatis的SQL時,業務接口定義的參數不符合MyBatis本身內部的規範,那麼就須要把業務接口的參數轉換成MyBatis內部參數規,MapperProxy代理就完成了這一職責,下面就來分析一下。sql

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

private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
}

上面的invoke就是代理類回調的方法,而cachedMapperMethod方法,作了一個緩存。這裏MapperMethod類的構造器與MapperMethod類的execute方法就是主要的邏輯,先來看一下MapperMethod構造器。數組

MapperMethod構造器緩存

這裏有2個類的實例化,根據圖中的查詢分析一下這2個類。app

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) throws BindingException {
      // cn.vansky.schedule.time.menu.dao.MenuMapper.findMenuByUserId
      String statementName = mapperInterface.getName() + "." + method.getName();
      MappedStatement ms = null;
      if (configuration.hasStatement(statementName)) {
        ms = configuration.getMappedStatement(statementName);
      } else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
        String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
        if (configuration.hasStatement(parentStatementName)) {
          ms = configuration.getMappedStatement(parentStatementName);
        }
      }
      if (ms == null) {
        throw new BindingException("Invalid bound statement (not found): " + statementName);
      }
      // cn.vansky.schedule.time.menu.dao.MenuMapper.findMenuByUserId
      name = ms.getId();
      // SELECT
      type = ms.getSqlCommandType();
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
}

public MethodSignature(Configuration configuration, Method method) throws BindingException {
      // 返回值類型Class
      // interface java.util.List
      this.returnType = method.getReturnType();
      // 有無返回值 true:無,false:有
      // false
      this.returnsVoid = void.class.equals(this.returnType);
      // 返回類型是不是集合Collection或者是數組
      // true
      this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
      // 返回類型是Map,獲取註解MapKey
      // 以上方法調用,值爲null
      this.mapKey = getMapKey(method);
      // 返回類型是否Map
      // false
      this.returnsMap = (this.mapKey != null);
      // 參數是否有@Param註解
      // true
      this.hasNamedParameters = hasNamedParams(method);
      // null
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      // null
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      // 獲取參數列表
      // 0 -> userId 
      this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}

SqlCommand類獲取處理的惟一標識及SQL語句類型,MethodSignature類對業務接口方法的入參類型及出參類型進行處理。this

MapperMethed執行入口-->execute方法spa

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
      // 新增
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      // 修改
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      // 刪除
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      // 查詢
      if (method.returnsVoid() && method.hasResultHandler()) {
        // 無返回值void
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        // 集合Collection或數組
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        // Map
        result = executeForMap(sqlSession, args);
      } else {
        // 惟一結果
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      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;
}

增(INSERT)、改(UPDATE)、刪(DELETE),這3種操做能夠概括爲一種,首先對業務接口的實際入參轉成MyBatis內部參數,而後調用SqlSession的處理方法,最後對結果進行處理,返回結果。代理

查(SELECT)有4中狀況。code

一:無返回值接口

二:返回是集合Collection或者數組

三:返回是Map

四:返回一個結果

4種狀況的具體細節再也不分析。

總結

綜上首先須要先把業務接口的實際入參轉成MyBatis內部的參數,而後調用SqlSession相應的處理方法,最後對返回結果進行處理,在返回給業務接口。

相關文章
相關標籤/搜索