Mybatis源码全流程切割分析

老司机学习MyBatis之动态SQL使用trim自定义字符串截取 一、前言前面一个章节讲到,如果where标签里面,and或者or写在后面,最后字符串拼串后,如果最后一个条件没有加上,可能会出现SQL多出 and或者or 的情形。那我们是不是没有办法解决这种问题的产生,事实上MyBatis为我们提供了另外一种标签trim,可以简单粗暴的解决这个问题,下面我们通过案例来看看如何实现二、案例我们先来看看前面使用where标签时的情景现在我们对上面的XML做如下改造在... 阅读详情

1. Mybatis

本文mybatis源码的分析大致思路围绕着下列代码进行开展的,如下4段代码。

//获取SqlSession工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("Mybatis-Config.xml"));
//工厂生成一个SqlSession会话
SqlSession session = factory.openSession(true);
//获取一个代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
//执行sql语句并映射结果集
User user = mapper.selectById(42);

2. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(“Mybatis-Config.xml”));

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
    /**
    	inputStream表示字节输入流的类的超类,通过它来读取xml文件,Resources.getResourceAsStream("Mybatis-Config.xml")
    	最终返回的是一个BufferedInputStream字节输入流对象,因为无论是类还是xml文件等,都需要被classLoader装载到内存后才能被当前线程使用,
   		而Mybatis-Config.xml文件其实是被AppClassLoader下的getResourceAsStream方法装载到内存的,
   		(根据双亲委派原则,AppClassLoader负责加载类路径下的指定内容)
   		此时的inputStream对象中的数据是字节序列,需要被解析转化
   		environment和properties此时为均为空对象,将引用传递new XMLConfigBuilder(inputStream, environment, properties);
   		由于是单例对象引用传递,该构造方法执行结束后,environment和properties均有值
   	**/
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
  
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties)的详细如下:

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
	//注:这里new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())
	//将inputStream的字节序列数据转换为document节点树对象
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
 //初始化一个Configuration对象,成员变量均为初始值
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

补充XPathParser:

 public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
	//XMLInputSource读取字节数组中的数据,最终将其转换为Document节点树对象
	//Document对象是org.w3c.dom包下的,Document接口代表整个 HTML 或 XML 文档。 从概念上讲,它是文档树的根,并提供对文档数据的主要访问。。
    this.document = createDocument(new InputSource(inputStream));
  }

super(new Configuration())

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    //别名注册实例化
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    //类型转化注册器实例化,当数据库映射字段类型和实体类属性类型不一致时,则通过该类型转换器将映射字段类型处理为实体类属性字段
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);执行结束后, XMLConfigBuilder对象具有主要的成员遍历有:实例化完成的Configuration配置对象,此时并未初始化完成,TypeAliasRegistr别名注册器,TypeHandlerRegistry做类型处理注册

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
      ....省略以下代码
      build(parser.parse());
     			|
XMLConfigBuilder:    
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //该方法用于初始化Configuration配置类对象
    //parser.evalNode("/configuration")解析document对象为XNode对象(该对象是Mybatis自定义的对象,用于存储document中的Node节点)
    parseConfiguration(parser.evalNode("/configuration"));
    //此时的configuration初始化完成,返回
    return configuration;
  }

补充: parseConfiguration(parser.evalNode("/configuration"));

private void parseConfiguration(XNode root) {
    try {
      //这些字段均为Mybatis配置文件中可配置的标签
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(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"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

执行结束后返回一个SqlSessionFactory对象

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

总结:SqlSessionFactoryBuilder()通过读取配置文件转为InputStream对象,再读取该对象转换为Document节点对象,将该对象封装在XPathParser(Mybatis自定义)中,再调用XPathParser的evalNode读取对应的Node节点。再将该节点封装再XNode对象。
为啥这样呢?因为方便接下来build构造SqlSessionFactory更加便捷,通过指定字段读取XNode的指定节点对象,让其封装在特点对象中,如配置文件中的environment标签封装在environment对象中,mappers标签下配置的类路径会使用类加载器加载到内存等待读取,如配置了package name=“com.hnjd.mapper”/ 会去扫描mapper下的接口将其封装在MapperRegistry对象中,内有一个成员变量knownMappers,HashMap的结构,key值为接口的类信息,value值为一个代理对象MapperProxyFactory工厂对象,该对象中封装了相应的Class对象,最终返回一个MapperProxy代理对象,这就是为啥我们可以通过 UserMapper mapper = session.getMapper(UserMapper.class);获取到相应的UserMapper代理对象,就是去MapperRegistry对象中读取的。
最终像environment对象,以及MapperRegistry对象和配置文件中的标签转化的对象,都存放在Configuration对象中,类似在配置文件中configuration是environment和mappers的根标签一样,而Configuration配置类对象又存储在SqlSessionFactory中。
最后可以分析得出:SqlSessionFactory有两件事:1.存放Configuration配置类,2.根据Configuration配置类生产相应的SqlSession。

补充MapperRegister:
在这里插入图片描述

3.SqlSession session = factory.openSession(true);

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
 @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }
			
		
//configuration.getDefaultExecutorType()获取默认的执行器
//Configuration中默认的执行器为SIMPLE
//可以通过	<setting name="defaultExecutorType" value="BATCH"/>配置
// protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
//而执行器类型分三种,是枚举的形式(看Mybatis版本所示)
package org.apache.ibatis.session;
public enum ExecutorType {
//策略模式,三种策略
  SIMPLE, REUSE, BATCH
}
//根据Mybatis官方文档所述
SIMPLE:普通的执行器
REUSE:重用预处理语句(PreparedStatement)
BATCH:不仅重用语句还会执行批量更新

生产SqlSession的过程如下所示

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
    	//获取 Environment,用于连接数据库,Mybatis底层操作MySQL数据库的方式仍然是JDBC,只不过对结果集映射的处理更加只能了,还有各种拦截器的配合使用,同时可以利用拦截器生成日志了
      final Environment environment = configuration.getEnvironment();
      //获取事务工厂,为JdbcTransactionFactory,根据Connection用于生产JdbcTransaction
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //JdbcTransaction的事务控制就是封装了下Connection的commit和rollback,当openSession(true)指定为true了,就是使用JdbcTransaction做事务控制,
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //根据事务和执行器类型生成Executor,Mybaits是使用Executor执行SQL语句所以需要事务transaction对象。
      final Executor executor = configuration.newExecutor(tx, execType);
      //根据Configuration配置类和Executor执行器生成SqlSession对象,以及autoCommit是否由Mybaits管理事务
      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();
    }
  }

