天天看点

《深入理解Mybatis原理》 03-Mybatis SQL执行流程

    本章将以简单SQL查询为例,在前两章初始化基础上讲解Mybatis运行时流程。然后会针对Mybatis运行期核心实现进行详细阐述

    本章以下面的简单查询案例进行分析:

public static void main(String[] args) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.selectByPrimaryKey(1);
        System.out.println(user.getUserName());
    }
           

Mybatis SQL执行流程

《深入理解Mybatis原理》 03-Mybatis SQL执行流程

 Mybatis执行SQL完整流程如下:

  1.   初始化Mybatis,获得SqlSession对象
  2.  通过SqlSession获得 UserMapper接口 代理类
  3.  执行UserMapper中查询方法 selectByPrimaryKey方法
  4.  返回查询结果

Step1: 初始化Mybatis,获得SqlSession对象

    关于Mybatis初始化流程,笔者已经在《深入理解Mybatis原理》一栏中的前两章详细讲解,感兴趣的同学可以跳转阅读。

    《深入理解Mybatis原理》 01-Mybatis初始化机制、《深入理解Mybatis原理》 02-Mybatis数据源与连接池

Step2: 通过SqlSession获得 UserMapper接口 代理类

                 UserMapper mapper = sqlSession.getMapper(UserMapper.class);

《深入理解Mybatis原理》 03-Mybatis SQL执行流程

    从sqlSession.getMapper(UserMapper.class)入口方法进入,首先会检查MapperRegister中有没有UserMapper代理类,若没有则进行创建:

//1. 首先会创建一个MapperProxy<UserMapper>对象
public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

//MappperProxy类(省略部分代理)
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @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);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

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


//2.通过JDK动态代理返回 UserMapper 代理类
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }
           

Step3: 执行UserMapper中查询方法 selectByPrimaryKey方法

 由于Step2中 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);返回的是一个代理类。

 所以 User user = mapper.selectByPrimaryKey(1); 查询语句会执行 代理类的 invoke方法。

 在invoke方法中,会首先从methodCache中获取查询语句方法。若没有则会为此次method创建MapperMethod。

 并将新创建的MapperMethod缓存到mrthodCache中

//MapperProxy  invoke代理方法
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //判断method所在类是否为Java Object类
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {//判断methos是的为所在类中的默认方法
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

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

    接下来会调用MapperMethod的execute(sqlSession,args)方法准备底层sql查询。

    根据 select/update/delete/insert不同类型sql到相应的sqlSession中执行相应的 select/update/delete/insert方法

//根据 sql类型,执行XXXSqlSession中相应的执行方法
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        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;
  }
           

接下来SqlSession会调用XXXExecutor进入真正sql查询的开始阶段

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

   CachingExecutor、BaseExecutor

以DefultSqlSession为例:sqlSession首先会调用 CachingExecutor 进行sql方法执行

为什么会首先调用CachingExecutor 呢?

因为Mybatis支持缓存机制,所以会首先调用 CachingExecutor判断将要执行的sql所在的mapper中是否配置缓存(cache节点),若缓存可以命中则直接返回结果

若没有配置cache缓存则调用 BaseExecutor 进一步查询。(BaseExecutor中有localCach对查询进行缓存,commit之前此缓存是有效的)

 BaseExceutor最终会调用 SimpleExecutor (以简单查询为例)进行底层原生JDBC 查询并返回查询结果
           
@Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

 @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);
  }
           

以上就是本文 《深入理解Mybatis原理》 03-Mybatis SQL执行流程 的全部内容,

上述内容如有不妥之处,还请读者指出,共同探讨,共同进步!

@author : [email protected]

继续阅读