Mybatis执行流程解析

发布时间:2022-09-18 22:30

目录

启动Mybatis

Resources.getResourceAsStream

SqlSessionFactoryBuilder.build

XMLConfigBuilder.parse

DefaultSqlSessionFactory.openSession

Configuration.newExecutor

DefaultSqlSession.getMapper

MapperMethod.execute

SimpleExecutor.query

获取数据库连接

预编译SQL

参数化

执行SQL处理结果集


启动Mybatis

在了解mybatis的原理之前,一定得知道它是如何运行起来的,它运行的入口在哪里?回到最原始的时期,在不使用Spring或SpringBoot的情况下,想要使用mybatis,一般都得按如下方式编写。

    public static void main(String[] args) throws Exception{
        // 读取 mybatis 的配置文件为输入流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 通过配置文件输入流创建 SqlSession工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 通过SqlSession 工厂创建一个SqlSession
        SqlSession sqlSession = factory.openSession();
        // 通过SqlSession找到对应的 mapper 接口类
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        // 执行sql
        userDao.get("1");
    }

短短五行代码就完成了mybatis的初始化和使用过程,下面就来剖析一下每行代码背后都做了什么。

Resources.getResourceAsStream

这个没什么好说的,就是将mybatis-config.xml这个文件读取成流。

SqlSessionFactoryBuilder.build

该方法就是创建了一个xml的解析器,然后将xml解析为Configuration对象后,设置到创建的DefaultSqlSessionFactory对象中,最后将DefaultSqlSessionFactory对象返回。

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }


  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 创建XMLConfigBuilder 对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      // 调用XMLConfigBuilder.parse方法,返回一个Configuration对象
      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.
      }
    }
  }

  // 将Configuration 对象设置到DefaultSqlSessionFactory对象属性中
  // 将DefaultSqlSessionFactory对象返回
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

XMLConfigBuilder.parse

解析xml文件,并将其中的配置设置到configuration对象中。具体xml中有哪些配置,可以到MYBATIS官网参考。

    // 从父类继承的
    protected final Configuration configuration;

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    // 实例化Configuration对象,并调用父类构造器设置到configuration属性中
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  // 解析xml中的节点设置到configuration属性中
  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      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);
    }
  }

DefaultSqlSessionFactory.openSession

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  public SqlSession openSession() {
    // defaultExecutorType = ExecutorType.SIMPLE;
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  // execType = ExecutorType.SIMPLE
  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);
      // 创建DefaultSqlSession对象并返回
      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();
    }
  }

  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    // 未配置则使用ManagedTransactionFactory,这个事务管理器几乎什么都不做
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    // 配置了则使用配置的,默认有JdbcTransactionFactory和ManagedTransactionFactory可配置
    return environment.getTransactionFactory();
  }

在mybatis-config.xml配置文件中,通常environments是这样配置的。这里transactionManager的类型可以是JDBC和MANAGED。


  
    
    
    
      
      
      
      
    
  

这里以JDBC为例,TranscationFactory就是JdbcTransactionFactory。方法也很简单,直接创建一个JdbcTransaction对象返回即可。

  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }

Configuration.newExecutor

  protected boolean cacheEnabled = true;

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // executorType未传则使用SIMPLE
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 根据ExecutorType来创建对应的执行器
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    // mybatis-config.xml里的settings中是否设置了cacheEnabled(true or false)
    // 未设置默认为true
    if (cacheEnabled) {
      // 代理模式
      // 创建CachingExecutor对象作为原执行器的代理对象
      executor = new CachingExecutor(executor);
    }
    // 为该执行器装载插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

DefaultSqlSession.getMapper

通过DefaultSqlSession.getMapper方法获取我们自定义的接口。

public class DefaultSqlSession {
  public  T getMapper(Class type) {
    return configuration.getMapper(type, this);
  }
}

public class Configuration {

    protected MapperRegistry mapperRegistry = new MapperRegistry(this);

    public  T getMapper(Class type, SqlSession sqlSession) {
     return mapperRegistry.getMapper(type, sqlSession);
    }
}

public class MapperRegistry {
  
  // 在解析mybatis-config.xml时,其中的mappers标签下的mapper标签经过解析之后会存在这里
  private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();

