zoukankan      html  css  js  c++  java
  • mybatis随笔四之MapperProxy

    在上一篇文章我们已经得到了mapper的代理对象,接下来我们对demoMapper.getDemo(1)这种语句进行分析。
    由于返回的mapper是个代理对象,因此会进入invoke方法,接下来我们来看看MapperProxy的invoke方法。
    @Override
      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);
      }
    Object.class.equals(method.getDeclaringClass())的意思是如果定义方法的类是个具体类就使用具体类的实现,如果是接口则往下执行。
    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;
      }
    methodCache是个Map<Method, MapperMethod>对象,第一次取时为空会进入MapperMethod构造方法。
    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, method);
      }
    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
          String statementName = mapperInterface.getName() + "." + method.getName();
          MappedStatement ms = null;
          if (configuration.hasStatement(statementName)) {
            ms = configuration.getMappedStatement(statementName);
          } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35
            String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
            if (configuration.hasStatement(parentStatementName)) {
              ms = configuration.getMappedStatement(parentStatementName);
            }
          }
          if (ms == null) {
            if(method.getAnnotation(Flush.class) != null){
              name = null;
              type = SqlCommandType.FLUSH;
            } else {
              throw new BindingException("Invalid bound statement (not found): " + statementName);
            }
          } else {
            name = ms.getId();
            type = ms.getSqlCommandType();
            if (type == SqlCommandType.UNKNOWN) {
              throw new BindingException("Unknown execution method for: " + name);
            }
          }
        }
    这里主要做了这几件事,根据方法名以及接口名的组合从configuration中取得对应的MappedStatement,然后从中取出name和type。

    MethodSignature构造方法如下
    public MethodSignature(Configuration configuration, Method method) {
          this.returnType = method.getReturnType();
          this.returnsVoid = void.class.equals(this.returnType);
          this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
          this.mapKey = getMapKey(method);
          this.returnsMap = (this.mapKey != null);
          this.hasNamedParameters = hasNamedParams(method);
          this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
          this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
          this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
        }
    这里主要是标记下入参中的RowBounds、ResultHandler类型参数,以及对返回值进行些标记。
    MethodSignature与SqlCommand初始化后MapperMethod也就构造完成,然后methodCache将method与mapperMethod关系保留。
    接下来就是屌用mapperMethod的execute方法来执行。
    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()) {
            executeWithResultHandler(sqlSession, args);
            result = null;
          } else if (method.returnsMany()) {
            result = executeForMany(sqlSession, args);
          } else if (method.returnsMap()) {
            result = executeForMap(sqlSession, args);
          } else {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(command.getName(), param);
          }
        } else if (SqlCommandType.FLUSH == command.getType()) {
            result = sqlSession.flushStatements();
        } 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;
      }
    我们的mapper方法定义如下,返回的不是集合也不为空,因此进入convertArgsToSqlCommandParam方法。
    convertArgsToSqlCommandParam对入参进行转换,如果没有入参返回null如果一个入参对象则直接返回,多个入参则封装成个map对象返回。
    public Demo getDemo(long id);
    现在进入到sqlSession的selectOne方法,使用ibatis的同学应该相当熟悉。
    在selectOne内部调用了selectList方法,然后返回集合对象的第一个元素,如果集合对象大于1个则抛错。
    @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    rowBounds是用来分页的,暂时不管该对象,默认的初始值如下
    public static final int NO_ROW_OFFSET = 0;
    public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
    selectList也是根据statement从configuration中取得mappedStatement,然后交由executor来执行,sqlSessionFactory构建的时候默认使用的是simpleExecutor

    到这里我们分析了mapper接口的方法最终是交到executor来执行。
     




  • 相关阅读:
    02---控制移动底座8
    02---控制移动底座7
    02---控制移动底座6
    02---控制移动底座5
    第四章输入/输出(I/O)4.2PCL中I/O模块及类介绍
    第四章输入/输出(I/O)4.1I/O涉及的设备及相关概念简介
    1.6 opencv视频操作基础
    1.5快速上手OpenCV图像处理
    02---控制移动底座4
    函数cvtColor
  • 原文地址:https://www.cnblogs.com/tuifeideyouran/p/6397788.html
Copyright © 2011-2022 走看看