MyBatis 源码分析-技术分享

MyBatis 源码分析java

这张图总结的很到位::spring

  • MyBatis的主要成员
    • Configuration        
      • MyBatis全部的配置信息都保存在Configuration对象之中,
      • 配置文件中的大部分配置都会存储到该类中
    • SqlSession            
      • 做为MyBatis工做的主要顶层API
      • 表示和数据库交互时的会话,完成必要数据库增删改查功能
    • Executor              
      • MyBatis执行器,
      • 是MyBatis 调度的核心,
      • 负责SQL语句的生成和查询缓存的维护
    • StatementHandler
      • 封装了JDBC Statement操做,
      • 负责对JDBC statement 的操做,
        • 如设置参数等
    • ParameterHandler  
      • 负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
    • ResultSetHandler  
      • 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
    • TypeHandler          
      • 负责java数据类型和jdbc数据类型(也能够说是数据表列类型)之间的映射转换
    • MappedStatement  
      • MappedStatement维护一条<select|update|delete|insert>节点的封装
    • SqlSource              
      • 负责根据用户传递的parameterObject,
      • 动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
    • BoundSql              
      • 表示动态生成的SQL语句以及相应的参数信息
    • knownMappers.put(type, new MapperProxyFactory<T>(type));
    • sqlSessionFactory.openSession();
      • openSessionFromDataSource
        •  
  • spring-mybatis
    • ClassPathMapperScanner doScan方法的真正调用地方
      • definition.setBeanClass(this.mapperFactoryBean.getClass()); 注册 beanDefinition class 为mapperFactoryBean
    • xmlConfigBuilder
      • configuration = xmlConfigBuilder.getConfiguration();
    • XMLStatementBuilder
      • builderAssistant.addMappedStatement...
        • MappedStatement.Builder statementBuilder = new MappedStatement.Builder(...
    • XMLMapperBuilder  解析xml 配置文件,包括 mapper.xml 的配置
      • xmlMapperBuilder.parse();
        • bindMapperForNamespace 绑定 mapper
          • configuration.addMapper(boundType);
            • mapperRegistry.addMapper(type);
              • knownMappers.put(type, new MapperProxyFactory<T>(type));
    • return this.sqlSessionFactoryBuilder.build(configuration);
      • return new DefaultSqlSessionFactory(config);
    • MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
      • public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
          if (!this.externalSqlSession) {
            this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
          }
        }
    • SqlSessionTemplate 本质是 SqlSession 的装饰器
      • this.sqlSessionProxy = (SqlSession) newProxyInstance(   // sqlSessionProxy  本质是 SqlSession的动态代理
            new Class[] { SqlSession.class },
            new SqlSessionInterceptor());
    • if (!isEmpty(this.plugins)) {
        for (Interceptor plugin : this.plugins) {
          configuration.addInterceptor(plugin);   // 初始化插件
          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Registered plugin: '" + plugin + "'");
          }
        }
      }
      • interceptorChain.addInterceptor(interceptor);
      • protected final InterceptorChain interceptorChain = new InterceptorChain();  // InterceptorChain 是configuration 的成员变量
      • 插件的调用是在 建立四大 Handler 的时候 执行 pluginAll
        • 是在程序执行阶段
          好比:doUpdate 时候,执行 newParameterHandler
          • StatementHandler handler = configuration.newStatementHandler(
            • public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
                ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
                parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
                return parameterHandler;
              }
    • processPropertyPlaceHolders

      • 执行属性的处理,简单的说,
        • 就是把xml中${XXX}中的XXX替换成属性文件中的相应的值
    • findCandidateComponents
      • if (isCandidateComponent(sbd)) {
        • protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
            return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
          }
  • 初始化阶段:
  • 程序运行阶段:
    • 基本运行:
      • public static void main(String[] args) {
                //定义 SqlSessionFactory
                SqlSessionFactory sqlSessionFactory = null;
                try {
                    //使用配置文件建立 SqlSessionFactory
                    sqlSessionFactory = new SqlSessionFactoryBuilder().build(
                            Resources.getResourceAsReader("mybatis-config.xml"));
                } catch (IOException ex) {
                    //打印异常.
                    Logger.getLogger(MainCh1.class.getName()).fatal("建立 SqlSessionFactory失败", ex);
                    return;
                }
                //定义 sqlSession
                SqlSession sqlSession = null;
                try {
                    //用sqlSessionFactory建立sqlSession
                    sqlSession = sqlSessionFactory.openSession();
                    //获取Mapper
                    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                    //执行Mapper接口方法.
                    UserPO user = userMapper.findUser(1);
                    //打印信息
                    System.err.println(user.getUsername());
                } finally {
                    //使用完后要记得关闭sqlSession资源
                    if (sqlSession != null) {
                        sqlSession.close();
                    }
                }
            }
    • spring 托管:
      • 添加配置
        <import resource="spring-mybatis.xml"/>
  • 插件:
    • 多个interceptor呢?固然是代理类又被代理了​​​​​
      • 配置的时候写在最前面的最后执行
    • 以分页插件为例:
      • 须要 继承 Interceptor
        • @Intercepts(
              {
                  @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                  @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
              }
          )
          public class PageInterceptor implements Interceptor {
      • interceptorChain.pluginAll(executor) 在configuration 内部注册全部的 plugin
      • 本质就是getSignatureMap 方法,扫描全部注解,对配置的Signature 方法进行 动态代理
      • 代理类就是public class Plugin implements InvocationHandler
      • 执行Plugin 的invoke 会判断该方法是否被代理(signatureMap 里面有没有)
      • 若是有执行 intercept 方法
      • 该方法最后一行执行的proceed 方法,其实就是该方法的invoke 执行
  • 手写TypeHandler:
    • 详见 催收系统CalendarTypeHandler
  • 缓存:
    • 一级缓存:
      • 一级缓存默认开启,SqlSession 级别的
      • 验证:
        • 相同的查询,连续查询两遍,记录查询用时,会发现第二次快得多
        • update、 insert、delete 等语句会触发清除缓存
      • 一级缓存,在存在俩sqlsession 时,可能存在脏数据的状况
        • 好比,sqlsessionA 两次相同查询t 表中间,
        • sqlsessionB 更新了t表数据,
        • sqlsessionA 第二次查询的数据就是可能已被修改的脏数据
    • 二级缓存:
      • 二级缓存默认关闭,SqlSessionFactory 级别的 
      • 更不靠谱,开启方式:
  • N+1问题:
    • 存在级联查询(嵌套查询)中
      • 外层查询的一条结果数据,由内层查询得到
      • 外层查询一次,得到结果数N ,就要进行N 次内层查询
        • (官方不鼓励使用,这样产生大量1+N次查询)
    • 因为1+N 问题的性能损耗,能够考虑配合使用 延时加载
    • 官网解释:
  • lazy loading 是怎么作到的?
    • 懒加载在级联查询时用到了,SimpleStatementHandler 里面query结果
    • DefaultResultSetHandler 处理结果
    • handleResultSets -->handleResultSets -->...getRowValue-->createResultObject 
    • 若是有嵌套查询且开启了懒加载 那么会使用代理工厂来处理(代理工厂类型cglib或javasissit类型(默认))
    • 针对某一个属性,当执行
      • 新版本,已经变化:
      • 解释为何是“或”的关系:lazyLoadTriggerMethods 包含该方法的时候,
        • 说明对象再进行 equals、clone比较,须要全部属性所有查询来才能进行
          • protected Set<String> lazyLoadTriggerMethods =
            • new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
    • 在嵌套查询的时候 get 方法会触发 ResultLoaderMap LoadPair load() 方法去查询(我看源代码的理解)
      • set 方法不会触发
    • 我找到了触发函数lazyLoadTriggerMethods 里面没有get/is 
      • 依赖的是PropertyNamer.isGetter(methodName)
相关文章
相关标签/搜索