Mybatis 框架源码解读(详细流程图+时序图)

 2023-09-15 阅读 18 评论 0

摘要:看源码都要带着问题去看,比如 UserMapper.java只有接口而没有实现类,那么是如何执行的呢?mybatis中一级缓存是如何进行缓存与维护的?底层是如何执行query查询的查询后的结果是如何处理的,为什么可以将结果集自动映射到对象中去? 让

看源码都要带着问题去看,比如

  1. UserMapper.java只有接口而没有实现类,那么是如何执行的呢?
  2. mybatis中一级缓存是如何进行缓存与维护的?
  3. 底层是如何执行query查询的
  4. 查询后的结果是如何处理的,为什么可以将结果集自动映射到对象中去?

让我们带着上面的问题来到mybatis框架底层一探究竟吧

四大组件

Java流程图、开始之前我们先了解下几个重要的组件它们的作用

我们使用的时候,都是从SqlSession中的对象开始,它是属于接口层
Mapper执行的过程是通过ExecutorStatementHandlerParameterHandlerResultHandler来完成数据库操作和结果返回的:

  • Executor

    javaweb思维导图。代表执行器,由它来调度StatementHandler、ParameterHandler、ResultHandler等来执行对应的SQL。

  • StatementHandler

    作用是使用数据库的Statement(PreparedStatement)执行操作,起到承上启下的作用。

  • 流程图怎么做?ParameterHandler

    用于SQL对参数的处理。

  • ResultHandler

    Mybatis框架?进行最后数据集(ResultSet)的封装返回处理的。

查询User记录的简单例子

public static void main(String[] args) {SqlSession sqlSession = null;try {// 获取到sqlSessiionsqlSession = SqlSessionFactoryUtil.openSqlSession();// 获取到MapperUserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 查询记录User user = userMapper.getUserById(2L);System.out.println("姓名:" + user.getName() + ",年龄:" + user.getAge());} catch (Exception e) {e.printStackTrace();} finally {if (sqlSession != null) {sqlSession.close();}}}
}

从查询一条用户记录开始为起点,进行源码分析

六大阶段

  • 阶段1:获得Mapper动态代理阶段
  • 阶段2:获得MapperMethod对象
  • 阶段3:根据SQL指令跳转执行语句
  • 阶段4:查询前的缓存处理
  • 阶段5:执行DB查询操作
  • 阶段6:针对ResultSet结果集转换为POJO

第一阶段:获取Mapper动态代理阶段

在这里插入图片描述
我们自定义的Mapper接口想要发挥功能,必须有具体的实现类,在MyBatis中是通过为Mapper每个接口提供一个动态代理类来实现的。整个过程主要有四个类:MapperRegistry、MapperProxyFactory、MapperProxy、MapperMethod

  • MapperRegistry
    是Mapper接口及其对应的代理对象工厂的注册中心
  • MapperProxyFactory
    是MapperProxy的工厂类,主要方法就是包装了Java动态代理的Proxy.newProxyInstance()方法。
  • MapperProxy
    是一个动态代理类,它实现了InvocationHandler接口。对于代理对象的调用都会被代理到InvocationHandler#invoke方法上。
  • MapperMethod
    包含了具体增删改查方法的实现逻辑。

第一阶段关键代码:

sqlSession.getMapper(UserMapper.class);

调用的是DefaultSqlSession中的getMapper()方法

public class DefaultSqlSession implements SqlSession {...// eg1: type=UserMapper.class@Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}...
}

然后进入Configuration,这个类是mybatis配置信息类,将mybatis的xml配置文件和映射mapper的xml配置文件,读入到该类中进行使用

public class Configuration {// eg1: type=UserMapper.class sqlSession=DefaultSqlSession实例public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}
}

MapperRegistry是Mapper接口及其对应的代理对象工厂的注册中心

public class MapperRegistry {// eg1: type=UserMapper.class@SuppressWarnings("unchecked")public <T> T getMapper(Class<T> type, SqlSession sqlSession) {/*** 加载mybatis-config.xml配置的<mapper>配置,根据指定type,查找对应的MapperProxyFactory对象**/// eg1: 获得UserMapper的mapperProxyFactoryfinal MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);/*** 如果没配置<mapper>,则找不到对应的MapperProxyFactory,抛出BindingException异常*/if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {/** 使用该工厂类生成MapperProxy的代理对象*/return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}
}

关键方法在:mapperProxyFactory.newInstance(sqlSession);

public class MapperProxyFactory<T> {/*** 通过动态代理,创建mapperInterface的代理类对象*/protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {mapperInterface}, mapperProxy);}public T newInstance(SqlSession sqlSession) {/*** 创建MapperProxy对象,每次调用都会创建新的MapperProxy对象,MapperProxy implements InvocationHandler*/final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}
}

