mybaits sqlSession 源碼解讀

SqlSession

org.apache.ibatis.session.SqlSession 是 mybatis 操做sql、 獲取mapper、處理事務的基本單元。通常意義上咱們運用mybatis 都是在操做 sqlSessionsql

類圖以下:apache

sqlSession

  • org.apache.ibatis.session.defaults.DefaultSqlSession 默認實現。session

    • DefaultSqlSession 的初始化過程 SqlSessionFactoryBuilder --> DefaultSqlSessionFactory--> SqlSession

SqlSession模式一

  • 全部 <T> T selectOne<E> List<E> selectList 均由<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)實現。源碼:數據結構

    [@Override](https://my.oschina.net/u/1162528)
       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();
         }
       }

    源碼分析: 該方法接受 3個參數
    - statement sql語句的標識符,就是經常使用的 namespace.sqlId組合,具備惟一性mybatis

    經過此id 能夠從mybatis配置類org.apache.ibatis.session.Configuration中獲取 映射語句對象MappedStatement, id 跟語句的映射關係儲存在 Configuration 下的內部數據結構(hashmap 字類) Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection") 中 - parameter 顧名思義 要傳遞給statement標識的sql語句的參數對象 - org.apache.ibatis.session.RowBounds 限制對象檢索的界限。比方說 上面2個參數聯合查出來200條,rowBoundsoffsetlimit 造成一個區間 [20,150],截取在此區間的數據集。就是mybatis 邏輯分頁 由 org.apache.ibatis.executor.Executor 操做返回查詢結果app

    [@Override](https://my.oschina.net/u/1162528)
          public int update(String statement, Object parameter) {
            try {
              dirty = true;
              MappedStatement ms = configuration.getMappedStatement(statement);
              return executor.update(ms, wrapCollection(parameter));
            } catch (Exception e) {
              throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
            } finally {
              ErrorContext.instance().reset();
            }
          }

SqlSession模式二

sqlSession 操做mapper 接口分析ide

  • 獲取Mapper

public <T> T getMapper(Class<T> type)的底層方法爲 Configuration.<T>getMapper(type, this),也就是說獲取Mapper接口是由配置類來完成的,具體是由其持有的 MapperRegistry 來維護。源碼分析

MapperRegistry中的成員 Map<Class<?>, MapperProxyFactory<?>> knownMappers 是Mapper 接口的註冊表,全部註冊的Mapper 將與之對應的MapperProxyFactory存儲在該註冊表中。底層是經過JDK 代理來進行代理ui

咱們的業務接口經過MapperProxy 代理,代理對象經過MapperMethod執行方法this

[@Override](https://my.oschina.net/u/1162528)
        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);
        }

流程以下:

獲取Mapper對象流程圖

  • 註冊Mapper

    在分析瞭如何獲取Mapper以後,再搞一搞如何註冊Mapper,經過上面知道Mapper的獲取是Configuration委託 MapperRegistry獲取,一樣的流程,提供了三種方式來註冊

    // 將指定包下面的指定接口進行加載
     public void addMappers(String packageName, Class<?> superType) {
         mapperRegistry.addMappers(packageName, superType);
       }
        //加載指定包下面的全部接口
       public void addMappers(String packageName) {
         mapperRegistry.addMappers(packageName);
       }
       //加載指定的接口
       public <T> void addMapper(Class<T> type) {
         mapperRegistry.addMapper(type);
       }

    核心基於MapperRegistry 下的方法:

    public <T> void addMapper(Class<T> type) {
             // 類型必須是接口
            if (type.isInterface()) {
            // 必須是未註冊的
              if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
              }
             // 加載完成標記 
              boolean loadCompleted = false;
              try {
                knownMappers.put(type, new MapperProxyFactory<T>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                //經過MapperAnnotationBuilder來對xml或者類進行解析 
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
              } finally {
                if (!loadCompleted) {
                  knownMappers.remove(type);
                }
              }
            }
          }

    批量註冊某個包下基於某個超類的 是對上面進行循環遍歷進行

    /**
            * @since 3.2.2
            */
           public void addMappers(String packageName, Class<?> superType) {
             ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
             // 判斷包下是否有該超類的實現  沒有會拋出異常  主要起到檢測加載做用
             resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
             // 獲取清單並進行註冊
             Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
             for (Class<?> mapperClass : mapperSet) {
               addMapper(mapperClass);
             }
           }

    註冊包下全部的接口

    /**
         * @since 3.2.2
         */
        public void addMappers(String packageName) {
          addMappers(packageName, Object.class);
        }

    由上面發現 重點是 MapperAnnotationBuilder,該類也十分重要的轉換類,後面會繼續分析

相關文章
相關標籤/搜索