  public  T getMapper(Class type, SqlSession sqlSession) {
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    if (mapperProxyFactory == null)
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    try {
      // 通过mapperProxyFactory获取具体实例
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
}

public class MapperProxyFactory{

  protected T newInstance(MapperProxy mapperProxy) {
    // JDK动态代理
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    // 创建一个JDK的动态代理对象
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  // 对原接口的方法调用,会触发invoke方法
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // 调用的方法是继承自Object,则直接调用
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    // 从缓存中取MapperMethod对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 通过MapperMethod对象进行执行
    return mapperMethod.execute(sqlSession, args);
  }

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

}

我们都知道,接口是不能被实例化的,只能实例化它的实现类。但是接口是可以被JDK动态代理的,这里可以很明显的看到,我们的接口类被MapperProxy给代理了,所有对原始接口类的方法调用都会执行MapperProxy的invoke方法。

在invoke方法中,如果调用的是接口继承自Object的方法,那么会直接调用;否则会根据调用的方法创建一个MapperMethod对象来调用其execute方法,这个方法才是真正干活的。

MapperMethod.execute

  public MapperMethod(Class mapperInterface, Method method, Configuration config) {
    // 实例化SqlCommand对象,包括该方法对应的sql标签时什么类型等属性
    this.command = new SqlCommand(config, mapperInterface, method);
    // 实例化方法签名对象,包括方法的全部信息
    this.method = new MethodSignature(config, method);
  }


public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    if (SqlCommandType.INSERT == command.getType()) {
      // insert 语句执行insert逻辑
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
    } else if (SqlCommandType.UPDATE == command.getType()) {
      // update 语句执行update逻辑       
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
    } else if (SqlCommandType.DELETE == command.getType()) {
      // delete 语句执行delete逻辑
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    } else if (SqlCommandType.SELECT == command.getType()) {
      // select 语句执行select逻辑
      if (method.returnsVoid() && method.hasResultHandler()) {
        // 方法返回值是void,且该方法参数有ResultHandler类型
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        // 方法返回值是集合或数组
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        // 方法返回值是Map,且加有@MapKey注解
        result = executeForMap(sqlSession, args);
      } else {
        // 方法返回值是普通类型
        // 参数值
        Object param = method.convertArgsToSqlCommandParam(args);
        // command.getName: 接口的全限定类名 + '.' + 方法名
        // 如果该方法是继承来的则使用的是父类的全限定类名
        result = sqlSession.selectOne(command.getName(), param);
      }
    } else {
      throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      // sql语句执行结果为null,且方法返回值是基本类型
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

execute方法里对四种类型的SQL语句都做了相应的处理,这里仅挑选SELECT语句的返回单条结果加以分析,即selectOne的逻辑;

在执行selectOne之前,需要先将方法的执行参数进行一个解析,这样才能对应上XML中写的变量名。

    public MethodSignature(Configuration configuration, Method method) throws BindingException {
      ...
      this.hasNamedParameters = hasNamedParams(method);
      this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
    }

    // 得到参数名和参数值得绑定关系Map,仅一个有效参数时则直接返回该参数值
    public Object convertArgsToSqlCommandParam(Object[] args) {
      final int paramCount = params.size();
      if (args == null || paramCount == 0) {
        // 没有参数则返回null
        return null;
      } else if (!hasNamedParameters && paramCount == 1) {
        // 未使用@Param注解且仅一个有效参数
        return args[params.keySet().iterator().next()];
      } else {
        // key: paramName, value:参数值
        final Map param = new ParamMap();
        int i = 0;
        for (Map.Entry entry : params.entrySet()) {
          // key:paramName, value: 参数值
          // 此处存入 0, value0; 1, value1...
          param.put(entry.getValue(), args[entry.getKey()]);
          // issue #71, add param names as param1, param2...but ensure backward compatibility
          // 此处存入param1,value0; param2, value1...
          final String genericParamName = "param" + String.valueOf(i + 1);
          if (!param.containsKey(genericParamName)) {
            param.put(genericParamName, args[entry.getKey()]);
          }
          i++;
        }
        return param;
      }
    }

    // 得到 key:参数下标, value:参数名的sortedMap
    private SortedMap getParams(Method method, boolean hasNamedParameters) {
      // key: 参数在该方法的index,value:参数名
      final SortedMap params = new TreeMap();
      final Class[] argTypes = method.getParameterTypes();
      // 遍历方法参数的类型
      for (int i = 0; i < argTypes.length; i++) {
        // 跳过 RowBounds 和 ResultHandler 类型
        if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {
          // 默认参数名:第几个有效参数
          String paramName = String.valueOf(params.size());
          if (hasNamedParameters) {
            paramName = getParamNameFromAnnotation(method, i, paramName);
          }
          params.put(i, paramName);
        }
      }
      return params;
    }

    // 方法的第 i 个参数, 如果有@Param注解则取该注解的value作为paramName
    // 否则使用默认的paramName
    private String getParamNameFromAnnotation(Method method, int i, String paramName) {
      final Object[] paramAnnos = method.getParameterAnnotations()[i];
      for (Object paramAnno : paramAnnos) {
        if (paramAnno instanceof Param) {
          paramName = ((Param) paramAnno).value();
        }
      }
      return paramName;
    }

    // 方法参数中是否有@Param注解
    private boolean hasNamedParams(Method method) {
      boolean hasNamedParams = false;
      final Object[][] paramAnnos = method.getParameterAnnotations();
      for (Object[] paramAnno : paramAnnos) {
        for (Object aParamAnno : paramAnno) {
          if (aParamAnno instanceof Param) {
            hasNamedParams = true;
            break;
          }
        }
      }
      return hasNamedParams;
    } 
  

以上代码可以看到,在XML中不仅可以使用@Param的value作为变量名,还可以使用#{0},#{1} 或者 #{param1},#{param2}来作为变量名使用。

执行selectOne,实际上还是执行的selectList,只是返回第一条结果罢了;另外还做了一个参数的包装,所以当参数时集合的时候,xml中可以直接写collection或者list作为变量名;

public class DefaultSqlSession {

  public  T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    // 调用selectList,返回第一条记录
    List 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 {
      // 没有记录则返回null
      return null;
    }
  }

  public  List selectList(String statement, Object parameter) {
    // 设置 RowBounds.DEFAULT 调用selectList
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 通过configuration获取待执行的sql语句信息
      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();
    }
  }

  // 包装参数,设置默认参数名
  private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      // 参数是集合则增加设置其参数名为collection
      StrictMap map = new StrictMap();
      map.put("collection", object);
      if (object instanceof List) {
        // 如果是List则再设置一个参数名为list
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      // 参数是数组则增加设置其参数名为array
      StrictMap map = new StrictMap();
      map.put("array", object);
      return map;
    }
    // 都不是则返回原参数对象
    return object;
  }

} 
  