接下来就是一直返回到最初的调用为UserMapper对象

总的来说就是通过预先扫描的mapper接口,然后对此接口进行JDK动态代理,然后通过代理实现sql的执行(就是invoke())

第二阶段:获取MapperMethod对象

执行如下代码的源码过程分析:

User user = userMapper.getUserById(2L);

在这里插入图片描述

    /*** 对代理类的所有方法的执行,都会进入到invoke方法中*/// eg1: UserMapper userMapper = sqlSession.getMapper(UserMapper.class);//      User user = userMapper.getUserById(2L); args = {2L}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {/** 如果被代理的方法是Object类的方法,如toString()、clone(),则不进行代理 */// eg1: method.getDeclaringClass()==interface mapper.UserMapper  由于被代理的方法是UserMapper的getUserById方法,而不是Object的方法,所以返回falseif (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}/** 如果是接口中的default方法,则调用default方法 */else if (isDefaultMethod(method)) { // eg1: 不是default方法,返回falsereturn invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)/** 初始化一个MapperMethod并放入缓存中 或者 从缓存中取出之前的MapperMethod */final MapperMethod mapperMethod = cachedMapperMethod(method);// eg1: sqlSession = DefaultSqlSession@1953  args = {2L}/** 调用MapperMethod.execute()方法执行SQL语句 */return mapperMethod.execute(sqlSession, args);}

然后我们来看下如何获取MapperMethod对象:

    private MapperMethod cachedMapperMethod(Method method) {/*** 在缓存中查找MapperMethod,若没有,则创建MapperMethod对象,并添加到methodCache集合中缓存*/// eg1: 因为methodCache为空,所以mapperMethod等于nullMapperMethod mapperMethod = methodCache.get(method);if (mapperMethod == null) {// eg1: 构建mapperMethod对象,并维护到缓存methodCache中mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());// eg1: method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)methodCache.put(method, mapperMethod);}return mapperMethod;}

主要方法在new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());,进入这个类看看构造函数:

    // 记录了SQL语句的名称和类型private final SqlCommand command;// Mapper接口中对应方法的相关信息private final MethodSignature method;// eg1: mapperInterface = interface mapper.UserMapper//      method = public abstract vo.User mapper.UserMapper.getUserById(java.lang.Long)public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {this.command = new SqlCommand(config, mapperInterface, method);this.method = new MethodSignature(config, mapperInterface, method);}

SqlCommand:主要维护两个属性

        /** MappedStatement的唯一标识id */private final String name;/** sql的命令类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH; */private final SqlCommandType type;

MethodSignature:主要用于维护方法签名,比如方法的返回值类型、入参名称等等。具体参数如下:

        private final boolean returnsMany;                  // 判断返回类型是集合或者数组吗private final boolean returnsMap;                   // 判断返回类型是Map类型吗private final boolean returnsVoid;                  // 判断返回类型是集void吗private final boolean returnsCursor;                // 判断返回类型是Cursor类型吗private final Class<?> returnType;                  // 方法返回类型private final String mapKey;                        // 获得@MapKey注解里面的value值private final Integer resultHandlerIndex;           // 入参为ResultHandler类型的下标号private final Integer rowBoundsIndex;               // 入参为RowBounds类型的下标号private final ParamNameResolver paramNameResolver;  // 入参名称解析器

至此,就获取到了一个MapperMethod对象。