补充: final Executor executor = configuration.newExecutor(tx, execType);执行器的实例化过程

org.apache.ibatis.session.Configuration

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//根据执行器类型生成相应的执行器
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
     //Batch类型
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      //REUSE类型
      executor = new ReuseExecutor(this, transaction);
    } else {
      //SIMPLE类型
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
    //!无论是那种类型这里都会将其封装到CachingExecutor执行器中,也就是说当调用执行器的方法,会调用CachingExecutor再调用相应类型执行器的方法
      executor = new CachingExecutor(executor);
    }
    //将执行器进行动态代理
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
	
//补充  executor = (Executor) interceptorChain.pluginAll(executor);
org.apache.ibatis.plugin.InterceptorChain

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

  public Object pluginAll(Object target) {
  //如果配置了拦截器则调用拦截器内部的plugin方法,plugin会调用Plugin类的warp方法进行动态代理,传入的target方法正是执行器对象
    for (Interceptor interceptor : interceptors) {
    //循环目的,配置了多少个拦截器就代理几次,类似套娃
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}
.
//补充org.apache.ibatis.plugin.Plugin 
//targetCachingExecutor执行器对象
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
    //返回代理对象
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

总结:获取SqlSession的过程就是,从factory工厂拿出Configuration配置类,从Configuration拿出Environment生成Transaction事务管理,Trasaction事务对象生成后,根据Tracation和执行器类型生成Executor执行器,最后将Configuration和Execturo执行器和标识事务管理传入SqlSession中返回。
最后分析得出:SqlSession就是根据Executuro执行器和Configuration配置类生成的,而执行器就是根据Trasaction事务管理器生成的,而SqlSesion的作用由于有执行器则可以执行语句,由于有Trasaction则可以管理事务,以及获取映射器(请继续见本文分析)

4.UserMapper mapper = session.getMapper(UserMapper.class);

  org.apache.ibatis.session.defaults.DefaultSqlSession
  
  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }
 
  org.apache.ibatis.session.Configuration
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //这就是加载Configuration通过读取配置文件中Mappers节点封装生成的MapperRegistry
    return mapperRegistry.getMapper(type, sqlSession);
  }
	
  org.apache.ibatis.binding.MapperRegistry

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  	//key值为接口的类信息,value值为一个代理对象MapperProxyFactory工厂对象,该对象中封装了相应的Class对象,最终返回一个MapperProxy代理对象
    //private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
    //根据Class类信息获取生成代理对象的工厂MapperProxyFactroy
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
    	//如果该Class对象并没有在MapperRegistry中注册,或者说是没有在配置文件Mappers中配置,报出该类型并没有被注册的异常信息
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
     //根据sqlSession通过工厂实例化一个代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  } 
 
  org.apache.ibatis.binding.MapperProxyFactory<T>
  public T newInstance(SqlSession sqlSession) {
  	//根据sqlSession和类信息生成一个MapperProxy对象
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

	protected T newInstance(MapperProxy<T> mapperProxy) {
    	//调用JDK的代码生成代理对象
    	return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

总结:通过class类信息getMapper的过程就是去Configuration中的MapperRegistry中的Class类信息和MapperProxyFactory工厂的Map对象中根据Class类信息get相应的MapperProxy工厂实例化生成MapperProxy代理对象

5.User user = mapper.selectById(42);

该mapper对象是一个MapperProxy代理对象,请看MapperProxy代理对象中的invoke方法

@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);
    }
    //重点在这,上述只是特殊情况的判断
    //将Method对象放进methodCache缓存中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //传入SqlSession(因为内部封装了执行器,sql语句的执行需要它),args就是方法参数调用execute
    return mapperMethod.execute(sqlSession, args);
  }
 //补充:
	private MapperMethod cachedMapperMethod(Method method) {
	//private final Map<Method, MapperMethod> methodCache;
		//当方法重复调用时,优先取缓存中取
    	MapperMethod mapperMethod = methodCache.get(method);
    	if (mapperMethod == null) {
    	//当方法初次调用时,生成MapperMethod对象放进缓存中,该对象中主要是SqlCommand(封装了类路径和Sql语句的类型,如本文是“SELECT”,策略模式,详细了解请继续看下文所述)和MethodSignature方法签名(判断返回值类型)两个成员变量
     	 mapperMethod = new MapperMethod(mapperInterface, method, 	sqlSession.getConfiguration());
      	methodCache.put(method, mapperMethod);
    	}
    	return mapperMethod;
  }

