Mybatis源码学习记录

一、对源码先上一个结构图:

源代码主要在org.apache.ibatis目录下,18个包,其中在应用中主要的包有:builder、session、cache、type、transaction、datasource、jdbc、mapping,提供支撑服务的包有annotation、binding、io、logging、plugin、reflection、scripting、exception、executor、parsing

二、从使用入手

MyBatis使用的三板斧是SqlSessionFactoryBuilder和SqlSessionFactory、SqlSession

  1. SqlSessionFactoryBuilder

    支持9种构造方法,其实最主要的是包含Cconfiguration对象的构造方法,目的是为了通过加载配置文件创造SqlSessionFactory对象

    Configuration对象主要是通过xml配置文件得到,xml文件的结构组成可从源码中看出如下:

      propertiesElement(root.evalNode("properties")); //加载资源文件属性和当前文件属性 
 
      typeAliasesElement(root.evalNode("typeAliases"));

      pluginElement(root.evalNode("plugins"));

      objectFactoryElement(root.evalNode("objectFactory"));

      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

      settingsElement(root.evalNode("settings"));

      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631

      databaseIdProviderElement(root.evalNode("databaseIdProvider"));

      typeHandlerElement(root.evalNode("typeHandlers"));

      mapperElement(root.evalNode("mappers"));

属性、类型别名、typeHandler、mapper、 环境元素(数据源和事务)、设置、插件

  • 属性:

    先来看属性的加载,属性的加载最重要的是了解三种属性来源(属性配置文件、当前文件的属性、java代码输入)和三种属性的加载顺序(先加载配置文件,在加载config文件的属性,最后加载java代码输入)

private void propertiesElement(XNode context) throws Exception {

    if (context != null) {

      Properties defaults = context.getChildrenAsProperties();

      String resource = context.getStringAttribute("resource");

      String url = context.getStringAttribute("url");

      if (resource != null && url != null) {

        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");

      }

      if (resource != null) {

        defaults.putAll(Resources.getResourceAsProperties(resource));

      } else if (url != null) {

        defaults.putAll(Resources.getUrlAsProperties(url));

      }

      Properties vars = configuration.getVariables();

      if (vars != null) {

        defaults.putAll(vars);

      }

      parser.setVariables(defaults);

      configuration.setVariables(defaults);

    }

  }
  • 类型别名

    主要是类的完整路径和简单别名的对应关系加载,保存在容器typeAliasRegistry中,最终映射到configuratiron对象中

private void typeAliasesElement(XNode parent) {

    if (parent != null) {

      for (XNode child : parent.getChildren()) {

        if ("package".equals(child.getName())) {

          String typeAliasPackage = child.getStringAttribute("name");

          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);

        } else {

          String alias = child.getStringAttribute("alias");

          String type = child.getStringAttribute("type");

          try {

            Class<?> clazz = Resources.classForName(type);

            if (alias == null) {

              typeAliasRegistry.registerAlias(clazz);

            } else {

              typeAliasRegistry.registerAlias(alias, clazz);

            }

          } catch (ClassNotFoundException e) {

            throw new BuilderException("Error registering typeAlias for ‘" + alias + "‘. Cause: " + e, e);

          }

        }

      }

    }

  }
  • typeHandler

    用户自定义类型处理器,保存在typeHandlerRegistry容器中,最终保存在configuration对象中

private void typeHandlerElement(XNode parent) throws Exception {

    if (parent != null) {

      for (XNode child : parent.getChildren()) {

        if ("package".equals(child.getName())) {

          String typeHandlerPackage = child.getStringAttribute("name");

          typeHandlerRegistry.register(typeHandlerPackage);

        } else {

          String javaTypeName = child.getStringAttribute("javaType");

          String jdbcTypeName = child.getStringAttribute("jdbcType");

          String handlerTypeName = child.getStringAttribute("handler");

          Class<?> javaTypeClass = resolveClass(javaTypeName);

          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);

          Class<?> typeHandlerClass = resolveClass(handlerTypeName);

          if (javaTypeClass != null) {

            if (jdbcType == null) {

              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);

            } else {

              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);

            }

          } else {

            typeHandlerRegistry.register(typeHandlerClass);

          }

        }

      }

    }

  }
  • mapper

    主要是加载mapper.xml文件,同构mapperParser对文件进行解析,最终保存到configuration对象中

private void mapperElement(XNode parent) throws Exception {

    if (parent != null) {

      for (XNode child : parent.getChildren()) {

        if ("package".equals(child.getName())) {

          String mapperPackage = child.getStringAttribute("name");

          configuration.addMappers(mapperPackage);

        } else {

          String resource = child.getStringAttribute("resource");

          String url = child.getStringAttribute("url");

          String mapperClass = child.getStringAttribute("class");

          if (resource != null && url == null && mapperClass == null) {

            ErrorContext.instance().resource(resource);

            InputStream inputStream = Resources.getResourceAsStream(resource);

            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

            mapperParser.parse();

          } else if (resource == null && url != null && mapperClass == null) {

            ErrorContext.instance().resource(url);

            InputStream inputStream = Resources.getUrlAsStream(url);

            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());

            mapperParser.parse();

          } else if (resource == null && url == null && mapperClass != null) {

            Class<?> mapperInterface = Resources.classForName(mapperClass);

            configuration.addMapper(mapperInterface);

          } else {

            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");

          }

        }

      }

    }

  }
  • 环境元素

    主要加载数据源、和事务配置信息,由Environment.Builder对象进行处理,并将信息保存到configuratiron对象