方法最后还是通过执行器来进行执行SQL,之前有提到openSqlSession的时候使用一个代理模式创建了CachingExector,代理对象是SimpleExecutor。

public class CachingExecutor {

  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // SQL语句
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 二级缓存key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  @Override
  public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 二级缓存
    Cache cache = ms.getCache();
    if (cache != null) { // 开启了二级缓存
      // 是否需要清除二级缓存
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        // 获取二级缓存
        @SuppressWarnings("unchecked")
        List list = (List) 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;
      }
    }
    // 未开启二级缓存则直接查询
    return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

}

SimpleExecutor.query

// SimpleExecutor的父类
public class BaseExecutor {

    protected PerpetualCache localCache;
    
    public  List 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 list;
    try {
      queryStack++;
      // 从一级缓存拿值
      list = resultHandler == null ? (List) 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  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List list;
    // 放置一个空对象到一级缓存
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 执行具体的查询逻辑, 交给子类实现
      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;
  }
}

别的逻辑都先不管,只看主要逻辑。这段的主要逻辑就是,先从一级缓存拿值,能拿到就返回拿不到就查数据库之后再放到一级缓存中;其中具体的查询逻辑doQuery方法是交给了子类实现,这里的子类是SimpleExecutor;

在看doQuery方法之前,有必要回顾一下最原始的使用JDBC来连接数据库的步骤:

    void jdbc() throws Exception{
        // 加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 获取连接
        Connection connection = DriverManager.getConnection("url", "username", "password");
        // sql语句参数化
        String sql = "select name from t_user where id = ?";
        // 预编译SQL
        PreparedStatement prepareStatement = connection.prepareStatement(sql);
        // 设置参数值
        prepareStatement.setString(1, "123");
        // 执行SQL
        prepareStatement.execute();
        ResultSet resultSet = prepareStatement.getResultSet();
        // 处理结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getString("name"));
        }
    }

以上就是JDBC执行SQL查询的过程,mybatis无非就是对这个过程进行了一系列的封装

public class SimpleExecutor {
  public  List 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);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 拿到一个数据库连接
    Connection connection = getConnection(statementLog);
    // 得到Statement 对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 参数化
    handler.parameterize(stmt);
    return stmt;
  }
}

public class Configuration {

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 实例化RoutingStatementHandler对象
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 装载拦截器
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

}

获取数据库连接

public class BaseExecutor {