mapperMethod.execute(sqlSession, args)

 org.apache.ibatis.binding.MapperMethod
   public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
    //步骤4.在MapperMethod的SqlCommand中封装的SQL语句类型在这里起作用了,策略模式,本文为查询SELECTE,所以直接看SELECTE
      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:
      //这里条件判断就是根据MapperProxy执行方法后的返回值是什么类型判断的,method为MethodSignature
        if (method.returnsVoid() && method.hasResultHandler()) {
        //无返回值
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          //返回的是集合等类型的
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          //返回的是Map类型的
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          //流式查询
          result = executeForCursor(sqlSession, args);
        } else {
         //本文返回值是User对象,所以走这
         //获取方法入参参数
          Object param = method.convertArgsToSqlCommandParam(args);
          //执行SqlSession查询单个方法,传入类路径和入参参数
          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;
  }

请看DefaultSqlSession对象中的SelectOne和selectList方法

 @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    //statement类路径,parameter入参参数
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
    //由于是查询单个则返回list集合的第一个元素
      return list.get(0);
    } else if (list.size() > 1) {
    //查询单个,如果查询出的数据数量大于1,则抛出TooManyResultsException异常
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
 @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    //根据类路径在Configuration的配置类中获取MappedStatement
   	//  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
   	//MappedStatement中封装了Configuration和类路径以及sql语句映射xml文件和返回值类型以及sql语句类型还有Sql语句,拦截器就是通过拦截该对象来生成日志的,因为它内部封装了语句执行的所有内容,此时部分属性仍为空,需要后续传入。
      MappedStatement ms = configuration.getMappedStatement(statement);
      //在SqlSession中调用Executor执行器执行sql语句,由于类型(BATCH,REUSE,SIMPLE)执行器被封装CachingExecutor对象中,而CachingExecutor对象由被动态代理了(本文配置了拦截器),所有会调用Plugin中的invoke方法,优先执行拦截器中的方法.
      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();
    }
  }
  
//org.apache.ibatis.plugin.Plugin
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    //这里的signature指的是拦截器上配置的@Signature注解,signatureMap的key值封装了执行器类信息,value值是根据配置的@Signature注解内的信息封装的Method方法
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
      	//执行配置拦截器内的intercept方法
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //如果该方法未被拦截则直接返回
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
  
//补充配置拦截器的代码如下:
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class,
                Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class,
                Object.class, RowBounds.class, ResultHandler.class})
})
public class MybatisPlugin implements Interceptor {


    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
        String sql = buildMethodArgNameMapAndResultSql(mappedStatement, args);
        System.out.println("sql = " + sql);
        //根据invocation中的method对象类型执行相应方法,如何又是代理对象则继续执行一遍拦截器方法,当执行到Executor时,结束代理,
        return invocation.proceed();
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    public void setProperties(Properties properties) {

    }

    public String buildMethodArgNameMapAndResultSql(MappedStatement mappedStatement, Object[] args){
        Map<String,Object> nameArgMap = Maps.newHashMap();
       Object parameterValue = null;
        if(args.length>1){
            //获取参数,if语句成立,表示sql语句有参数,参数格式是map形式
            parameterValue = args[1];
        }else {
            throw new RuntimeException("参数为空");
        }

        String[] split = parameterValue.toString().substring(1,parameterValue.toString().lastIndexOf('}')).split(",");
        String[] names = new String[split.length];
        final int[] i = {0};
        Arrays.asList(split).forEach(parameter -> {
            String name = parameter.substring(0,parameter.lastIndexOf('=')).trim();
            String value = parameter.substring(parameter.lastIndexOf('=')+1,parameter.length()).trim();
            names[i[0]] = name;
            i[0] = i[0] +1;
            nameArgMap.put(name,value);
        });
        BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql(parameterValue);
        ParameterMapping parameterMapping = boundSql.getParameterMappings().get(0);
        String parameterKey = parameterMapping.getProperty();
        System.out.println("parameterKey = " + parameterKey);
        String sql = boundSql.getSql();
        StringBuilder resultSql = getSql(nameArgMap, sql, names);
        return resultSql.toString();
    }
    public StringBuilder getSql(Map<String,Object> map ,String sql,String[] names){
        StringBuilder sb = new StringBuilder();
        System.out.println("map:"+map);
        System.out.println(sql);
        int length =sql.length();
        for (int i = 0; i < length; i++) {
            char c =sql.charAt(i);
            if(c == '?'){
                for (int j = 0;j<names.length;j++){
                    if(sb.indexOf(names[j])!=-1){
                        sb.append(map.get(names[j]));
                        names[j] = "  ";
                    }
                }

                continue;
            }
            sb.append(sql.charAt(i));
        }
        return sb;
    }
}