大致流程如下:

  1. 如果不是Object默认方法或default类型的方法,则会从cachedMapperMethod中以method为key获取MapperMethod对象
  2. 如果没有在cache中,会new MapperMethod对象并维护到cache中
  3. new MapperMethod对象里,会维护SqlCommand和MethodSignate对象,其中
    • SqlCommand用于保存MapperStatement的唯一标识id和sql的命令类型(

      MapperStatement:用于保存映射器的一个节点(select|insert|delete|update)。包括许多我们配置的SQL、SQL的id、缓存信息、resultMap、parameterType、resultType、languageDriver等重要配置内容。

    • MethodSignature主要维护方法签名,保存方法入参、返回值类型等等

第三阶段:根据SQL指令跳转执行语句

这个阶段主要将在代理的方法invoke中的最后一句代码:

    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {.../** 调用MapperMethod.execute()方法执行SQL语句 */return mapperMethod.execute(sqlSession, args);}

在这里插入图片描述
首先我们进入到excute()方法看看:

    /*** MapperMethod采用命令模式运行,根据上下文跳转,它可能跳转到许多方法中。实际上它最后就是通过SqlSession对象去运行对象的SQL。*/// eg1: sqlSession = DefaultSqlSession@1953  args = {2L}public Object execute(SqlSession sqlSession, Object[] args) {Object result;// eg1: command.getType() = SELECTswitch (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:// eg1: method.returnsVoid() = false  method.hasResultHandler() = falseif (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) { // eg1: method.returnsMany() = falseresult = executeForMany(sqlSession, args);} else if (method.returnsMap()) { // eg1: method.returnsMap() = falseresult = executeForMap(sqlSession, args);} else if (method.returnsCursor()) { // eg1: method.returnsCursor() = falseresult = executeForCursor(sqlSession, args);} else {// eg1: args = {2L}/** 将参数转换为sql语句需要的入参 */Object param = method.convertArgsToSqlCommandParam(args);// eg1: sqlSession=DefaultSqlSession  command.getName()="mapper.UserMapper.getUserById" param={"id":2L, "param1":2L}/** 执行sql查询操作 */result = sqlSession.selectOne(command.getName(), param);}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}...return result;}

根据不同的sql查询命令,进入不同的方法。

  1. 例子中,我们执行的是SELECT,所以会进入SELECT分支,返回的结果也是根据第二阶段获取的MethodSignature进行判断,是返回多条记录、单条记录还是map、Cursor类型,这里是单条记录
  2. 根据属性名与参值的映射关系,调用methodSignature实例的getNamedParams(args)方法进行对应,比如: param={"id": 2L, "param1", 2L}
  3. 执行sql查询操作:result = sqlSession.selectOne(command.getName(), param);

在第三阶段中

  1. 通过对mapperMethod中的methodSignature实例进行选择对应执行的方法
  2. 在select分支中对参数进行转换对应,准备开始执行selectOne()操作

第四阶段:查询前的缓存处理

这个阶段主要是对上个阶段的如下代码进行解析:

      /** 执行sql查询操作 */result = sqlSession.selectOne(command.getName(), param);

在这里插入图片描述
进入selectOne()方法实际调用的是DefaultSqlSession的方法:

    // eg1: statement="mapper.UserMapper.getUserById" parameter={"id":2L, "param1":2L}@Overridepublic <T> T selectOne(String statement, Object parameter) {List<T> list = this.selectList(statement, parameter);...}// 最终调用的是以下方法// eg1: statement="mapper.UserMapper.getUserById" parameter={"id":2L, "param1":2L}@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {// eg1: statement="mapper.UserMapper.getUserById"MappedStatement ms = configuration.getMappedStatement(statement);// eg1: executor=CachingExecutor//      wrapCollection(parameter)=parameter={"id": 2L, "param1", 2L}//      rowBounds=RowBounds.DEFAULT=new RowBounds()//      Executor.NO_RESULT_HANDLER=nullreturn 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();}}
  1. 需要根据之前sqlCommand中的id(id=“mapper.UserMapper.getUserById”)获取MappedStatement
  2. 然后调用CachingExcutor(带有sqlSession级别的缓存)的query方法
    // eg1: parameterObject={"id":2L, "param1":2L}
    //      rowBounds=new RowBounds()
    //      resultHandler=null
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {// eg1: parameterObject = {"id":2L, "param1":2L}/** 获得boundSql对象,承载着sql和对应的参数*/BoundSql boundSql = ms.getBoundSql(parameterObject);// eg1: parameterObject = {"id":2L, "param1":2L}  rowBounds = new RowBounds()/** 生成缓存key */CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);// eg1: parameterObject = {"id":2L, "param1":2L}  rowBounds = new RowBounds() resultHandler = null/** 执行查询语句 */return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    • 获取BoundSql,承载着sql和对应的参数
    • 生成一、二级缓存需要的缓存key
    • 开始执行查询操作:query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)

执行查询操作:query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)

    // eg1: parameterObject = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {Cache cache = ms.getCache();// eg1: cache = null/** 如果在UserMapper.xml配置了<cache/>开启了二级缓存,则cache不为null*/if (cache != null) {/*** 如果flushCacheRequired=true并且缓存中有数据,则先清空缓存** <select id="save" parameterType="XXXXXEO" statementType="CALLABLE" flushCache="true" useCache="false">*     ……* </select>* */flushCacheIfRequired(ms);/** 如果useCache=true并且resultHandler=null*/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);/** 以cacheKey为主键,将结果维护到缓存中 */tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}// eg1: delegate = SimpleExecutor(BaseExecutor) parameterObject = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = nullreturn delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}