  // 之前提过, 在XML中有两种参数可选 - JDBC,MANAGED
  // 这两种分别代表了使用 JdbcTransaction 和 ManageTranscation
  // 我在配置文件中使用了JDBC的方式
  protected Transaction transaction;

  protected Connection getConnection(Log statementLog) throws SQLException {
    // 通过事务获取连接
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }
}


public class JdbcTransaction {

  // 之前提过,在XML中可以选择POOLED和UNPOOLED
  // 分别对应 PooledDataSource 和 UnpooledDataSource
  // 这里以UnpooledDataSource举例, 因为比较简单
  protected DataSource dataSource;

  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

  protected void openConnection() throws SQLException {
    // 从数据源拿到连接
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }
}


public class UnpooledDataSource {

  public Connection getConnection() throws SQLException {
    return doGetConnection(username, password);
  }

  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  private Connection doGetConnection(String username, String password) throws SQLException {
    // 设置数据库连接属性
    Properties props = new Properties();
    if (driverProperties != null) {
      props.putAll(driverProperties);
    }
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    // 连接数据库获取连接
    return doGetConnection(props);
  }

  private Connection doGetConnection(Properties properties) throws SQLException {
    // 加载数据库驱动
    initializeDriver();
    // 获取连接
    Connection connection = DriverManager.getConnection(url, properties);
    configureConnection(connection);
    return connection;
  }


  private synchronized void initializeDriver() throws SQLException {
    if (!registeredDrivers.containsKey(driver)) {
      Class driverType;
      try {
        // 加载数据库驱动
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          driverType = Resources.classForName(driver);
        }
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        Driver driverInstance = (Driver)driverType.newInstance();
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }
}

至此,看到了Class.forName和DriverManager.getConnection这两个方法,说明数据库已经连接上了,下一步就是预编译SQL。

预编译SQL

public class RoutingStatementHandler {

  // 代理对象
  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 根据SQL语句定义的statementType类决定使用哪个作为代理,如果没有设置则默认是PREPARED
    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());
    }

  }

  // 调用代理对象方法
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }

  // 调用代理对象方法
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }
}
// PreparedStatementHandler父类
public class BaseStatementHandler {

   public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      // 实例化Statement对象
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      // 返回Statement对象
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}

public class PreparedStatementHandler {

  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }
}

至此,有看到了Connection.reparedstatement方法,所以预编译SQL也完成了,下一步就是参数化。

参数化

public class PreparedStatementHandler {

  // 参数处理器
  protected final ParameterHandler parameterHandler;

  // 参数化, 设置参数
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
}


public class DefaultParameterHandler{

  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List 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);
          }
        }
      }
    }
  }
}


public class BaseTypeHandler {

  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
              + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
              + "Cause: " + e, e);
      }
    } else {
      try {
        // 设置非空参数, 子类实现
        setNonNullParameter(ps, i, parameter, jdbcType);
      } catch (Exception e) {
        throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
              + "Try setting a different JdbcType for this parameter or a different configuration property. "
              + "Cause: " + e, e);
      }
    }
  }
}

// 以String为例
public class StringTypeHandler extends BaseTypeHandler {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
  }
}

抛开那些复杂的逻辑处理,看到了最后一步的PreparedStatement.setString方法,虽然过程比较曲折,有对参数进行各种判断,最终选择出一个合适的类型处理器来处理参数并设值。

执行SQL处理结果集

public class PreparedStatementHandler {

    protected final ResultSetHandler resultSetHandler;

    public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行SQL
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

}

public class DefaultResultSetHandler {

  public List handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 对ResultSetWrapper 进行一系列处理, 将结果设置到multipleResults 
    ...
    return collapseSingleResultList(multipleResults);
  }

  private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    // 获取结果集
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results. Must be no resultset
          break;
        }
      }
    }
    // 对结果集进行一次封装
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }
} 
  

preparedStatement.execute 和 preparedStatement.getResultSet 方法都已经看到,自此JDBC操作数据库的步骤,在mybatis中都完全体现了出来,mybatis无非就是封装的更加精妙。包括最后对ResultSet的处理,根据返回的结果集封装成我们需要的对象集合进行返回。


到现在为止,mybatis的执行流程,从加载配置文件到获取Mapper对象,再到最后执行SQL语句,都已经差不多都清晰了。其中还有很多细节:如何解析XML,如何对参数对象进行取值等等问题,这一篇文章肯定是没法完全讲完了,等到以后有时间有精力了再说吧......

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号