CachingExecutor执行器的query方法

@Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  //根据MappedStatement获取sql语句,以及入参参数
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //做一层缓存
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
  
 @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    //使用CachingExecutor的目的就是多次查询时,无需重复访问数据库,Mybatis做了缓存执行器,可以当重复请求时则在该处获取缓存直接返回了
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //deletate则为配置的执行器类型,由于第一次访问,无缓存则直接走配置类型的queyr方法,而Batch和Reuse以及Simple执行器的父类都是BaseExecutor执行器,所以会先走父类
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

BaseExecutor的public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) =>BaseExecutor的 private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)方法

//org.apache.ibatis.executor.BaseExecutor

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    //将sql语句相关信息放进localCache缓存中,当执行BaseExecutor的queyr方法时会先去localCache中取sql语句的信息,取到了直接返回,则无需访问数据库了
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
    	//BaseExecutor的doQuery方法是一个抽象方法,所以会交给子类执行,此时就到了配置的BatchExecutor(本文环境这样配置的,默认为Simple)出场了
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    //查询结束后将sql语句信息和查询的结果集合封装到localCache中,也就是一级缓存~
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
  
org.apache.ibatis.executor.BatchExecutor
@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      flushStatements();
      //获取配置类
      Configuration configuration = ms.getConfiguration();
	  //生成StatementHandler,看下列补充
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

//补充:
org.apache.ibatis.session.Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//生成statementHandler的目的为了处理statement,请看下列补充2
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //这里的动态代理操作和执行器的动态代理操作一模一样,套娃式代理,配置了几个拦截器就代理几次,最终就调用几次拦截器中的intercept的方法
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
 }
//补充2:
org.apache.ibatis.executor.statement.RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
      	//无需预处理
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
      	//需要预处理
      	//本文走这
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
      	//存储过程的调用
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

确定好StatementHandler的类型PreparedStatementHandler后,最终会生成它的父类BaseStatementHandler

org.apache.ibatis.executor.statement.BaseStatementHandler

 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;
	
	//重点在这,当statementHandler的类型确定好后,会生成相应的参数处理器parameterHandler和结果集处理器resultSetHandler
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

//补充:
//生成ParameterHandler和ResultSetHandler的过程同样会进行套娃式动态代理,为了调用拦截器的intercept方法,根据配置的签名拦截相应方法类型
//Configuration中
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

当StatementHandelr实例化完成后,就可以开始准备访问数据库了,回到doQuery方法

org.apache.ibatis.executor.BatchExecutor
@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      flushStatements();
      //获取配置类
      Configuration configuration = ms.getConfiguration();
	  //生成StatementHandler,看下列补充
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
     //获取连接,JDBC操作开始了
      Connection connection = getConnection(ms.getStatementLog());
      //看补充
      stmt = handler.prepare(connection, transaction.getTimeout());
      //调用StatementHandler处理生成的statement,其实是调用内部的parameterHandler处理参数看补充2
      handler.parameterize(stmt);
      //处理完sql语句的参数后,调用StatementHandler的query方法,resultHandelr为null,其实就是调用PreparedStatementHandler中的resultSetHandler对象,将sql语句的执行结果传入进行参数处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
//补充:
//调用BaseStatement的prepare方法
 @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
     //JDBC操作,操作数据库,生成Statement对象
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
  
//补充2
org.apache.ibatis.executor.statement.PreparedStatementHandler
 @Override
  public void parameterize(Statement statement) throws SQLException {
  	//将方法入参参数代替sql语句中的#{id},这里就不进行深入了,内部就是算法对参数处理
    parameterHandler.setParameters((PreparedStatement) statement);
  }

继续看Executor的doQuery方法的返回值是handler.query(stmt, resultHandler);中PreparedStatementHandler的query方法

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {

    PreparedStatement ps = (PreparedStatement) statement;
    //执行sql语句
    ps.execute();
    //将PreparedStatement传入resultSetHandler进行参数处理
    return resultSetHandler.<E> handleResultSets(ps);
  }

PreparedStatement中的ResultSet执行结果如下图所示:
在这里插入图片描述
继续看ResultSetHandler的handleResultSets方法

org.apache.ibatis.executor.resultset.DefaultResultSetHandler
@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
	//结果集容器
    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    //处理statement中ResultSet的结果集,将列名和类型读取出来包装在ResultSetWrapper中
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      //映射的实体类对象,实体类中属性所需的类型
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //将数据库中读取的数据类型rsw和实体类属性的类型resultMap和一个结果集容器(此时为空)传入,此时处理结果集.
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

handleResultSet(rsw, resultMap, multipleResults, null);=>handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);=> handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);均为结果集处理流程