private void environmentsElement(XNode context) throws Exception {

    if (context != null) {

      if (environment == null) {

        environment = context.getStringAttribute("default");

      }

      for (XNode child : context.getChildren()) {

        String id = child.getStringAttribute("id");

        if (isSpecifiedEnvironment(id)) {

          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

          DataSource dataSource = dsFactory.getDataSource();

          Environment.Builder environmentBuilder = new Environment.Builder(id)

              .transactionFactory(txFactory)

              .dataSource(dataSource);

          configuration.setEnvironment(environmentBuilder.build());

        }

      }

    }

  }
  • 设置

加载ibatis自身工作需要配置的所有设置信息

 private void settingsElement(XNode context) throws Exception {

    if (context != null) {

      Properties props = context.getChildrenAsProperties();

      // Check that all settings are known to the configuration class

      MetaClass metaConfig = MetaClass.forClass(Configuration.class);

      for (Object key : props.keySet()) {

        if (!metaConfig.hasSetter(String.valueOf(key))) {

          throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");

        }

      }

      configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));

      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"), true));

      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.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.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));

      configuration.setLogPrefix(props.getProperty("logPrefix"));

      configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));

      configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));

      configuration.setInjectionFilterEnabled(booleanValueOf(props.getProperty("injectionFilterEnabled"), false));

      configuration.setInjectionFilter(parseExpression(props.getProperty("injectionFilter"), "^[a-zA-Z0-9._]*$"));

    }

  }
  1. SqlSessionFactory
  2. SqlSession

Mybatis源码学习记录

时间: 2024-10-04 05:53:02

Mybatis源码学习记录的相关文章

mybatis专题(三)-----mybatis源码学习

源码分析概述 源码包分析 下载地址 MyBatis 源码下载地址:https://github.com/mybatis/mybatis-3 导入过程 1. 下载MyBatis的源码 2. 检查maven的版本,必须是3.25以上,建议使用maven的最新版本 3. mybatis的工程是maven工程,在开发工具中导入,工程必须使用jdk1.8以上版本: 4. 把mybatis源码的pom文件中true,全部改为false,或者直接删除这行: 5. 在工程目录下执行 mvn clean inst

mybatis源码学习: 编译的方法

mybatis3用了一段时间,抽出时间来研究一下.具体用法参考官方文档就行,源码在这里.mybatis相对而言,规模较小,可以从中学习如何编写高质量的java项目. mybatis3使用maven管理.方法很简单. 1,mybatis3项目依赖父项目,所以先clone这个项目,然后mvn install 就可以了.就会安装到本地仓库. 2.再mvn install  mybatis-3对应的项目就成功了

select 源码学习记录

先记录一下学习的成果,慢慢完善 四个相关函数 fd_set的结构在上一篇中有讲,同时解释了为什么最大1024. -fd_set为1024/32的long型数组结构体.也就是结构体里面保存了long型数组 int FD_ZERO(int fd, fd_set *fdset); int FD_CLR(int fd, fd_set *fdset); int FD_SET(int fd, fd_set *fd_set); int FD_ISSET(int fd, fd_set *fdset); FD_Z

Mybatis 源码学习系列

前言 很久以前,我们学习了Java,从一个控制台的 Hello world .开始,我们进入了面向对象的世界. 然后由学习了SQL语言,可以写出SQL语句来将尘封在硬盘之下的数据库数据,展现出来. 后来我们使用JDBC 来操作数据库,进行各种增删改查.然而,我们对于原生的JDBC并不满意,写起来很不爽-- 于是,我们使用了Mybatis,写了一个又一个的mapper,无论需求怎么变动,无论情况怎么复杂多变.对于我们来说只是增加mapper,修改xml中的sql就好了. 从此,一切似乎很简单,水到

【mybatis源码学习】mybatis的参数处理

一.mybatis的参数处理以及参数取值 1.单个参数 mybatis不做任何处理 取值方式: ? #{参数名/任意名} <!-- Employee getEmpById(Integer id); --> <select id="getEmpById" resultType="com.mxc.bean.Employee"> select * from employee where id=#{id} </select> 2.多个参数

Swoole源码学习记录(十四)——Server模块详解(下)

swoole版本:1.7.6-stable 上一章已经分析了如何启动swServer的相关函数.本章将继续分析swServer的相关函数, 1.swServer函数分析 swServer_addListener 该函数用于在swServer中添加一个需要监听的host及port.函数原型如下: // Server.h 438h int swServer_addListener(swServer *serv, int type, char *host,int port); 参数 说明 swServ

Swoole源码学习记录(十)——Factory模块(下)

Swoole版本:1.7.5-stable 本章将分析FactoryProcess.c中剩下的函数,这些函数用于操作worker.manager以及writer.这些函数提供了最核心的进程创建.管理等功能,是Swoole的master-worker结构的基石. 先从worker相关的函数开始(manager相关函数基本都涉及操作worker进程).在FactoryProcess.c中一共声明了4个操作函数,分别是: static int swFactoryProcess_worker_loop(

Swoole源码学习记录(十五)——Timer模块分析

swoole版本:1.7.7-stable Github地址:点此查看 1.Timer 1.1.swTimer_interval_node 声明: // swoole.h 1045-1050h typedef struct _swTimer_interval_node { struct _swTimerList_node *next, *prev; struct timeval lasttime; uint32_t interval; } swTimer_interval_node; 成员 说明

Swoole源码学习记录(十一)——Worker,Connection

Swoole版本:1.7.5-stable 本章将分析Swoole中的三个比较重要的模块,Worker,ReactorProcess和Connection.其中Worker和ReactorProcess其实是对前面三章的一个补充,在前面的章节中为了分析结果的流畅性没有针对这些模块做特定分析,在此做出补充. Worker模块 首先是Worker模块.Worker在Swoole中为核心工作进程的封装,包括用于处理核心逻辑的worker和用于处理任务的task_worker.在Swoole中使用了结构