• 关于mybatis的执行流程和源码


    关于mybatis的执行流程和源码

    Mybatis解析

    select * from user where name =? and pwd=?

    1. statement:select * from user where name = and pwd=(采用直接拼接的方式,有sql注入的风险)

    2. preparestatement:

       ​**~.setString(1,'name')    ~.setString(2,'pwd')**

       ​select * from user where name =‘${name}’ and pwd='‘${pwd}’'

       ​(采用String拼接的方式,没有sql注入的风险)

    Mybatis的执行流程图

     

    mybatis的四大处理器:

    解析config.xml、mapper.xml:

    @Before
    public static void initFactory() {
        try {
            SqlSession session = null;
            String resource = "configuration.xml";
            // 使用io流读取配置
            InputStream inputStream;
            inputStream = Resources.getResourceAsStream(resource);
            //这里是解析配置文件
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // 得到了一个会话,有了这个会话,你就可以对数据进行增,删,改,查的操作
            session = sqlSessionFactory.openSession();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
    }
    =======》new SqlSessionFactoryBuilder().build(inputStream);
                return build(inputStream, null, null);
                    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
                      return build(parser.parse());
                             if (parsed) {
                              throw new BuilderException("Each XMLConfigBuilder can only be used once.");
                            }
                            parsed = true;
                            parseConfiguration(parser.evalNode("/configuration"));//解析节点
                            //全局的配置文件就会被解析成一个org.apache.ibatis.session.Configuration
                            return configuration;
    ====parser.parse()调用========》parseConfiguration(parser.evalNode("/configuration"));//解析节点
        private void parseConfiguration(XNode root) {
            try {
              //issue #117 read properties first
              propertiesElement(root.evalNode("properties"));
              Properties settings = settingsAsProperties(root.evalNode("settings"));
              loadCustomVfs(settings);
              loadCustomLogImpl(settings);
              typeAliasesElement(root.evalNode("typeAliases"));
              pluginElement(root.evalNode("plugins"));
              objectFactoryElement(root.evalNode("objectFactory"));
              objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
              reflectorFactoryElement(root.evalNode("reflectorFactory"));
              settingsElement(settings);
              // read it after objectFactory and objectWrapperFactory issue #631
              environmentsElement(root.evalNode("environments"));
              databaseIdProviderElement(root.evalNode("databaseIdProvider"));
              typeHandlerElement(root.evalNode("typeHandlers"));
              mapperElement(root.evalNode("mappers"));//解析mapper节点
            } catch (Exception e) {
              throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
            }
          }
    /**
    *mapper接口的方法同时又xml配置和注解配置的话会报错
    *xml配置和注解配置都会被mybatis翻译成MappedStatement对象(两个mappedStatement的id相同(就是方法名),用的缓存容器是Hashmap(继承了hashmap的一个StateMap,重写了put())所以会报错)
    *<mappers>
            //如何只找到mapper文件的(mapper接口+*mapper.xml)
            <package name="mapper" />
            <!-- <mapper class="" resource="" url=""/> -->
        </mappers>
    */
    =============》 mapperElement(root.evalNode("mappers"));//解析mapper节点
        private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
              //解析mapper/package节点
            if ("package".equals(child.getName())) {
              String mapperPackage = child.getStringAttribute("name");
              configuration.addMappers(mapperPackage);//各种各样的mapper文件
            } else {
              String resource = child.getStringAttribute("resource");
              String url = child.getStringAttribute("url");
              String mapperClass = child.getStringAttribute("class");
                //解析你写的<mapper>注解的这三个属性(看源码可以看出这三个属性你只能配置一个属性)
                  /**
                  *resource/url是资源定位符(就是路径)
                  *    mapperClass你直接指定了一个接口
                  */
              if (resource != null && url == null && mapperClass == null) {
                  //配置了resource
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                mapperParser.parse();
              } else if (resource == null && url != null && mapperClass == null) {
                  //配置了url
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);//拿到mapper.xml文件的输入流
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                mapperParser.parse();//解析
              } else if (resource == null && url == null && mapperClass != null) {
                  //配置了mapperClass(直接拿接口了)
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }
    ===============》configuration.addMappers(mapperPackage);//各种各样的mapper文件
      public void addMappers(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        for (Class<?> mapperClass : mapperSet) {
          addMapper(mapperClass);//干活的地方
        }
      }
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {//拿接口
          if (hasMapper(type)) {//验证你是不是有一个加载的mapper接口了
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
          }
          boolean loadCompleted = false;
          try {
            knownMappers.put(type, new MapperProxyFactory<>(type));
            // It's important that the type is added before the parser is run
            // otherwise the binding may automatically be attempted by the
            // mapper parser. If the type is already known, it won't try.
            //解析接口(注解还是xml写的sql语句)
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
      }
    ============》parser.parse();//解析接口的注解
      public void parse() {
        String resource = type.toString();//先拿接口的名字<mapper class="" url="" resource=""/>
        if (!configuration.isResourceLoaded(resource)) {
          loadXmlResource();//加载xml文件
          configuration.addLoadedResource(resource);
          assistant.setCurrentNamespace(type.getName());
          parseCache();
          parseCacheRef();
          Method[] methods = type.getMethods();
          for (Method method : methods) {
            try {
              // issue #237
              if (!method.isBridge()) {
                parseStatement(method);
              }
            } catch (IncompleteElementException e) {
              configuration.addIncompleteMethod(new MethodResolver(this, method));
            }
          }
        }
        parsePendingMethods();//解析注解
      }
    ==============》//parsePendingMethods();//解析注解
       private void parsePendingMethods() {
        Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
        synchronized (incompleteMethods) {
          Iterator<MethodResolver> iter = incompleteMethods.iterator();
          while (iter.hasNext()) {
            try {
              iter.next().resolve();
              iter.remove();
            } catch (IncompleteElementException e) {
              // This method is still missing a resource
            }
          }
        }
      }
    ========================》mapperx.xml文件的解析
       public void parse() {
        if (!configuration.isResourceLoaded(resource)) {//判断是否解析过你这个文件了
          configurationElement(parser.evalNode("/mapper"));
          configuration.addLoadedResource(resource);
          bindMapperForNamespace();
        }
    
        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
      }
    ==========================》解析mapper的里面的节点,拿到里面的配置项,最终封装成一个MapperedStatement对象
     private void configurationElement(XNode context) {
        try {
          String namespace = context.getStringAttribute("namespace");
          if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          builderAssistant.setCurrentNamespace(namespace);
          cacheRefElement(context.evalNode("cache-ref"));
          cacheElement(context.evalNode("cache"));
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          sqlElement(context.evalNodes("/mapper/sql"));
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//解析你写的sql语句了,解析到一个list容器,
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
      }
    ===========================》//buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//解析你写的sql语句了,解析到一个list容器,
      private void buildStatementFromContext(List<XNode> list) {
        if (configuration.getDatabaseId() != null) {
          buildStatementFromContext(list, configuration.getDatabaseId());//那你配置的那个指定的databaseid
        }
        buildStatementFromContext(list, null);//开始循环遍历
      }
    ===========================》//buildStatementFromContext(list, null);//开始循环遍历
    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        //list表示Usermapper.xml的select|insert|update|delete节点
        for (XNode context : list) {
          final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
          try {
              //具体解析
            statementParser.parseStatementNode();
          } catch (IncompleteElementException e) {
              //你写的sql语句有问题
            configuration.addIncompleteStatement(statementParser);
          }
        }
      }
    ================================》//这个方法解析select|~|~|~节点的
     public void parseStatementNode() {
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");
    
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
          return;
        }
    
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        //刷新缓存:一级缓存二级缓存
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
    
        // Include Fragments before parsing
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());
    
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
    
        String lang = context.getStringAttribute("lang");
        LanguageDriver langDriver = getLanguageDriver(lang);
    
        // Parse selectKey after includes and remove them.
        processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        KeyGenerator keyGenerator;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
          keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
          keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
              configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
              ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
        //解析sql 根据sql文本来判断是否需要动态解析(就是看你写了动态sql标签没<where>...) 如果没有动态sql语句且只有#{}表达式时  直接使用?静态解析占位 当有${}时不进行解析
        /**
            科普:<select lang="就是自己写的一个语言解析器"></select>
            mybatis支持自己写自定义的标签,但是在解析标签时你必须指定语言相对应的语言解析器
        */
        /**
            ${}---->动态的sql语句(dynamicSqlSource)---解析配置文件的时候不会动这个sql语句,执行是才会去除${}符号(sql注入问题风险)
            #{}---->静态的sql语句(staticSqlSource)---解析的时候会把他改成?占位符(会对自动传入的数据加一个""双引号),mybatis官网有解释
        */
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        String resultType = context.getStringAttribute("resultType");
        Class<?> resultTypeClass = resolveClass(resultType);
        String resultMap = context.getStringAttribute("resultMap");
        String resultSetType = context.getStringAttribute("resultSetType");
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
        if (resultSetTypeEnum == null) {
          resultSetTypeEnum = configuration.getDefaultResultSetType();
        }
        String keyProperty = context.getStringAttribute("keyProperty");
        String keyColumn = context.getStringAttribute("keyColumn");
        String resultSets = context.getStringAttribute("resultSets");
        //构造一个MappedStatement
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered,
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
      }
    ======================》//构造一个MappedStatement  builderAssistant.addMappedStatement
          public MappedStatement addMappedStatement(
          String id,
          SqlSource sqlSource,
          StatementType statementType,
          SqlCommandType sqlCommandType,
          Integer fetchSize,
          Integer timeout,
          String parameterMap,
          Class<?> parameterType,
          String resultMap,
          Class<?> resultType,
          ResultSetType resultSetType,
          boolean flushCache,
          boolean useCache,
          boolean resultOrdered,
          KeyGenerator keyGenerator,
          String keyProperty,
          String keyColumn,
          String databaseId,
          LanguageDriver lang,
          String resultSets) {
    
        if (unresolvedCacheRef) {
          throw new IncompleteElementException("Cache-ref not yet resolved");
        }
    
        id = applyCurrentNamespace(id, false);
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
            .resource(resource)
            .fetchSize(fetchSize)
            .timeout(timeout)
            .statementType(statementType)
            .keyGenerator(keyGenerator)
            .keyProperty(keyProperty)
            .keyColumn(keyColumn)
            .databaseId(databaseId)
            .lang(lang)
            .resultOrdered(resultOrdered)
            .resultSets(resultSets)
            .resultMaps(getStatementResultMaps(resultMap, resultType, id))
            .resultSetType(resultSetType)
            .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
            .useCache(valueOrDefault(useCache, isSelect))
            .cache(currentCache);
    
        ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
        if (statementParameterMap != null) {
          statementBuilder.parameterMap(statementParameterMap);
        }
    
        MappedStatement statement = statementBuilder.build();
        configuration.addMappedStatement(statement);//将构建好的mapperedstatement文件添加到一个全局的配置文件中
        return statement;
      }

    解析sqlsessionFactory:

    session = sqlSessionFactory.openSession();
    ==========》//默认实现类org.apache.ibatis.session.defaults.DefaultSqlSessionFactory  然后调用openSessionFromDataSource
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          //通过解析到的configuration来获得environment标签
          final Environment environment = configuration.getEnvironment();
          //事物
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

    mapper代理对象的执行流程:

    CustomerMapper mapper = session.getMapper(CustomerMapper.class);该方法开始

    科普1:接口参数不合法时(即mapper接口方法的的参数名与mapper.xml参数名不一致时)解决方法:

    #{arg0}......#{argn}、#{param1}......#{paramn}、接口方法参数打@Param("参数名")注解

    为什么这么设计(为什么有arg0):

    JDK8之前,你拿参数名是拿不到的拿到的字符串是arg0;

    JDK8之后,你映射参数时才能使用具体的方法名

    为什么这么设计(为什么有param1):

    mybatis框架设计时,需要思考我拿到用户参数时,写什么参数名,为了你一定有一个参数可以使用,param1就产生了

      @SuppressWarnings("unchecked")
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
          return mapperProxyFactory.newInstance(sqlSession);//拿到代理的实例
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
    ===============》//return mapperProxyFactory.newInstance(sqlSession);//拿到代理的实例
    //MapperProxy实现了 InvocationHandler接口
        //invoke方法就是sql具体的执行流程了
        //我这是mybatis3.5.6的
      @Override
      public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
          return mapperMethod.execute(sqlSession, args);
        }
      }
    ===============》MapperMethed
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
          case INSERT: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.insert(command.getName(), param));
            break;
          }
          case UPDATE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.update(command.getName(), param));
            break;
          }
          case DELETE: {
            Object param = method.convertArgsToSqlCommandParam(args);
            result = rowCountResult(sqlSession.delete(command.getName(), param));
            break;
          }
          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);
              if (method.returnsOptional()
                  && (result == null || !method.getReturnType().equals(result.getClass()))) {
                result = Optional.ofNullable(result);
              }
            }
            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;
      }
    ============》//method.convertArgsToSqlCommandParam(args);单一非特殊参数返回没有名字。 多个参数用的命名规则命名(就是这里提供了param1这种参数写法)主要针对sql语句的参数映射
      public Object getNamedParams(Object[] args) {
        final int paramCount = names.size();
        if (args == null || paramCount == 0) {
          return null;
        } else if (!hasParamAnnotation && paramCount == 1) {
          Object value = args[names.firstKey()];
          return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
        } else {
          final Map<String, Object> param = new ParamMap<>();
          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 + (i + 1);
            // ensure not to overwrite parameter named with @Param
            if (!names.containsValue(genericParamName)) {
              param.put(genericParamName, args[entry.getKey()]);
            }
            i++;
          }
          return param;
        }
      }
    ============》//select查询具体调用方法流程
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        List<T> list = this.selectList(statement, parameter);//查询都是调用的selectList
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }    
    ===============》//调用selectList
    @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();
        }
      }
    ==========》//开始查询
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //sql语句就包装在这里面(走完这行代码:静态的sql语句没有赋值但是#{}改成了?,动态的sql语句已经赋值了)
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    =========》query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
              //开始先在缓存中去查询
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
              //数据库中去查询
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
    ===========》//去数据库查询
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            //根据你指定的执行器去调用不同的方法(默认是simpleExecutor)
            /**
            三大执行器:
                SimpleExecutor:每执行一次update或select,就开bai启一个Statement对象,用完立刻关闭Statement对象。
                ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
                BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。(使用时出现一种情况:我无法在他这个处理器的缓存中拿到我希望得到的id)
            四大处理器:
                StatementHandler:处理对象PrepareStatement
                ParameterHandler:处理参数
                ResultSetHandler:处理结果集
                TypeHandler:类型转换
            */
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          localCache.removeObject(key);
        }
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
          localOutputParameterCache.putObject(key, parameter);
        }
        return list;
      }
    ================》//simpleExecutor的doQuery()
    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            //这个就是JDBC使用preparestatement去查询数据了
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    ===========》//jdbc
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        //这里实现~.setString的()设置参数(调用的是RoutingStatementHandle)
        handler.parameterize(stmt);
        return stmt;
      }
    ==========》//调用prepareStatement的parameterize方法
     @Override
      public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
      }
    ==========》//调用defaultpreparestatementHandler的setParameters((PreparedStatement) statement);参数都存在parameterObject这个对象里(给问号赋值)----走完这个方法就是preparestatement已经完成赋值操作了
    @Override
      public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
            //迭代参数对比参数名字和参数类型和参数顺序分别赋值
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
              Object value;
              String propertyName = parameterMapping.getProperty();
              if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                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);
              }
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) {
                jdbcType = configuration.getJdbcTypeForNull();
              }
              try {
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
              } catch (TypeException | SQLException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              }
            }
          }
        }
      }
  • 相关阅读:
    winform右键菜单
    IIS添加映射配置
    linux+nginx+python+django环境配置
    ASP.NET获取客户端IP地址
    Ext未定义问题解决
    c#获取硬件信息
    DevExpress GridControl使用方法总结
    CDN技术原理
    Mac搭建nginx+rtmp服务器
    MAXOS安装FFMPEG
  • 原文地址:https://www.cnblogs.com/five-five/p/13869431.html
Copyright © 2020-2023  润新知