//org.apache.ibatis.executor.resultset.DefaultResultSetHandler

 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      //封装了数据库字段名,实体类属性类型,javaType和jdbcType等信息都封装在该类中,结果集映射
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
	  //重点在这,结果集处理
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }
  
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //返回结果集对象!如果不为空直接返回了,也就是结果集映射结束了,查询出的对象返回。
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
    }
    return rowValue;
 }

  private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    this.useConstructorMappings = false; // reset previous mapping result
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();
   	//!获取结果集对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      for (ResultMapping propertyMapping : propertyMappings) {
        // issue gcode #109 && issue #149
        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
          resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          break;
        }
      }
    }
    //设置当前映射结果
    this.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); 
    return resultObject;
  }

 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
      throws SQLException {
      //返回的对象Class类信息
    final Class<?> resultType = resultMap.getType();
    //根据类信息和反射工厂生成MetaClass对象
    final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
    //如果返回值对象中使用了参数构造器,构造器中封装的属性类型封装在该List集合中(本文使用了有参构造器)
    final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
    if (hasTypeHandlerForResultObject(rsw, resultType)) {
      return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    } else if (!constructorMappings.isEmpty()) {
    //如果对象的有参构造器不为空,则使用参数构造器创建返回结果集对象。
      return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
    } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
      return objectFactory.create(resultType);
    } else if (shouldApplyAutomaticMappings(resultMap, false)) {
      return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
    }
    throw new ExecutorException("Do not know how to create an instance of " + resultType);
  }
  
Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
                                         List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
    boolean foundValues = false;
    //根据构造器中的入参参数类型数量遍历,封装在constructorArgs结合中
    for (ResultMapping constructorMapping : constructorMappings) {
      final Class<?> parameterType = constructorMapping.getJavaType();
      final String column = constructorMapping.getColumn();
      final Object value;
      try {
        if (constructorMapping.getNestedQueryId() != null) {
          value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
        } else if (constructorMapping.getNestedResultMapId() != null) {
          final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
          value = getRowValue(rsw, resultMap);
        } else {
          final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
          //其实就是根据参数的不同类型去ResultSet中根据列名取值
          //如此时是String类型就好在typeHandler下的子类StringTypeHandler中的getNullableResult方法调用ResultSet的getString方法
          //(还是JDBC操作)
          value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
        }
      } catch (ResultMapException e) {
        throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
      } catch (SQLException e) {
        throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
      }
      constructorArgTypes.add(parameterType);
      //传入集合中
      constructorArgs.add(value);
      foundValues = value != null || foundValues;
    }
    return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
  }


//补充本文的实体类:
@Data
@ToString
@AllArgsConstructor
public class User {
    private Integer id;
    private String username;
    private Timestamp birthday;
    private String sex;
    private String address;

}

objectFactory.create(resultType, constructorArgTypes, constructorArgs),该方法根据相应实体类中的参数构造器实例化对象。

org.apache.ibatis.reflection.factory.DefaultObjectFactory
@Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    Class<?> classToCreate = resolveInterface(type);
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }
  
 <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      //constructorArgTypes参数类型封装不为空直接走这
      //获取参数构造器
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      //调用JDK代码实例化对象,返回对象
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

此时其实一个完整的返回值对象已经构建完毕,回到DefaultResultSetHandler的createResultObject创建结果集对象这里,
这里存在一个小问题可以参考我的另一篇博客分析:类型处理

 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
      throws SQLException {
    final Class<?> resultType = resultMap.getType();
    final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
    final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
    if (hasTypeHandlerForResultObject(rsw, resultType)) {
      return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    } else if (!constructorMappings.isEmpty()) {
    //执行结束,返回结果集
      return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
    } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
      return objectFactory.create(resultType);
    } else if (shouldApplyAutomaticMappings(resultMap, false)) {
      return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
    }
    throw new ExecutorException("Do not know how to create an instance of " + resultType);
  }

此时结果集对象构建完成,ResultSetHandler的工作也完成了,执行结束后会将该对象存储到MetaObject对象中,一路返回到BatchExecutor的doQuery方法

@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      flushStatements();
      Configuration configuration = ms.getConfiguration();
      // StatementHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      //ResultSetHandler
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  //此时一个完整对象已经构造完成了,源码分析结束。

总结:通过调用MapperProxy代理对象的invoke方法确定SQL语句的类型,调用SqlSession的相应方法,SqlSession会去调用Executor执行器,但由于使用了拦截器,执行器已被Plugin对象中的wrap方法调用创建了代理对象,所以会走Plugin对象的invoke方法,调用拦截器的intercept方法,然后才会去执行CachingExecutor缓存执行器的方法,如query查询,优先去缓存中取,如果换成中有执行结果则直接返回,没有则调用相应的执行器取执行sql语句,Executor会去创建一个StatementHandler对象,StatementHandler对象中封装了Preparestatement和ResultsetHandler对象,Preparestatement用于处理sql语句的参数,ResultSetHandler用于处理statement执行完后的ResultSet结果集对象,最终返回相应的实体类对象。返回到Executo执行器中,将返回结果作为value值放进localCache一级缓存中,方法的类路径和执行语句等信息作为key值,下次访问当调用执行器时,会先检测localCache中是否有该方法的缓存,有则直接返回不做数据库访问了。
在这里插入图片描述

6.后言

