栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

MyBatis源代码学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MyBatis源代码学习

版本
mybatis:3.5.3

源码分析

使用(不集成Spring)
public class App {
    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        Reader reader;
        try {
            //将XML配置文件构建为Configuration配置类
            reader = Resources.getResourceAsReader(resource);
            // 通过加载配置文件流构建一个SqlSessionFactory  DefaultSqlSessionFactory
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
            // 数据源 执行器  DefaultSqlSession
            SqlSession session = sqlMapper.openSession();
            try {
                // 执行查询 底层执行jdbc
                TimeZoneName timeZoneName = (TimeZoneName)session.selectOne("com.whc.mapper.TimeZoneNameMapper.selectById", 12);

                TimeZoneNameMapper timeZoneNameMapper = session.getMapper(TimeZoneNameMapper.class);
                TimeZoneName zoneName = timeZoneNameMapper.selectById(11);
                System.out.println(zoneName);
                
                session.commit();
                System.out.println(timeZoneName);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                session.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

流程主要分为以下几步:

1.解析配置文件

2.解析mapper

3.获取SqlSession

4.使用

解析配置文件

最终在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration 中解析xml配置文件各个属性

 private void parseConfiguration(XNode root) {
        try {
            //1.解析 properties节点
            propertiesElement(root.evalNode("properties"));
            //2.解析 settings节点
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            //3.加载自定义文件系统
            loadCustomVfs(settings);
            //4.加载自定义日志实现类
            loadCustomLogImpl(settings);
            //5.加载别名
            typeAliasesElement(root.evalNode("typeAliases"));
            //6.加载插件
            pluginElement(root.evalNode("plugins"));
            //不常用
            objectFactoryElement(root.evalNode("objectFactory"));
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            //7.设置settings 和默认值
            settingsElement(settings);
            //8.environments节点
            environmentsElement(root.evalNode("environments"));
            //9.解析数据库厂商
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            //10.解析我们的类型处理器节点
            typeHandlerElement(root.evalNode("typeHandlers"));
            //11.解析mapper
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

1.解析属性(properties)

  
  

2.解析设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 


  
  
  
  
  
  
  
  
  
  
  
  
  
  
  

3.加载自定义文件系统

VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源。

Mybatis中提供了VFS这个配置,主要是通过该配置可以加载自定义的虚拟文件系统应用程序 解析到:org.apache.ibatis.session.Configuration#vfsImpl

4.加载自定义日志实现类

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

* SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING *

解析到org.apache.ibatis.session.Configuration#logImpl

5.加载类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。


  
  
  
  
  
  

当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。

6.加载插件
configuration.addInterceptor(interceptorInstance);

 会加入到  InterceptorChain 拦截器链

7.设置settings 和默认值
 private void settingsElement(Properties props) {
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultscriptingLanguage(resolveClass(props.getProperty("defaultscriptingLanguage")));
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    configuration.setCallSettersonNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  }

8.environments环境配置节点

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

9.解析数据库厂商

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:

10.解析我们的类型处理器typeHandlers节点

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。

11.解析映射器(mappers)

指定mapper映射文件可以通过如下4中方式:类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名
















解析mapper文件

入口在:org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement

我们是通过 包名批量注册的,所以会走

if ("package".equals(child.getName()))分支,

通过configuration.addMappers(mapperPackage)来解析

在MapperRegistry#addMappers(java.lang.String, java.lang.Class)中循环指定包名下的每个类,使用 MapperAnnotationBuilder 的parse()解析每个类,

最终用XMLMapperBuilder.parse() 方法解析mapper.xml文件

我们来看看org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement,具体解析mapper.xml

 private void configurationElement(XNode context) {
    try {
      
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      
      builderAssistant.setCurrentNamespace(namespace);
      
      cacheRefElement(context.evalNode("cache-ref"));
      
      cacheElement(context.evalNode("cache"));
      
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      
      sqlElement(context.evalNodes("/mapper/sql"));
      
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

当配置了节点后,会在“4”中创建二级缓存对象

二级缓存对象通过装饰器模式,一层层包装

SynchronizedCache-->LoggingCache-->SerializedCache-->LruCache-->PerpetualCache

而解析select | insert |update |delete节点会在XMLMapperBuilder#buildStatementFromContext(java.util.List)方法中,通过 langDriver.createSqlSource(),由默认的XMLLanguageDriver 驱动来解析,最终递归解析每个节点

递归解析  selectById这个sql元素会解析成
   1层  MixedSqlNode