真正调用的是Executor的实现类BaseExecutor实例的query()方法

    // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null@SuppressWarnings("unchecked")@Overridepublic <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());// eg1: closed = falseif (closed) {throw new ExecutorException("Executor was closed.");}// eg1: queryStack = 0  ms.isFlushCacheRequired() = false/** 如果配置了flushCacheRequired=true并且queryStack=0(没有正在执行的查询操作),则会执行清空缓存操作*/if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {/** 记录正在执行查询操作的任务数*/queryStack++; // eg1: queryStack=1// eg1: resultHandler=null localCache.getObject(key)=null/** localCache维护一级缓存,试图从一级缓存中获取结果数据,如果有数据,则返回结果;如果没有数据,再执行queryFromDatabase */list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;// eg1: list = nullif (list != null) {/** 如果是执行存储过程 */handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {// eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = nulllist = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {/** 延迟加载处理 */for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();// eg1: configuration.getLocalCacheScope()=SESSION/** 如果设置了<setting name="localCacheScope" value="STATEMENT"/>,则会每次执行完清空缓存。即:使得一级缓存失效 */if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}
  1. 如果开启了缓存,则尝试从缓存中获取结果
  2. 如果缓存中没有结果,则执行查询操作
  3. 如果没有开启缓存,则直接执行查询操作,真正查询操作调用的是queryFromDatabase()

最后将查询的list结果返回,如果是selectOne(),只会list.get(0)

在第三阶段中,主要做了如下事情:

  1. 调用DefaultSession实例的selectOne(command.getName(), param);,获取在Configuration中对应的MappedStatement
  2. 在selectOne()中,会调用query()方法
    • 获得BoundSql(承载sql和参数的对象)
    • 生成一、二级缓存需要的缓存key
    • 执行查询操作(会尝试从缓存中获取)
    • 返回list.get(0)结果

第五阶段:执行DB查询操作(真正开始查询了)

在上一个阶段中,调用了执行sql的方法:

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

在这里插入图片描述

    // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = nullprivate <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds,ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;// eg1: key = -445449180:-48278933:mapper.UserMapper.getUserById:0:2147483647:select id, name, age from tb_user where id = ?:2:devlocalCache.putObject(key, EXECUTION_PLACEHOLDER);try {// eg1: SimpleExecutor.doQuery parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = nulllist = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}/** 将查询结果放到一级缓存中,如果同一session中有相同查询操作,则可以直接从缓存中获取结果*/localCache.putObject(key, list);// eg1: ms.getStatementType() = PREPAREDif (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}

主要执行语句是list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);,而真正调用的是SimpleExecutor类的doQuery()

    // eg1: parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();/** 根据Configuration来构建StatementHandler */StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,resultHandler, boundSql);// eg1: handler=RoutingStatementHandler/** 然后使用prepareStatement方法,对SQL进行预编译并设置入参 */stmt = prepareStatement(handler, ms.getStatementLog());// eg1: handler=RoutingStatementHandler parameter = {"id": 2L, "param1", 2L}  rowBounds = new RowBounds() resultHandler = null/** 开始执行真正的查询操作。将包装好的Statement通过StatementHandler来执行,并把结果传递给resultHandler */return handler.<E>query(stmt, resultHandler);} finally {closeStatement(stmt);}}