大总结下:
mybatis的执行流程就是:根据配置文件生成一个Configuration配置类对象从而构建出一个SqlSessionFactory工厂,该工厂通过实例化一个执行器Executor并确定执行器的类型,将执行器放入CachingExecutor执行器中(委托模式),若配置了拦截器,此时还会调用Plugin对象的wrap方法进行动态代理生成代理对象和事务控制器Trasacation生成一个SqlSesson对象,再从MapperRegister注册器中根据类信息取出注册的MapperProxyFactory工厂,再由该工厂根据SqlSession生产了一个并进行了一层动态代理mapperProxy对象,调用mapperProxy对象会优先执行MapperProxy中的invoke方法,根据策略模式,调用sqlsession的相应方法,然后会去让Executor去执行sql语句,如果是SELECT类型同时又需要处理参数,它会创建一个statementHandler对象,若有拦截器配置,该对象同样会被动态代理,同时实例化Preparestatement和ResultSetHandler对象,同样,若有拦截器配置,这两个同样会被动态代理(此时可以得知,Mybatis拦截器可以在执行器执行时,预处理SQL语句时,处理返回集结果时配置签名进行拦截),Preparestatement用于预处理SQL语句,ResultSetHandler用于处理ResultSet执行sql语句后数据库返回的数据,处理完后,放入localCache一级缓存中,这就是Mybatis的基本执行流程。
谢谢阅读,如有错误,还请斧正。

SSM框架+Plupload实现分块上传(Spring+SpringMVC+MyBatis+Plupload) 关于Plupload的介绍,相信它的官网http://www.plupload.com/已经给得很详细了。Plupload的上传原理简单点说,就是将用户选中的文件(可多个)分隔成一个个小块,依次向服务器上传,这是它能驾驭上传大文件的原因之一,而且在这个过程可以暂停上传,暂停后再继续上传(异于断点续传)。最重要的是,从头到尾没有一点点UI阻塞,保证了用户体验。下面会开始讲Plupload的实现流程,分 阅读详情

相关推荐

(附源码)计算机毕业设计SSM幼儿英语学习平台的设计与实现

用户要通过填写自己的账号和密码进入系统,如果用户没有登录的账号需要先进行注册用户在注册页面输入账号和密码,点击注册,按要求输入注册表所要的信息,登录密码和确认密码,电话和qq号可填可不填,点击注册后,系统先判断用户输入的“密码”和“确认密码”是否一致,如果不一致,用户需重新输入;软件系统的优劣很大程度上是由系统设计的完善与否决定的。2. 系统的运行是否通畅进行分析:系统的每个功能都有编写数据的关系和应对的代码,通过需求分析和可行性分析进行分析和显示系统的物理数据,保证其进行通畅;减少服务器爆炸的情况。

李会计算机程序设计 614

mybatis的映射文件xml中对参数的处理--#{}

Mapple中传入参数情况: 1,单个参数,mybatis不会做特殊处理 #{参数名}:取出参数值。#{无论什么值,都可以取出} 2,多个参数,mybatis会做特殊处理 mybatis会把多个参数封装成一个Map。key:param1,param2,param3.....,获取参数的索引,固定的键名。param value:传入的参数值。 所以获取值使用:#{param1}:...

我爱吃红薯的博客 1567

代码执行现场显微镜:从源码到硬件的四层穿透观察法

编程的本质不是书写语法,而是理解代码在机器中真实发生的全过程。从高级语言源码,到编译器生成的中间表示(如Java字节码、C预处理代码),再到运行时内存布局与线程状态,最终落于CPU缓存、指令周期等硬件行为——这一‘执行链路’构成了软件工程的认知主干。掌握字节码分析、perf性能采样、jstack线程诊断等底层可观测技术,能系统性弥合‘会写’与‘懂行’之间的认知断层。本文聚焦代码执行现场的分层解构方法,特别强化对中间表示层(IR Layer)和运行时层(Runtime Layer)的实操穿透,帮助开发者摆脱黑

baochuanying9531的博客 430

MyBatis基础篇五:获取参数方式

MyBatis获取参数值的两种方式

czj66666666666的博客 487

医院LIS系统源码深度解构:从核心架构到二次开发实战

实验室信息管理系统(LIS)是医疗信息化领域的核心系统,负责检验科从申请到报告的全流程管理。其技术原理基于微服务架构,通过模块化设计解耦业务,并利用消息队列实现异步处理以保障高并发场景下的系统性能。该系统的技术价值在于将复杂的医学检验流程标准化、自动化,显著提升工作效率与数据准确性。在应用场景上,LIS不仅需要与HIS、电子病历等院内系统集成,更要稳定对接各类检验仪器,实现数据的自动采集与处理。本文聚焦于医院LIS系统源码,深入剖析其以患者与申请管理、样本流转跟踪、检验仪器数据采集、结果处理审核等为核心的功

weixin_33724659的博客 407

Java Web项目从零部署实战:Spring Boot健身系统全流程解析

