Mybatis的使用与工作原理

使用方法

1.添加maven依赖

<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4-snapshot</version>
</dependency>
<!--test测试引用-->
<dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>RELEASE</version>
</dependency>

2.Mybatis-config.xml文件

<properties resource="db.properties"></properties>
    <settings>
        <!-- 打印查询语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />

        <!-- 控制全局缓存(二级缓存),默认 true-->
        <setting name="cacheEnabled" value="true"/>

        <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
        <setting name="aggressiveLazyLoading" value="true"/>
        <!--  Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
        <!--<setting name="proxyFactory" value="CGLIB" />-->
        <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
        <!--
                <setting name="localCacheScope" value="STATEMENT"/>
        -->
        <setting name="localCacheScope" value="SESSION"/>
    </settings>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="BlogMapper.xml"/>
    </mappers>

3.代码

@Before
public void prepare() throws IOException {
    
    
     String resource = "mybatis-config.xml";
     InputStream inputStream = Resources.getResourceAsStream(resource);
     //1.sqlSessionFactory
     sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
 }
@Test
public void testSelect() throws IOException {
    
    
    //2-sqlSession
     SqlSession session = sqlSessionFactory.openSession(); // ExecutorType.BATCH
     try {
    
    
         //3-MapperProxy
         BlogMapper mapper = session.getMapper(BlogMapper.class);
         //4-MapperProxy.invoke();
         Blog blog = mapper.selectBlogById(1);
         System.out.println(blog);
     } finally {
    
    
         session.close();
     }
}

工作流程图

在这里插入图片描述

工作原理

接下来,我们通过流程图来一步步解析源码,了解mybatis的工作原理

1.加载配置文件生成sqlSessionFactory会话工厂

SqlSessionFactoryBuilder.java

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    
    
    try {
    
    
      // 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >>
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 解析XML,最终返回一个 DefaultSqlSessionFactory >>
      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.
      }
    }
  }

通过IO读取配置文件,然后去解析XML

XMLConfigBuilder.java

public Configuration parse() {
    
    
    if (parsed) {
    
    
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // XPathParser,dom 和 SAX 都有用到 >>
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    
    
    try {
    
    
      //issue #117 read properties first
      // 对于全局配置文件各种标签的解析
      propertiesElement(root.evalNode("properties"));
      // 解析 settings 标签
      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"));
      // settings 子标签赋值,默认值就是在这里提供的 >>
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 创建了数据源 >>
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 解析引用的Mapper映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
    
    
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

其中,最重要的是解析Mapper.xml文件的过程

XMLMapperBuilder.java

public void parse() {
    
    
    // 总体上做了两件事情,对于语句的注册和接口的注册
    if (!configuration.isResourceLoaded(resource)) {
    
    
      // 1、具体增删改查标签的解析。
      // 一个标签一个MappedStatement。 >>
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      // 2、把namespace(接口类型)和工厂类绑定起来,放到一个map。
      // 一个namespace 一个 MapperProxyFactory >>
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

第一步,先看对增删改查标签的解析

XMLMapperBuilder.java

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;
    }

    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对象,一个标签就是一个MappedStatement对象
第二步,就是对接口也就是namespace的注册

XMLMapperBuilder.java

  private void bindMapperForNamespace() {
    
    
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
    
    
      Class<?> boundType = null;
      try {
    
    
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
    
    
        //ignore, bound type is not required
      }
      if (boundType != null) {
    
    
        if (!configuration.hasMapper(boundType)) {
    
    
          // Spring may not know the real resource name so we set a flag
          // to prevent loading again this resource from the mapper interface
          // look at MapperAnnotationBuilder#loadXmlResource
          configuration.addLoadedResource("namespace:" + namespace);
          // 添加到 MapperRegistry,本质是一个 map,里面也有 Configuration >>
          configuration.addMapper(boundType);
        }
      }
    }
  }

MapperRegistry.java

  public <T> void addMapper(Class<T> type) {
    
    
    if (type.isInterface()) {
    
    
      if (hasMapper(type)) {
    
    
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
    
    
        // !Map<Class<?>, MapperProxyFactory<?>> 存放的是接口类型,和对应的工厂类的关系
        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.

        // 注册了接口之后,根据接口,开始解析所有方法上的注解,例如 @Select >>  不提倡用这种方法
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
    
    
        if (!loadCompleted) {
    
    
          knownMappers.remove(type);
        }
      }
    }
  }

实际上是把接口通过MapperRegistry容器管理,而这个容器的本质是一个Map

//存放的是接口类型,和对应的工厂类的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

最后生成一个sqlSessionFactory会话工厂

XMLConfigBuilder.java

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

2.生成sqlSession会话

DefaultSqlSessionFactory.java

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    
    
    Transaction tx = null;
    try {
    
    
      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();
    }
  }

通过事务工厂穿件事务,然后创建了执行器,也就是流程图中的Executor
最后,返回了一个DefaultSqlSession对象。注意,DefaultSqlSession不是线程安全的,所以每次连接时都要创建一个DefaultSqlSession对象

3.生成Mapper对象

MapperRegistry.java

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);
    }
  }