这里有三步:

  1. 根据Configuration来构建StatementHandler,并添加到拦截器链interceptorChain中

  2. 然后使用prepareStatement方法,对SQL进行预编译并设置入参

        /*** 使用prepareStatement方法,对SQL编译并设置入参** @param handler* @param statementLog* @return* @throws SQLException*/// eg1: handler=RoutingStatementHandlerprivate Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;/** 获得Connection实例 */Connection connection = getConnection(statementLog);// eg1: handler=RoutingStatementHandler/** 第一步:调用了StatementHandler的prepared进行了【sql的预编译】 */stmt = handler.prepare(connection, transaction.getTimeout());/** 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】 */handler.parameterize(stmt);// eg1: 返回org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570dedreturn stmt;}
    

    在handle.prepare()中执行编译语句,调用的是原生的JDBC进行预编译

        /*** 使用prepareStatement方法,对SQL编译并设置入参** @param handler* @param statementLog* @return* @throws SQLException*/
    // eg1: handler=RoutingStatementHandler
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;/** 获得Connection实例 */Connection connection = getConnection(statementLog);// eg1: handler=RoutingStatementHandler/** 第一步:调用了StatementHandler的prepared进行了【sql的预编译】 */stmt = handler.prepare(connection, transaction.getTimeout());/** 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】 */handler.parameterize(stmt);// eg1: 返回org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570dedreturn stmt;
    }
    
    • 第一步:调用了StatementHandler的prepared进行了【sql的预编译】

      	    // eg1: delegate=PreparedStatementHandler
      /*** 执行预编译语句*/
      @Override
      public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// eg1: 调用PreparedStatementHandler的instantiateStatementstatement = 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);}
      }```
      
    • 第二步:通过PreparedStatementHandler的parameterize来给【sql设置入参】

      // eg1: org.apache.ibatis.logging.jdbc.PreparedStatementLogger@2e570ded
      /*** 针对预处理语句,设置入参*/
      @Override
      public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());// eg1: parameterMappings[0] = ParameterMapping{property='id', mode=IN, javaType=class java.lang.Long, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {// eg1: parameterMappings.size() = 1for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);// eg1: parameterMapping.getMode() = INif (parameterMapping.getMode() != ParameterMode.OUT) {Object value;// eg1: propertyName="id"String propertyName = parameterMapping.getProperty();// eg1: boundSql.hasAdditionalParameter(propertyName) = falseif (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional paramsvalue = boundSql.getAdditionalParameter(propertyName);}// eg1: parameterObject = 2else if (parameterObject == null) {value = null;}// eg1: parameterObject.getClass() = java.lang.Long.class   返回tureelse if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {// eg1: value = parameterObject = 2Lvalue = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// eg1: typeHandler=class java.lang.LongTypeHandler typeHandler = parameterMapping.getTypeHandler();// eg1: jdbcType=nullJdbcType jdbcType = parameterMapping.getJdbcType();// eg1: value=2L jdbcType=nullif (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// eg1: typeHandler=BaseTypeHandler/** 针对预处理语句,设置入参 */typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping +". Cause: " + e, e);} catch (SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping +". Cause: " + e, e);}}}}
      }
      
    • 开始执行真正的JDBC查询操作,将包装好的Statement通过StatementHandler来执行,并把结果传递给resultHandler

          // eg1: delegate = PreparedStatementHandler  resultHandler = null
      @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {/** 最终还是使用JDBC去进行数据操作 */PreparedStatement ps = (PreparedStatement) statement;/** 执行查询操作 */ps.execute();// eg1: 封装结果集 resultSetHandler=DefaultResultSetHandler/** 将结果集进行封装 */return resultSetHandler.handleResultSets(ps);
      }
      

执行查询完成后,会将结果集进行处理封装resultSetHandler.handleResultSets(ps);,这里逻辑看第六阶段,最终返回一个list(POJO)并维护到一级缓存中,至此就完成了DB查询操作

第五阶段基本流程如下:

  1. 通过queryFromDatabase()方法执行查询操作
  2. 调用SimpleExecutor的doQuery()方法
    • 利用Configuration构建一个StatementHandler
    • 根据构建的StatementHandler执行prepare()方法对sql预编译
    • 调用DefaultParameterHandler实例的setParameters()方法对预处理的语句设置入参(也就是处理?的占位符)
    • 调用构建的StatementHandler(PreparedStatementHandler)执行查询操作ps.execute();,然后将结果集处理并返回return resultSetHandler.handleResultSets(ps);
  3. 根据获取的list集合维护到一级缓存中并返回处理后的结果POJO

第六阶段:针对ResultSet结果集转换为POJO

在上一个阶段,我们将结果集查出了,需要进行处理

     /** 将结果集进行封装 */return resultSetHandler.handleResultSets(ps);

在这里插入图片描述
我们来看下handleResultSets(Statement stmt)方法:

    /*** 处理数据库操作的结果集*/// eg1: 执行到这里@Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());final List<Object> multipleResults = new ArrayList<>();int resultSetCount = 0;/** 首先:获得执行后的结果集,并封装到ResultSetWrapper */ResultSetWrapper rsw = getFirstResultSet(stmt);/** 其次:如果rsw != null && resultMapCount < 1,则抛异常ExecutorException */List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size(); // eg1: resultMapCount = 1validateResultMapsCount(rsw, resultMapCount);// eg1: rsw不为空 resultMapCount=1 resultSetCount=0/** 第三步:处理结果集 */while (rsw != null && resultMapCount > resultSetCount) {// eg1: ResultMap resultMap=resultMaps.get(0);ResultMap resultMap = resultMaps.get(resultSetCount);/** 处理结果集, 存储在multipleResults中 */handleResultSet(rsw, resultMap, multipleResults, null);// eg1: rsw=nullrsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++; // eg1: 自增后resultSetCount=1}String[] resultSets = mappedStatement.getResultSets();// eg1: resultSets = nullif (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++;}}// eg1: multipleResults.get(0).get(0) = User{id=2, name='muse2', age=24, userContacts=null}/** 返回结果 */return collapseSingleResultList(multipleResults);}
  1. 第一步:获得执行后的结果集,并封装到ResultSetWrapper中

     private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException 					{// eg1: rs != null/** 通过JDBC获得结果集ResultSet */ResultSet rs = stmt.getResultSet();while (rs == null) {if (stmt.getMoreResults()) {rs = stmt.getResultSet();} else {/*** getUpdateCount()==-1,既不是结果集,又不是更新计数了.说明没的返回了。* 如果getUpdateCount()>=0,则说明当前指针是更新计数(0的时候有可能是DDL指令)。* 无论是返回结果集或是更新计数,那么则可能还继续有其它返回。* 只有在当前指指针getResultSet()==null && getUpdateCount()==-1才说明没有再多的返回。*/if (stmt.getUpdateCount() == -1) {// no more results. Must be no resultsetbreak;}}}// eg1: rs不为空,则将结果集封装到ResultSetWrapper中/** 将结果集ResultSet封装到ResultSetWrapper实例中 */return rs != null ? new ResultSetWrapper(rs, configuration) : null;
    }
    
  2. 第二步:从MappedStatement中获得ResultMap集合(就是列和属性的映射map)

  3. 第三步:处理结果集handleResultSet(rsw, resultMap, multipleResults, null);,最终会将结果存在multipleResults中

    // eg1: parentMapping = null
    /*** 处理结果集*/
    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults,ResultMapping parentMapping) throws SQLException {try {// eg1: parentMapping = nullif (parentMapping != null) {handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {// eg1: resultHandler = nullif (resultHandler == null) {// eg1: objectFactory = DefaultObjectFactory defaultResultHandler里面包含了一个空集合的ArrayList实例/** 初始化ResultHandler实例,用于解析查询结果并存储于该实例对象中 */DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);/** 解析行数据 */handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);multipleResults.add(defaultResultHandler.getResultList());} else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// eg1:/** 关闭ResultSet */closeResultSet(rsw.getResultSet());}
    }// eg1: parentMapping = null
    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {// eg1: resultMap.hasNestedResultMaps()=false/** 是否是聚合Nested类型的结果集 */if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {// eg1: parentMapping = nullhandleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}
    }// eg1: parentMapping = null
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap,ResultHandler<?> resultHandler, RowBounds rowBounds,ResultMapping parentMapping) throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<>();// eg1: skipRows里面没做什么事情/** 将指针移动到rowBounds.getOffset()指定的行号,即:略过(skip)offset之前的行 */skipRows(rsw.getResultSet(), rowBounds);// eg1: shouldProcessMoreRows(resultContext, rowBounds) = true    rsw.getResultSet().next() = truewhile (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {/** 解析结果集中的鉴别器<discriminate/> */ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);/** 将数据库操作结果保存到POJO并返回 */Object rowValue = getRowValue(rsw, discriminatedResultMap);// eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null}  parentMapping = null/** 存储POJO对象到DefaultResultHandler中 */storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}
    }
    

    handleRowValuesForSimpleResultMap()方法中主要做了如下处理:

    • 将数据库操作结果保存到POJO并返回
          /*** 将数据库操作结果保存到POJO并返回*/private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {final ResultLoaderMap lazyLoader = new ResultLoaderMap();/** 创建空的结果对象 */Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);// eg1: rowValue=User{id=null, name='null', age=null, userContacts=null}   hasTypeHandlerForResultObject(rsw, resultMap.getType())=falseif (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {/** 创建rowValue的metaObject */final MetaObject metaObject = configuration.newMetaObject(rowValue);// eg1: foundValues = useConstructorMappings = falseboolean foundValues = this.useConstructorMappings;// eg1: shouldApplyAutomaticMappings(resultMap, false) = true/** 是否应用自动映射 */if (shouldApplyAutomaticMappings(resultMap, false)) {// eg1: applyAutomaticMappings(rsw, resultMap, metaObject, null)=true/*** 将查询出来的值赋值给metaObject中的POJO对象*/foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; // eg1: foundValues=true}// eg1: foundValues=truefoundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;// eg1: lazyLoader.size()=0   foundValues=truefoundValues = lazyLoader.size() > 0 || foundValues;// eg1: foundValues=true  configuration.isReturnInstanceForEmptyRow()=false/** configuration.isReturnInstanceForEmptyRow() 当返回行的所有列都是空时,MyBatis默认返回null。当开启这个设置时,MyBatis会返回一个空实例。*/rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;}return rowValue; // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null}}// eg1: columnPrefix = nullprivate Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader,String columnPrefix) throws SQLException {this.useConstructorMappings = false; // reset previous mapping resultfinal List<Class<?>> constructorArgTypes = new ArrayList<>();final List<Object> constructorArgs = new ArrayList<>();/** 创建一个空的resultMap.getType()类型的实例对象 */Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);// eg1: resultObject=User{id=null, name='null', age=null, userContacts=null}//      hasTypeHandlerForResultObject(rsw, resultMap.getType()) = false/** 判断resultMap.getType()是否存在TypeHandler */if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();// eg1: propertyMappings={}for (ResultMapping propertyMapping : propertyMappings) {/** 如果是聚合查询并且配置了懒加载 */if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration,objectFactory, constructorArgTypes, constructorArgs);break;}}}// eg1: resultObject=User{id=null, name='null', age=null, userContacts=null} constructorArgTypes.isEmpty()=truethis.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); // eg1: useConstructorMappings = falsereturn resultObject; // eg1: resultObject=User{id=null, name='null', age=null, userContacts=null}}// eg1: constructorArgTypes={} constructorArgs={} columnPrefix=nullprivate Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes,List<Object> constructorArgs, String columnPrefix) throws SQLException {// eg1: resultType = vo.User.class/** 获得需要转换为POJO的类型 */final Class<?> resultType = resultMap.getType();/** 将POJO类型包装成MetaClass */final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);// eg1: constructorMappings = {}final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();// eg1: hasTypeHandlerForResultObject(rsw, resultType) = false/** 判断resultType是否存在TypeHandler */if (hasTypeHandlerForResultObject(rsw, resultType)) {/** 创建原始的ResultObject,即:通过配置好的TypeHandler来创建 */return createPrimitiveResultObject(rsw, resultMap, columnPrefix);}// eg1: constructorMappings.isEmpty()=trueelse if (!constructorMappings.isEmpty()) {/** 创建参数化的ResultObject */return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);}// eg1: resultType.isInterface()=false  metaType.hasDefaultConstructor()=trueelse if (resultType.isInterface() || metaType.hasDefaultConstructor()) {// eg1: objectFactory=DefaultObjectFactory  resultType=vo.User.class/** 使用objectFactory初始化User对象*/return objectFactory.create(resultType); // eg1: 返回User{id=null, name='null', age=null, userContacts=null}}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);}
      
      在getRowValue()方法中调用了有对自动映射进行判断(shouldApplyAutomaticMappings()),如果开启了自动映射,会将列名与属性名进行映射,然后在结果集中取出对应的列名value,然后set到对应属性名中
          // eg1: columnPrefix=nullprivate boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,String columnPrefix) throws SQLException {/** 创建自动映射 */List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;// eg1: autoMapping={UnMappedColumnAutoMapping("id", "id", LongTypeHandler@2397, false),//                   UnMappedColumnAutoMapping("name", "name", StringTypeHandler@2418, false),//                   UnMappedColumnAutoMapping("age", "age", IntegerTypeHandler@2433, false)}// 真正的赋值操作if (autoMapping.size() > 0) {for (UnMappedColumnAutoMapping mapping : autoMapping) {// eg1: mapping.column="id"      mapping.typeHandler=LongTypeHandler       value=2L// eg1: mapping.column="name"    mapping.typeHandler=StringTypeHandler     value="muse2"// eg1: mapping.column="age"     mapping.typeHandler=IntegerTypeHandler    value=24final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {// eg1: foundValues = true// eg1: foundValues = true// eg1: foundValues = truefoundValues = true;}// eg1: configuration.isCallSettersOnNulls()=false  mapping.primitive=false// eg1: configuration.isCallSettersOnNulls()=false  mapping.primitive=false// eg1: configuration.isCallSettersOnNulls()=false  mapping.primitive=falseif (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {// eg1: mapping.property="id"  value=2L// eg1: mapping.property="name"  value="muse2"// eg1: mapping.property="age"  value=24metaObject.setValue(mapping.property, value);}}}return foundValues; // eg1: 返回true}
      
  4. 存储POJO对象到DefaultResultHandler和DefaultResultContext中

    // eg1: rowValue=User{id=2, name='muse2', age=24, userContacts=null}  parentMapping = null/*** 存储POJO对象到DefaultResultHandler中*/private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {// eg1: parentMapping = nullif (parentMapping != null) {linkToParents(rs, parentMapping, rowValue);} else {// eg1: resultHandler里保存空size的ArrayList rowValue=User{id=2, name='muse2', age=24, userContacts=null}/** 将结果存储到DefaultResultHandler中 */callResultHandler(resultHandler, resultContext, rowValue);}}// eg1: resultHandler里保存空size的ArrayList rowValue=User{id=2, name='muse2', age=24, userContacts=null}/****/@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext,Object rowValue) {resultContext.nextResultObject(rowValue);// eg1: resultHandler=DefaultResultHandler((ResultHandler<Object>) resultHandler).handleResult(resultContext);}

至此,就完成最后的结果集处理操作

第六阶段主要流程如下:

  1. 调用handlerResultSet()方法处理结果集
  2. 获取到ResultSet并封装到ResultSetWrapper
  3. 然后处理结果集rsw和resultMap handleResultMap(rsw, resultMap, multipleResults, null)
    • 对行数据进行解析handlerRowValues()
      • 利用反射创建空的POJO对象
      • 将column和property进行自动映射或根据配置的ResultMap映射
      • 根据映射的集合进行遍历,从ResultSet中获取值并set到POJO对象中
      • 返回rowValue
    • 将最终的结果存储在multipleResults集合中
  4. 返回multipleResults

总结

mybatis主要可以分为四大组件Executor、StatementHandler、ParameterHandler、ResultHandler

举个例子,我们需要查询一个id为1L的用户:

SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1L); 

第一阶段:使用动态代理获取UserMapper

  1. sqlSession.getMapper(UserMapper.class)时会去Configuration下的mapperRegistry获取到对应的Mapper代理对象
  2. 里面是通过jdk动态代理来代理这个接口,最终返回的是一个代理对象

第二阶段:获取MapperMethod对象

  1. 调用getUserById(1L)时,由于调用的是UserMapper的代理对象,最终会调用invoke方法
  2. 在invoke方法中会识别是否调用的的object方法,是的话直接调用
  3. 不是则判断是否调用default方法,是的话直接调用对应方法
  4. 如果都不是则准备创建MapperMethod对象
  5. MapperMethod对象会先从缓存中获取,如果没有则取创建并维护到methodCache缓存中
  6. 其中MapperMethod主要包含了SqlCommand和MethodSignature两个对象
    1. SqlCommand保存了MapperStatement唯一标识id和sql命令类型,如select、update、delete
    2. MethodSignature保存了方法的签名和元数据信息,比如返回的类型、入参等

第三阶段:根据sql类型调用对应方法

  1. 在获取到MapperMethod对象后,开始执行查询
  2. 查询会根据sqlCommand的类型进行调用不同方法,比如select、update、delete
  3. 在select中有executorForMany、executorForMap等,这里是查询一个user对象
  4. 首先会将参数进行解析,最终格式为json格式,然后调用sqlSession.selectOne(command.getName,param)

第四阶段:查询前的缓存处理

  1. 在mybatis中有分一级缓存、二级缓存,所以在调用sqlSession.selectOne的时候会先查询
  2. 根据cacheKey,先在mapper二级缓存查询,没有则在sqlSession一级缓存中查询,如果有则从缓存中取,否则执行查询语句并维护到一二级缓存中

第五阶段:开始执行查询操作

  1. 在上一步中在一二级中都没有找到缓存,则会调用queryFromDatabase执行真正的查询操作
  2. 调用的是SimpleExecutor的doQuery()方法
    1. 利用Configuration构建一个StatementHandler(PreparedStatement)
    2. 调用StatementHandler.prepare()对sql预编译(占位符?)
    3. 然后根绝StatemnetHandler执行原生的jdbc查询,就是ps.execute();

第六阶段:将得到的结果集转为POJO

  1. execute之后调用handlerResultSet对结果集进行处理
  2. 通过xml中配置的ResultMap或自动映射规则name->属性进行映射列和属性
  3. 遍历结果集,然后通过反射构建User对象并将ResultSet中的值set到POJO对象中
  4. 最终得到一个POJO的一个集合,然后通过list.get(0)返回我们最终查询到的User对象

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/60257.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息