在Java Web开发领域,Spring Boot框架因其简化配置和快速启动的特性,已成为构建企业级应用的主流选择。其核心原理在于通过自动配置和约定优于配置的理念,大幅降低了传统Spring项目的复杂度,使开发者能更专注于业务逻辑实现。这一技术价值体现在快速迭代和微服务架构支持上,广泛应用于电商、管理后台、物联网平台等场景。本文以健身管理系统为例,结合Spring Boot和MySQL数据库,详细拆解从环境准备、依赖管理到服务部署的完整流程,涵盖Maven构建、Docker容器化等关键环节,为开发者提供一套

weixin_33871366的博客 420

Mybatis学习笔记11 - 动态sql之trim标签

trim标签体中是整个字符串拼串后的结果。prefix="" 前缀: prefix给拼串后的整个字符串加一个前缀prefixOverrides="" 前缀覆盖: 去掉整个字符串前面多余的字符suffix="" 后缀: suffix给拼串后的整个字符串加一个后缀suffixOverrides="" 后缀覆盖:去掉整个字符串后面多余的字符 示例代码: 接口定义: package com.my...

weixin_30500473的博客 251

【附源码】计算机毕业设计java智能仓储进出货管理系统设计与实现

用户要通过填写自己的账号和密码进入系统,如果用户没有登录的账号需要先进行注册用户在注册页面输入账号和密码,点击注册,按要求输入注册表所要的信息,登录密码和确认密码,点击注册后,系统先判断员工输入的“密码”和“确认密码”是否一致,如果不一致,用户需重新输入;入库登记管理,在入库登记管理页面可以对索引、入库编号、物品编号、物品名称、物资分类、数量、仓库名称、仓库类型、登记时间等内容进行详情、修改和删除等操作,如图5-7所示。(6)易用性:各种常见的功能操作不要隐藏的太深,让用户很轻易的找到他们所需要的操作;

李会计算机程序设计 1339

(转载)EL表达式截取字符串

转载自[url]http://blog.sina.com.cn/s/blog_4903671b0100bl4l.html[/url][code="html"]uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> 上面的 uri 根据你的实际情况定。 ${fn:substring("你要截取的字符串"),beginIndex,...

蓝色的博客 516

[附源码]计算机毕业设计基于springboot学习互助辅助系统

项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:SSM + mybatis + Maven + Vue 等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。2.IDE环境:IDEA,Eclipse,Myeclipse都可以。推荐IDEA;

李会计算机程序设计 798

EDSR-PyTorch实战:从模型下载到超分辨率图像生成

本文详细介绍了如何使用PyTorch实现EDSR模型进行超分辨率图像生成,包括环境配置、模型下载、参数调优及常见问题解决方案。EDSR作为超分辨率重建的利器,通过深度残差网络显著提升图像质量,特别适合老照片修复等场景。文章还提供了视频处理和自定义训练的进阶技巧,帮助开发者快速掌握这一技术。

weixin_29046035的博客 457

jxl导入Excel 切割List 并使用MyBatis批量插入数据库

需求:从Excel导入大量客户数据。 项目环境:Spring+SpringMVC+MyBatis。 技术:使用jxl实现Excel数据导入。 方案:上传Excel文件,Controller层读取每行的内容保存到对象中,存储到List中。将保存数据的List每100个对象切割并存放到一个新的List 中的,然后使用一个大的List保存这些切割好的List组,传递到Service层遍历大List,将每...

weixin_33704591的博客 115

笔记本摄像头模拟监控推送RTSP流

FFmpeg软件:选择Windows builds by BtbN一个完整的跨平台解决方案,用于录制、转换和流式传输音频和视频。选择Windows版本EasyDarwin开源流媒体服务器 支持Windows、Linux、macOS平台;支持RTSP推流分发(推模式转发);支持RTSP拉流分发(拉模式转发);VLC Media Player软件:选择Windows64bit支持众多与及文件格式,并支持DVD影音光盘,VCD影音光盘及各类协议。

IT_LittleBaby的博客 3874

Mybatis源码分析:trim标签

trim标签使用    标签用于对标签内的sql语句进行前后缀补齐或者前后缀删除。该标签提供了四个属性,prefix,prefixOverrides,suffix,suffixOverrides。prefix,suffix用于补齐sql前后缀的值。而prefixOverrides,suffixOverrides则表示如果sql语句中前缀或者后缀的值跟两个属性中的值一致,则会将这...

weixin_30709929的博客 174

模糊控制——基本原理(附MATLAB程序)

模糊化:使用隶属函数将实际输入转换为模糊集合的隶属度值。规则库:利用“如果-那么”规则将模糊输入映射到模糊输出。推理机制:通过规则的模糊化和模糊集合的合成,生成模糊输出。去模糊化:将模糊输出转换为具体的控制信号,常用的方法包括质心法和最大隶属度法。这些步骤综合起来,实现了模糊控制系统对复杂和不确定系统的有效控制。模糊控制系统通过结合模糊逻辑和规则库来处理复杂系统中的不确定性和模糊性。它的核心在于模糊集合和隶属函数,通过推理机处理模糊规则,并最终通过去模糊化生成具体的控制信号。

qq_35623594的博客 5559

Pytorch版deeplabv3+环境配置训练自己的数据集

参考:https://blog.youkuaiyun.com/qq_36766560/article/details/110009622?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161594607816780266214828%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161594607816780266214828

Ring__Rain的博客 1万+

