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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
    <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


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » mybatis一个对象查询流程简单分析(集成spring boot)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