TABLE OF CONTENTS Mybatis包结构 Mybatis使用流程 一些关键逻辑和亮点 通过SqlSessionFactory工厂获取SqlSession 加载Mybatis配置文件 配置项的默认值 SqlSession接口 获取Mapper代理类 执行Mapper代理方法 SqlSession, SqlSessionFactory, SqlSessionFactoryBuilder的关系 SqlSession的使用 SqlSession的执行原理 Excutor 总结 Mybatis包结构 
Mybatis使用流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Slf4j public  class  MyBatisBootStrap  {    @Test      public  void  test ()  {         try  {                          InputStream  inputStream  =  Resources.getResourceAsStream("mybatis-config.xml" );                          SqlSessionFactory  sqlSessionFactory  =  new  SqlSessionFactoryBuilder ().build(inputStream);                          SqlSession  sqlSession  =  sqlSessionFactory.openSession();                          CategoryMapper  categoryMapper  =  sqlSession.getMapper(CategoryMapper.class);                          Category  category  =  categoryMapper.queryCategoryByCategoryId(1 );             log.info("category = "  + JsonUtil.toJson(category));                          sqlSession.commit();                          sqlSession.close();             inputStream.close();         } catch  (Exception e) {             log.error(e.getMessage(), e);         }     } } 
如果对于Mybatis启动不清晰,可以通过上面这段代码找到入口,阅读源码。
一些关键逻辑和亮点 通过SqlSessionFactory工厂获取SqlSession 工厂方法模式+建造者设计模式的应用
SqlSessionFactory通过工厂方法openSession创建SqlSession实例,具体的工厂类实现了工厂接口,负责创建特定类型的对象,使SqlSession的实例化延迟到SqlSessionFactory子类。客户端通过SqlSessionFactory接口和工厂子类来获取SqlSession,而不需要知道具体的对象创建过程。而工厂本身是一个复杂的对象,因此可以通过建造者模式SqlSessionFactoryBuilder来构建。
源码位置:org.apache.ibatis.session.SqlSessionFactory
两个子类:org.apache.ibatis.session.defaults.DefaultSqlSessionFactoryorg.apache.ibatis.session.SqlSessionManager
建造者类SqlSessionFactoryBuilderorg.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
加载Mybatis配置文件 1 2 SqlSessionFactory  sqlSessionFactory  =  new  SqlSessionFactoryBuilder ().build(inputStream);
上面这段代码,XMLConfigBuilder会解析inputStream,根据得到Configuration默认实例化一个工厂子类DefaultSqlSessionFactory。
源码位置:org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private  void  parseConfiguration (XNode root)  {   try  {            propertiesElement(root.evalNode("properties" ));      Properties  settings  =  settingsAsProperties(root.evalNode("settings" ));      loadCustomVfs(settings);      loadCustomLogImpl(settings);      typeAliasesElement(root.evalNode("typeAliases" ));      pluginElement(root.evalNode("plugins" ));      objectFactoryElement(root.evalNode("objectFactory" ));      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory" ));      reflectorFactoryElement(root.evalNode("reflectorFactory" ));      settingsElement(settings);            environmentsElement(root.evalNode("environments" ));      databaseIdProviderElement(root.evalNode("databaseIdProvider" ));      typeHandlerElement(root.evalNode("typeHandlers" ));      mapperElement(root.evalNode("mappers" ));    } catch  (Exception e) {      throw  new  BuilderException ("Error parsing SQL Mapper Configuration. Cause: "  + e, e);    }  } 
配置项的默认值 org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement
SqlSession接口 sqlSession是操作数据库的高级接口,获取sqlSession有两种方式:
SqlSession接口源码,下面是其主要方法,已过滤一些重载方法。org.apache.ibatis.session.SqlSession
statement:Mybatis语句ID,Mapper接口的方法名称 
RowBounds:分页对象,主要拼接sql中的start,limit条件。 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public  interface  SqlSession  extends  Closeable  {  <T> T selectOne (String statement, Object parameter) ;   <E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds) ;   <K, V> Map<K, V> selectMap (String statement, Object parameter, String mapKey, RowBounds rowBounds) ;   <T> Cursor<T> selectCursor (String statement, Object parameter, RowBounds rowBounds) ;   void  select (String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) ;   int  insert (String statement, Object parameter) ;   int  update (String statement, Object parameter) ;   int  delete (String statement, Object parameter) ;   void  commit (boolean  force) ;   void  rollback (boolean  force) ;   List<BatchResult> flushStatements () ;   @Override    void  close () ;   void  clearCache () ;   Configuration getConfiguration () ;   <T> T getMapper (Class<T> type) ;   Connection getConnection () ; } 
获取sqlSession源码位置:org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSourceorg.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromConnection
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39  private  SqlSession openSessionFromDataSource (ExecutorType execType, TransactionIsolationLevel level, boolean  autoCommit)  {     Transaction  tx  =  null ;     try  {       final  Environment  environment  =  configuration.getEnvironment();       final  TransactionFactory  transactionFactory  =  getTransactionFactoryFromEnvironment(environment);       tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);       final  Executor  executor  =  configuration.newExecutor(tx, execType);       return  new  DefaultSqlSession (configuration, executor, autoCommit);     } catch  (Exception e) {       closeTransaction(tx);        throw  ExceptionFactory.wrapException("Error opening session.  Cause: "  + e, e);     } finally  {       ErrorContext.instance().reset();     }   }   private  SqlSession openSessionFromConnection (ExecutorType execType, Connection connection)  {     try  {       boolean  autoCommit;       try  {         autoCommit = connection.getAutoCommit();       } catch  (SQLException e) {                           autoCommit = true ;       }       final  Environment  environment  =  configuration.getEnvironment();       final  TransactionFactory  transactionFactory  =  getTransactionFactoryFromEnvironment(environment);       final  Transaction  tx  =  transactionFactory.newTransaction(connection);       final  Executor  executor  =  configuration.newExecutor(tx, execType);       return  new  DefaultSqlSession (configuration, executor, autoCommit);     } catch  (Exception e) {       throw  ExceptionFactory.wrapException("Error opening session.  Cause: "  + e, e);     } finally  {       ErrorContext.instance().reset();     }   } 
获取Mapper代理类 1 2 CategoryMapper  categoryMapper  =  sqlSession.getMapper(CategoryMapper.class);
因为默认的SqlSession实现是DefaultSqlSession,进入源码可以得知,CategoryMapper是通过代理工厂MapperProxyFactory反射得到Mapper代理类。这段代码逻辑可以通过顺藤摸瓜,找到Mapper代理是通过Map<Class<?>, MapperProxyFactory<?>> knownMappers获取的。
源码入口:org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
1 2 3 public  <T> T getMapper (Class<T> type)  {    return  this .configuration.getMapper(type, this ); } 
org.apache.ibatis.session.Configuration#getMapper
1 2 3 4 public  <T> T getMapper (Class<T> type, SqlSession sqlSession)  {    return  this .mapperRegistry.getMapper(type, sqlSession); } 
org.apache.ibatis.binding.MapperRegistry#getMapper找到这段代码可以得知:
1 2 3 4 5 6 7 8 9 10 11 public  <T> T getMapper (Class<T> type, SqlSession sqlSession)  {  final  MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);   if  (mapperProxyFactory == null ) {     throw  new  BindingException ("Type "  + type + " is not known to the MapperRegistry." );   }   try  {     return  mapperProxyFactory.newInstance(sqlSession);   } catch  (Exception e) {     throw  new  BindingException ("Error getting mapper instance. Cause: "  + e, e);   } } 
knownMappers是如何初始化?
org.apache.ibatis.binding.MapperRegistry#addMapper ==> org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
可以发现是在XMLConfigBuilder解析mybatis-config.xml时通过parseConfiguration方法进行的。仔细观察下图,还提供了一个Spring下的FacotryBean实现进行配置。
执行Mapper代理方法 从上面分析得知,获取得到的Mapper是通过MapperProxyFactory工厂类创建的MapperProxy代理类。因此,在执行Mapper接口的方法时,会调用MapperProxy的invoke方法。
源码入口:org.apache.ibatis.binding.MapperProxy#invoke
cachedInvoker(method)返回MapperMethodInvoker对象,执行invoke方法(具体通过哪个实现可以实际debug,这里是MapperMethodInvoker的子类PlainMethodInvoker的invoke方法)org.apache.ibatis.binding.MapperMethod#execute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 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);         if  (method.returnsOptional()             && (result == null  || !method.getReturnType().equals(result.getClass()))) {           result = Optional.ofNullable(result);         }       }       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, SqlSessionFactory, SqlSessionFactoryBuilder的关系 SqlSession的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  void  testSelectOne ()  {    try  {                  InputStream  inputStream  =  Resources.getResourceAsStream("mybatis-config.xml" );                  SqlSessionFactory  sqlSessionFactory  =  new  SqlSessionFactoryBuilder ().build(inputStream);                  SqlSession  sqlSession  =  sqlSessionFactory.openSession();                  Category  category  =  sqlSession.selectOne("moe.tsukasa.demo.dao.CategoryMapper.queryCategoryByCategoryId" , 1 );         log.info("category = [{}]" , JsonUtil.toJson(category));         Category  category2  =  sqlSession.selectOne("queryCategoryByCategoryId" , 1 );         log.info("category2 = [{}]" , JsonUtil.toJson(category2));                  sqlSession.close();         inputStream.close();     } catch  (Exception e){         log.error("errMsg = [{}]" , e.getMessage(), e);     } } 
SqlSession的执行原理 Excutor Excutor是MyBatis持久层框架中的一个关键组件,负责SQL语句的执行,缓存的处理,事务管理,参数处理。
Executor接口有两个实现,一个是BaseExecutor抽象类,一个是CachingExecutor实现类。BaseExecutor抽象类有四个实现SimpleExecutor,BathExecutor, ReuseExecutor, ClosedExecutor。
Executor继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public  interface  Executor  {  ResultHandler  NO_RESULT_HANDLER  =  null ;   int  update (MappedStatement ms, Object parameter)  throws  SQLException;   <E> List<E> query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql)  throws  SQLException;   <E> List<E> query (MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)  throws  SQLException;   <E> Cursor<E> queryCursor (MappedStatement ms, Object parameter, RowBounds rowBounds)  throws  SQLException;   List<BatchResult> flushStatements ()  throws  SQLException;   void  commit (boolean  required)  throws  SQLException;   void  rollback (boolean  required)  throws  SQLException;   CacheKey createCacheKey (MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) ;   boolean  isCached (MappedStatement ms, CacheKey key) ;   void  clearLocalCache () ;   void  deferLoad (MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) ;   Transaction getTransaction () ;   void  close (boolean  forceRollback) ;   boolean  isClosed () ;   void  setExecutorWrapper (Executor executor) ; } 
Excutor是模板方法设计模式的应用,Executor接口有个抽象子类BaseExecutor,定义了一些模板方法,通过子类来实现。
SimpleExecutor是默认的Executor实现,适用于大多数情况,但不支持高级功能如二级缓存。我们只需要定义好myabtis-config.xml,MyBatis框架会负责调用适当的Executor来执行数据库操作。
总结 
声明:本站所有文章均为原创或翻译,遵循署名 - 非商业性使用 - 禁止演绎 4.0 国际许可协议 ,如需转载请确保您对该协议有足够了解,并附上作者名 (Tsukasa) 及原文地址