【附源码】计算机毕业设计java幼儿英语学习平台的设计与实现设计与实现

用户要通过填写自己的账号和密码进入系统,如果用户没有登录的账号需要先进行注册用户在注册页面输入账号和密码,点击注册,按要求输入注册表所要的信息,登录密码和确认密码,电话和qq号可填可不填,点击注册后,系统先判断用户输入的“密码”和“确认密码”是否一致,如果不一致,用户需重新输入;软件系统的优劣很大程度上是由系统设计的完善与否决定的。管理员登录幼儿英语学习平台后,可以对首页、个人中心、学生管理、教师管理、英语单词管理、英语阅读管理、系统管理等功能进行相应的操作管理,如图5-8所示。减少服务器爆炸的情况。

bishe507的博客 301

mapbox进阶,百度地图、高德地图纠偏,使用在线网络地图加载插件

本文详细介绍如何基于mapbox-gl v3.8.0版本百度地图、高德地图纠偏,使用在线网络地图加载插件,亲测可用。希望能帮助到您。一起学习,加油!加油!

我哎GIS博客 749

【课程设计】SSM校园跳蚤市场-计算机毕业设计源码39404

该平台有注册与登录界面,用户可以购买或者发布商品,为大学生提供一个便捷的二手交易平台,促进校园内的资源共享和循环利用,同时提供一个安全可靠的交易环境。围绕大学生购买设计了商品分类、商品列表、商品收藏、订单管理、评论社交等功能。该系统从实际出发,较好地满足学生对二手商品交易的需求,更高效地实现了资源循环利用。

Biye_Design的博客 2251

RT-DETR训练自己的数据集(从代码下载到实例测试)

在Windows10上配置CUDA环境教程是百度提出的一种实时目标检测模型。RT-DETR采用了与DETR相同的编码器和解码器结构,但对其进行了大量的优化,在保持较高检测精度的同时,实现了实时目标检测,为目标检测领域提供了一种新的有效解决方案,具有广泛的应用前景,如自动驾驶、智能监控、机器人等领域。以上是RT-DETR官方的论文和代码,详细介绍了RT-DETR的构成和实现。在YOLOv10中也实现了RT-DETR的代码,所以本文使用YOLOv10的项目文件进行RT-DETR的训练和测试。YOLOv10。

Limiiiing的博客 4万+

高通SDX35:SGMII接口PHY RTL8211FSI-CG驱动适配

高通SDX35 RTL8211F SGMII

不秃头的程序猿Gabriel的博客 786

SpringBoot+Vue敬老院管理系统开发实战

前后端分离架构已成为现代Web开发的主流范式,其核心思想是通过API接口实现前后端解耦。SpringBoot作为Java生态的微服务框架,结合Vue的响应式特性,能够快速构建企业级应用。在养老机构信息化场景中,这种技术组合展现出独特优势——通过MyBatis实现复杂业务查询,利用Element UI提升老年用户的可访问性体验。系统采用RBAC权限模型保障数据安全,集成FullCalendar实现可视化排班,并运用Redis缓存优化性能。本文详解从技术选型到部署落地的全流程实践,特别适合需要快速搭建养老管理系

weixin_30243533的博客 404

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

[附源码]Sprintboot计算机毕业设计学习互助辅助系统【源码+数据库+LW+部署】

这是开发系统的开始,也是相当关键的一步,如果不在这个阶段制定系统所需模块,日后会带来不必要的麻烦。管理员登录系统后,可以对首页,个人中心,学生管理,任务申请管理,任务类型管理,任务信息管理,接受任务管理,任务文件管理,课程信息管理,留言反馈,在线交流,系统管理等功能进行相应的操作管理,如图5-6所示。任务申请管理,在任务申请页面可以对索引,编号,任务标题,任务类型,任务文件,图片,价格,发布日期,学号,姓名,是否支付,审核回复,审核状态,审核等信息进行详情,任务发布和删除等操作,如图5-8所示。

海灵学姐 150

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

[附源码]JAVA+ssm计算机毕业设计房屋租赁管理系统(程序+Lw)

项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:SSM + mybatis + Maven + Vue 等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。

海彤程序设计 305

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

[附源码]计算机毕业设计springboot基于Web的软考题库平台

用户要通过填写自己的账号和密码进入系统,如果用户没有登录的账号需要先进行注册用户在注册页面输入账号和密码,点击注册,按要求输入注册表所要的信息,点击注册后,系统先判断用户输入的“密码”和“确认密码”是否一致,如果不一致,用户需重新输入;教师管理,在教师管理页面可以对索引,教师工号,教师姓名,性别,照片,职称,联系电话,教师邮箱等信息进行详情,修改和删除等操作,如图5-6所示。考试管理,在考试管理页面可以对姓名,试卷,试卷名称,分值,正确答案,考生答案,试题分析,考试时间等内容进行操作,如图5-9所示。

卓杰计算机程序设计 569
上一篇: vue|Element,前端哪些奇奇怪怪的需求——el-tree结合el-select实现通过多选框选定一级目录展示该目录下的所有子集分支
下一篇: JDK自定义的ThreadPoolExecutor类中四种线程池的分析
朝山行
博客等级 码龄7年 197粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值