MyBatis(四) sql执行流程

来源:互联网 发布:android自带数据库 编辑:程序博客网 时间:2024/05/16 06:48

1.MyBatis(三) xml文件解析流程 动态SQL解析,中介绍了MyBatis SQL的解析过程。那么MyBatis执行流程中就涉及到具体sql生成的过程了。首先看下SqlSession的获取

MyBatis源码分析时序图.png
1.1 首先调用SqlSessionFactoryBuilder的build方法来初始化加载配置文件这里前面已经分析过了,此处给返回SqlSessionFactory对象

sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

1.2 通过DefaultSqlSessionFactory的openSession方法,实际调用的是它的openSessionFromDataSource方法,这里返回SqlSession ,具体来说是返回的是SqlSession 的实现类DefaultSqlSession

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, autoCommit);      return new DefaultSqlSession(configuration, executor);    } 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();    }  }

2.SqlSession与SqlSessionFactorySqlSession通过SqlSessionFactory的opensession方法返回的

  private final static SqlSessionFactory sqlSessionFactory;    static {        String resource = "mybatisConfig.xml";        Reader reader = null;        try {            reader = Resources.getResourceAsReader(resource);        } catch (IOException e) {            System.out.println(e.getMessage());        }        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);    }
 SqlSession sqlSession = sqlSessionFactory.openSession();VmanBomDao vmanBomDao = sqlSession.getMapper(VmanBomDao.class);int i = vmanBomDao.insertVmanBomBean(vmanBomBean);

当我们得到SqlSession通过getMapper方法来获取dao最后通过dao来执行相应的数据库代码,其实这里获取是Mapper也就是dao的代理类对象,具体流程如下:

MyBatis源码分析时序图2.png
2.1 首先通过SqlSession这个具体实现类是DefaultSqlSession的getMapper来获取代理对象,

  public <T> T getMapper(Class<T> type) {    return configuration.<T>getMapper(type, this);  }

2.2 然而DefaultSqlSession什么都不做,将mapper的获取交给了Configuration了。Configuration的getMapper方法

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

2.3 接着又被转交给MapperRegistry对象的getMapper方法

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

2.4 MapperProxyFactory的newInstance方法,我们可以看出这里使用了动态代理机制,而代理类就是MapperProxy

 protected T newInstance(MapperProxy<T> mapperProxy) {    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);  }  public T newInstance(SqlSession sqlSession) {    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);    return newInstance(mapperProxy);  }

3 MapperProxy
3.1 sql真正调用开始的地方,MapperProxy的invoke方法最终会被代理对象调用

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//因为所有的类都继承自Object对象,如果没有此处的代码则tostring等方法调用都只会返回null    if (Object.class.equals(method.getDeclaringClass())) {      return method.invoke(this, args);    }//处理Mapper方法    final MapperMethod mapperMethod = cachedMapperMethod(method);    return mapperMethod.execute(sqlSession, args);  }

3.2 MapperMethod 处理mapper接口的方法,调用 mapperMethod.execute(sqlSession, args);此处根据不同的sql语句类型来处理调用sqlSession对应的方法,而这个SqlCommandType从哪里来,答案就在前面的xml解析中的MappedStatement对象,而MappedStatement又保存在Configuration中,前面说过Configuration是MyBatis这个调度配置中心,详情可以MapperMethod构造方法中查看SqlCommand对象。这里就不展开

public Object execute(SqlSession sqlSession, Object[] args) {    Object result;    if (SqlCommandType.INSERT == command.getType()) {      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.insert(command.getName(), param));    } else if (SqlCommandType.UPDATE == command.getType()) {      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.update(command.getName(), param));    } else if (SqlCommandType.DELETE == command.getType()) {      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.delete(command.getName(), param));    } else if (SqlCommandType.SELECT == command.getType()) {      if (method.returnsVoid() && method.hasResultHandler()) {        executeWithResultHandler(sqlSession, args);        result = null;      } else if (method.returnsMany()) {        result = executeForMany(sqlSession, args);      } else if (method.returnsMap()) {        result = executeForMap(sqlSession, args);      } else {        Object param = method.convertArgsToSqlCommandParam(args);        result = sqlSession.selectOne(command.getName(), param);      }    } else {      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;  }

4.回到SqlSession的实现类DefaultSqlSession,只分析一个方法其他的都类似,MyBatis(三) xml文件解析流程 动态SQL解析文中保存了各个SqlNode都在等待着MixedSqlNode的apply方法。

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);      return result;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }

4.1 Executor: SQL的执行器。
Executor.png

1.BaseExecutor: 作为SQL的执行器基类
2.ReuseExecutor:继承自BaseExcutor,ReuseExecutor顾 名思义就是重复使用执行,其定义了一个Map,将执行的sql作为key,将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了。
3. BatchExecutor:批量处理器
4. SimpleExecutor:简单处理器,也是默认的sql执行器
5. CachingExecutor: 这个执行器跟二级缓存有关,当开启二级缓存是就会使用这个SQL执行器。

4.2 SimpleExecutor默认sql执行器的创建在Configuration中的newExecutor方法中

public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;    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);    }    if (cacheEnabled) {      executor = new CachingExecutor(executor, autoCommit);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }

4.3 SimpleExecutor默认sql执行器的query方法, 此方法来自他的基类BaseExecutorSimpleExecutor并没有重写该方法,但是此处执行BaseExecutor此方法的意义就是sql的生成。其中BoundSql 类中包含了sql

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    BoundSql boundSql = ms.getBoundSql(parameter);    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);    return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());    if (closed) throw new ExecutorException("Executor was closed.");    if (queryStack == 0 && ms.isFlushCacheRequired()) {      clearLocalCache();    }    List<E> list;    try {      queryStack++;//进行查询是 localCache一级缓存中获取      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;      if (list != null) {        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);      } else {//如果一级缓存中为null则执行查询数据库        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);      }    } finally {      queryStack--;    }    if (queryStack == 0) {      for (DeferredLoad deferredLoad : deferredLoads) {        deferredLoad.load();      }      deferredLoads.clear(); // issue #601      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {        clearLocalCache(); // issue #482      }    }    return list;  }

这里稍微提下Mybatis一级缓存,上面方法中用到了一级缓存,后面会说到。
4.3.1 BoundSql

public class BoundSql {  private String sql;  private List<ParameterMapping> parameterMappings;  private Object parameterObject;  private Map<String, Object> additionalParameters;  private MetaObject metaParameters;}

4.3.2 BoundSql 通过SqlSource(实现类DynamicSqlSource)的getBoundSql方法获取,取得了BoundSql也就取得了动态生成后的sql语句就可以交给Executor执行了。

public BoundSql getBoundSql(Object parameterObject) {    DynamicContext context = new DynamicContext(configuration, parameterObject);//这里的rootSqlNode就是前文中的MixedSqlNode    rootSqlNode.apply(context);    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());    }    return boundSql;  }

4.3.3
XMLScriptBuilder中的此方法中就已经保存到了 SqlSource MixedSqlNode 会根据 SqlNode的不同执行不同类别的sqlNode的apply方法正真根据参数生成可执行sql语句

  public SqlSource parseScriptNode() {    List<SqlNode> contents = parseDynamicTags(context);    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);    SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode);    return sqlSource;  }

4.4 BaseExecutor的 queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

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

4.5 doquery方法,BaseExecutor的doquery为抽象方法,所以具体实现在它的非抽象子类SimpleExecutor默认sql执行器中

 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {//MappedStatement 来自Configuration,同时MappedStatement 中也包含Configuration,通过MappedStatement 来获取Configuration对象      Configuration configuration = ms.getConfiguration();//StatementHandler 获取      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.<E>query(stmt, resultHandler);    } finally {      closeStatement(stmt);    }  }

5 . StatementHandler 介绍

StatementHandler.png

> 1.**SimpleStatementHandler:** 其实就是对jdbc **Statement**的封装2.**PreparedStatementHandler:** 其实就是对jdbc **PreparedStatement**的封装,对sql进行预编译,防止sql注入3.**CallableStatementHandler:** 其实就是对jdbc 的**CallableStatement**的封装,对调用存储过程时使用4 . **RoutingStatementHandler:**作为其他Statement的调度中心5.1**MappedStatement**中默认设置StatementType为PREPARED

  mappedStatement.statementType = StatementType.PREPARED;

5.2 当追踪查询语句执行时返现MappedStatement的创建在 Configuration中的newStatementHandler方法中调用 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());    }  }

5.3 sql执行StatementHandler的query方法,以 PreparedStatementHandler为例,这里是不是很熟悉,这不就是jdbc的操作嘛

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.execute();    return resultSetHandler.<E> handleResultSets(ps);  }
原创粉丝点击