又回到我们之间解析namespace接口时所生成Map的对象,然后通过接口的工厂类,通过jdk动态代理生成了一个代理类,也就是说BlogMapper mapper = session.getMapper(BlogMapper.class)其实是生成了一个MapperProxy代理类

MapperProxyFactory.java

// 1
public T newInstance(SqlSession sqlSession) {
    
    
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
 // 2
protected T newInstance(MapperProxy<T> mapperProxy) {
    
    
    // 1:类加载器:2:被代理类实现的接口、3:实现了 InvocationHandler 的触发管理类
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {
    
     mapperInterface }, mapperProxy);
  }

4.执行

了解过jdk动态代理的都知道,执行对象的方法其实是执行MapperProxy的invoke()方法

MapperProxy.java

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
    try {
    
    
      // toString hashCode equals getClass等方法,无需走到执行SQL的流程
      if (Object.class.equals(method.getDeclaringClass())) {
    
    
        //代理类执行
        return method.invoke(this, args);
      } else {
    
    
        // 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke
        // 普通方法会走到 PlainMethodInvoker(内部类) 的 invoke
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
    
    
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

最后来到真正的invoke()方法

MapperProxy.java

 @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    
    
      // SQL执行的真正起点
      return mapperMethod.execute(sqlSession, args);
    }

MapperMethod.java

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);
          // 普通 select 语句的执行入口 >>
          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;
  }

我们以selectOne为例

DefaultSqlSession.java

public <T> T selectOne(String statement, Object parameter) {
    
    
    // 来到了 DefaultSqlSession
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    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方法,也就是说无论你是查询一条还是多条都是执行这个方法

DefaultSqlSession.java

 @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    
    
    try {
    
    
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 如果 cacheEnabled = true(默认),Executor会被 CachingExecutor装饰
      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();
    }
  }

拿到了我们之前封装增删查改标签的MappedStatement 对象,然后通过处理器executor去执行sql语句

CachingExecutor.java

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    
    
    // 获取SQL
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 创建CacheKey:什么样的SQL是同一条SQL? >>
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    
    
    Cache cache = ms.getCache();
    // cache 对象是在哪里创建的?  XMLMapperBuilder类 xmlconfigurationElement()
    // 由 <cache> 标签决定
    if (cache != null) {
    
    
      // flushCache="true" 清空一级二级缓存 >>
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
    
    
        ensureNoOutParams(ms, boundSql);
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
    
    
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // 写入二级缓存
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    // 走到 SimpleExecutor | ReuseExecutor | BatchExecutor
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

先去缓存拿结果,如果缓存没有,就查询数据库,并将结果写入缓存。

BaseExecutor.java

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 {
    
    
      // 三种 Executor 的区别,看doUpdate
      // 默认Simple
      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.java

 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();
      // 注意,已经来到SQL处理的关键对象 StatementHandler >>
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 获取一个 Statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行查询
      return handler.query(stmt, resultHandler);
    } finally {
    
    
      // 用完就关闭
      closeStatement(stmt);
    }
  }

Configuration.java

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 植入插件逻辑(返回代理对象)
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

最后来到了流程图最核心的一个对象StatementHandler ,通过一个路由类RoutingStatementHandler去创建PreparedStatementHandler对象

RoutingStatementHandler.java

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    
    // StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPARED
    switch (ms.getStatementType()) {
    
    
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        // 创建 StatementHandler 的时候做了什么? >>
        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());
    }

  }

因为增删改查标签中的 statementType的默认值 PREPARED,所以创建PreparedStatementHandler对象

BaseStatementHandler.java

 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;

    // 创建了四大对象的其它两大对象 >>
    // 创建这两大对象的时候分别做了什么?
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

调用PreparedStatementHandler父类的构造方法生成了ParameterHandler和ResultSetHandler对象,这样,插件能够拦截的四大对象,就都创建了,最后通过插件的包装。然后通过Statement对象去执行query方法

PreparedStatementHandler.java

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    
    
    PreparedStatement ps = (PreparedStatement) statement;
    // 到了JDBC的流程
    ps.execute();
    // 处理结果集
    return resultSetHandler.handleResultSets(ps);
  }

把Statement 对象强转成JDBC的PreparedStatement 对象,然后去执行JDBC的代码。最后用上一步创建的resultSetHandler处理结果集

总结

  1. 配置文件,最核心的就是创建了Configuration对象,然后把每一个sql语句变成了一个MappedStatement对象,把namespace接口跟一个MapperProxyFactory类映射起来。
  2. 通过openSession拿到一个会话,通过它获取一个经过插件包装的SimpleExecutor执行器,并创建了事务,最后返回一个DefaultSqlSession
  3. 去MapperRegistry容器中拿到MapperProxyFactory工厂类,然后通过jdk动态代理获得一个MapperProxy动态代理类,并返回。
  4. 执行代理类的invoke()方法,然后执行器BaseExecutor拿到sql语句,创建ParameterHandler和ResultSetHandler对象,在创建一个Connection连接,然后用JDBC的PreparedStatement 去执行JDBC的查询,最后返回一个结果集。

猜你喜欢

转载自blog.csdn.net/xzw12138/article/details/106299527