mybatis一个对象查询流程简单分析(集成spring boot)

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
首先写一个单测,然后跟随断点看
    @Test
    public void getMaxUpdateTimeByPositions(){
        assert  appBlockDao.getMaxUpdateTimeByPositions("mine")!=null;
    }

跟着断点进来,发现进入了org.apache.ibatis.binding.MapperProxy动态代理类,执行invoke方法

//获取一个MapperMethod
 MapperMethod mapperMethod = this.cachedMapperMethod(method);
//接着执行execute方法,传入一个sqlSession,与被代理方法的入参
 return mapperMethod.execute(this.sqlSession, args);
/**
     * cachedMapperMethod会从methodCache中去获取MapperMethod,获取不到则new一个
     * 1、入参为当前被调用接口 private final Class<T> mapperInterface;
     * 2、method
     * 3、调用sqlSession.getConfiguration()接口,获取sqlSession中的的Configuration对象,由于 
     * sqlSession都是从sqlSessionFactory中获取的,所以实际上调用的是 
     * org.apache.ibatis.session.defaults.DefaultSqlSessionFactory的Configuration对象
     * 
 */
 private MapperMethod cachedMapperMethod(Method method) {
    //private final Map<Method, MapperMethod> methodCache;
    MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
    if (mapperMethod == null) {
         mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
          this.methodCache.put(method, mapperMethod);
      }
      return mapperMethod;
    }

初始化MapperMethod =>org.apache.ibatis.binding.MapperMethod下的SqlCommand

private final MapperMethod.SqlCommand command;
private final MapperMethod.MethodSignature method;
//分别会初始化内部类SqlCommand、MethodSignature
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
   this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
   this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
//SqlCommand的实例化,篇幅有限,这里只暂时本次被代理接口执行的重要代码
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
    String statementName = mapperInterface.getName() + "." + method.getName();
    MappedStatement ms = null;
    //hasStatement方法主要是调用protected final Map<String, MappedStatement> 
    //mappedStatements,判断是否存在statementName,mappedStatements主要维护的是statementName与 
    //xml配置文件sql块的关系
    if (configuration.hasStatement(statementName)) {
        ms = configuration.getMappedStatement(statementName);
    }
    //获取获取不到ms,会报这个熟悉的Invalid bound statement异常
    if(ms==null){
       if (method.getAnnotation(Flush.class) == null) {
          throw new BindingException("Invalid bound statement (not found): "                 statementName);
     }
    } 
...
     //获取到ms后,给name复制未ms的id,这里的id=我们dao接口的全类名.方法名
     this.name = ms.getId();
     //SqlCommandType(type=SELECT)
     this.type = ms.getSqlCommandType();
   }

初始化MapperMethod =>org.apache.ibatis.binding.MapperMethod下的MethodSignature,主要是为MapperMethod中的一些参数赋值,为后续的流程使用,如returnType、入参params,判断是selectOne/List等

================================================================================================

到了这里,MapperMethod也就成功获取到了,开始执行execute部分,select请求会进入SqlCommandType.SELECT == this.command.getType()部分

 mapperMethod.execute(this.sqlSession, args);
==================================================================================
 public Object execute(SqlSession sqlSession, Object[] args) {
        Object param;
        Object result;
        if (SqlCommandType.INSERT == this.command.getType()) {
           ...
        } else if (SqlCommandType.UPDATE == this.command.getType()) {
           ...
        } else if (SqlCommandType.DELETE == this.command.getType()) {
          ...
        //获取刚才为SqlCommand中SqlCommandType赋值的type=SELECT
        } else if (SqlCommandType.SELECT == this.command.getType()) {
            if .//这里获取list、map等方法..{
           else {
                //param是一个map,存放了参数名与value
                param = this.method.convertArgsToSqlCommandParam(args);
               ////由于我们的接口是返回Date类型,所以会执行SelectOne方法
                result = sqlSession.selectOne(this.command.getName(), param);
            }
        } else {
            ...
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

sqlSession.selectOne首先经过=>

  org.mybatis.spring.SqlSessionTemplate的内部类SqlSessionInterceptor的代理

  通过SqlSessionUtils.getSqlSession获取SqlSession,获取过程=>

  1、从Spring中TransactionSynchronizationManager中获取一个sqlSessionHolder

  2、接着从sqlSessionHolder获取sqlSession,如果获取不到,那么就从SqlSessionFactory#openSession获取一个sqlSession,再调用SqlSessionUtils#registerSessionHolder注册到TransactionSynchronizationManager中

然后进入SqlSession的实际实现类:org.apache.ibatis.session.defaults.DefaultSqlSession,调用实际的selectList方法

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
           //以statement为key,获取MappedStatement,与前面讲过的过程一致
            MappedStatement ms = this.configuration.getMappedStatement(statement);
           //执行CachingExecutor的query方法 
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }

        return var5;
}
========================================================================================
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
       //dao的sql语句信息,包括sql,parameterObject、metaParameters
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        //获取一级缓存用的CacheKey,在BaseExecutor中创建、获取
        CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

然后会执行自身的另外一个query方法,首先判断MappedStatement中是否存在缓存Cache

如果不存在,那么会执行org.apache.ibatis.executor.BaseExecutor的query方法

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//内部使用一个ThreadLocal<ErrorContext>,设置resource对象等等
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (this.closed) {
            throw new ExecutorException("Executor was closed.");
        } else {
            if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
                this.clearLocalCache();
            }

            List list;
            try {
                ++this.queryStack;
               //判断是否从localCache中获取resultList
                list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
                if (list != null) {
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
                    //判断到为空后,就开始从数据库的查询
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

            if (this.queryStack == 0) {
                Iterator i$ = this.deferredLoads.iterator();

                while(i$.hasNext()) {
                    BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                    deferredLoad.load();
                }

                this.deferredLoads.clear();
                if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                    this.clearLocalCache();
                }
            }

            return list;
        }
    }

queryFromDatabase,会调用BaseExecutor子类org.apache.ibatis.executor.SimpleExecutor中的doQuery方法

1、从Configuration中获取StatementHandler,调用Configuration#newStatementHandler方法,初始化org.apache.ibatis.executor.statement.RoutingStatementHandler,根据mapperStatement中的statementType判断需要返回SimpleStatementHandler/PreparedStatementHandler/CallableStatementHandler对象

2、接着执行org.apache.ibatis.executor.SimpleExecutor中的prepareStatement方法,在这里获取jdbc ConnectionStatement等,从而建立数据库链接,然后调用StatementHandler的prepare方法,获取Statement,调用org.apache.ibatis.executor.statement.BaseStatementHandler#instantiateStatement()方法,调用Connection.prepareStatement方法,

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Connection connection = this.getConnection(statementLog);
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
}

3、然后调用org.apache.ibatis.executor.statement.PreparedStatementHandler#parameterize()方法,设置占位符参数PreparedStatement

4、调用ResultSetHandler#handleResultSets方法,然后结果集合ResultSet

5org.apache.ibatis.executor.SimpleExecutor#closeStatement()方法,关闭org.apache.ibatis.executor.BaseExecutor#state方法,接着移除localCache,再把查询到的结果list和key再放入localCache中

动态代理=》SqlSession=>MapperStatement=》Executor=>Handler

                           
 

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26093341/article/details/83995645
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