mybatis源码分享一sql执行

1分层介绍

mybatis从宏观上可以分为三层如图:

mybatis的主要工作流程如下:

1.1 接口层

首先接口层是我们打交道最多的。核心对象是SqlSession,它是上层应用和MyBatis打交道的桥梁, SqlSession上定义了非常多的对数据库的操作方法。接口层在接收到调用请求的时候,会调用核心处理 层的相应模块来完成具体的数据库操作。
1.2 核心层
核心处理层就是跟数据库打交道的逻辑在这里进行完成
核心处理层主要做了这几件事:

  1. 把接口中传入的参数解析并且映射成JDBC类型;
  2. 解析xml文件中的SQL语句,包括插入参数,和动态SQL的生成;
  3. 执行SQL语句;
  4. 处理结果集,并映射成Java对象。
    插件也是属于核心层,是由他的工作方式和拦截对象决定的
    1.3 基础处理层
    最后一个就是基础支持层。基础支持层主要是一些抽取出来的通用的功能(实现复用),用来支持 核心处理层的功能。比如数据源、缓存、日志、xml解析、反射、IO、事务等等这些功能。
    2.核心流程
    2.1 核心对象的生命周期
    2.1.1 SqlSessionFactoryBuiler
    首先是SqlSessionFactoryBuiler。它是用来构建SqlSessionFactory的,而SqlSessionFactory只需要 一个,所以只要构建了这一个SqlSessionFactory,它的使命就完成了,也就没有存在的意义了。所以它 的生命周期只存在于方法的局部。
    2.1.2 SqlSessionFactory
    SqlSessionFactory是用来创建SqlSession的,每次应用程序访问数据库,都需要创建一个会话。因为 我们一直有创建会话的需要,所以SqlSessionFactory应该存在于应用的整个生命周期中(作用域是应用 作用域)。创建SqlSession只需要一个实例来做这件事就行了,否则会产生很多的混乱,和浪费资源。 所以我们要采用单例模式 。
    2.1.3 SqlSession
    SqlSession是一个会话,因为它不是线程安全的,不能在线程间共享。所以我们在请求开始的时候创 建一个SqlSession对象,在请求结束或者说方法执行完毕的时候要及时关闭它(一次请求或者操作 中)。
    2.1.4 Mapper
    Mapper(实际上是一个代理对象)是从SqlSession中获取的。
    userMapper mapper = sqlSession.getMapper(UserMapper.class)
    它的作用是发送SQL来操作数据库的数据。它应该在一个SqlSession事务方法之内。

2.2 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
2.2.1 SqlSessionFactoryBuilder
首先我们new了一个SqlSessionFactoryBuilder,这是建造者模式的运用(建造者模式用来创建复杂 对象,而不需要关注内部细节,是一种封装的体现)。MyBatis中很多地方用到了建造者模式(名字以 Builder结尾的类还有9个)。 SqlSessionFactoryBuilder中用来创建SqlSessionFactory对象的方法是build(),build()方法有9个重 载,可以用不同的方式来创建SqlSessionFactory对象。SqlSessionFactory对象默认是单例的。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
   
  try {
   
    // 用于解析 mybatis-config.xml,同时创建了 Configuration 对象 >>
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 解析XML,最终返回一个 DefaultSqlSessionFactory >>
    return build(parser.parse());
  } catch (Exception e) {
   
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
   
    ErrorContext.instance().reset();
    try {
   
      inputStream.close();
    } catch (IOException e) {
   
      // Intentionally ignore. Prefer previous error.
    }
  }
}

在build方法中首相创建了一个XMLConfigBuilder对象,专门用来解析全局配置文件,针对不同的构建目标还有其他的一些子类,比如:
XMLMapperBuilder:解析Mapper映射器
XMLStatementBuilder:解析增删改查标签
XMLScriptBuilder:解析动态SQL
然后构建执行 build(parser.parse());
parser.parse():方法返回一个Configuration对象,后面在详看

public Configuration parse() {
   
  if (parsed) {
   
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // XPathParser,dom 和 SAX 都有用到 >>
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

build方法:


```java
public SqlSessionFactory build(Configuration config) {
   
  return new DefaultSqlSessionFactory(config);
}

可以看到sqlsessionFactory最后实现的是DefaultSqlSessionFactory对象
2.2.2 XMLConfigBuilder
在XMLConfigBuilder 初始化后做了那些操作

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
   
  // EntityResolver的实现类是XMLMapperEntityResolver 来完成配置文件的校验,根据对应的DTD文件来实现
  this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

进入重载方法,可以看见创建了Configuration对象

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
   
  super(new Configuration()); // 完成了Configuration的初始化
  ErrorContext.instance().resource("SQL Mapper Configuration");
  this.configuration.setVariables(props); // 设置对应的Properties属性
  this.parsed = false; // 设置 是否解析的标志为 false
  this.environment = environment; // 初始化environment
  this.parser = parser; // 初始化 解析器
}

Configuration初始化了类型别名的初始工作

public Configuration() {
   
  // 为类型注册别名
  typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

  typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  typeAliasRegistry.registerAlias("LRU", LruCache.class);
  typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

  typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

  typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

  typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

  typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

  languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  languageRegistry.register(RawLanguageDriver.class);
}

2.2.4 parse解析
parser.parse() 方法解析

public Configuration parse() {
   
  if (parsed) {
   
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  // XPathParser,dom 和 SAX 都有用到 >>
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}
parseConfiguration方法:
private void parseConfiguration(XNode root) {
   
  try {
   
    //issue #117 read properties first
    // 对于全局配置文件各种标签的解析
    propertiesElement(root.evalNode("properties"));
    // 解析 settings 标签
    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"));
    // settings 子标签赋值,默认值就是在这里提供的 >>
    settingsElement(settings);
    // read it after objectFactory and objectWrapperFactory issue #631
    // 创建了数据源 >>
    environmentsElement(root.evalNode("environments"));
    databaseIdProviderElement(root.evalNode("databaseIdProvider"));
    typeHandlerElement(root.evalNode("typeHandlers"));
    // 解析引用的Mapper映射器
    mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
   
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

2.2.4.1 全局配置文件解析
properties解析

private void propertiesElement(XNode context) throws Exception {
   
  if (context != null) {
   
    // 创建了一个 Properties 对象,后面可以用到
    Properties defaults = context.getChildrenAsProperties();
    String resource = context.getStringAttribute("resource");
    String url = context.getStringAttribute("url");
    if (resource != null && url != null) {
   
      // url 和 resource 不能同时存在
      throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
    }
    // 加载resource或者url属性中指定的 properties 文件
    if (resource != null) {
   
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
   
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    Properties vars = configuration.getVariables();
    if (vars != null) {
   
      // 和 Configuration中的 variables 属性合并
      defaults.putAll(vars);
    }
    // 更新对应的属性信息
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}

第一个是解析标签,读取我们引入的外部配置文件,例如db.properties。 这里面又有两种类型,一种是放在resource目录下的,是相对路径,一种是写的绝对路径的(url)。 解析的最终结果就是我们会把所有的配置信息放到名为defaults的Properties对象里面(Hashtable对 象,KV存储),最后把XPathParser和Configuration的Properties属性都设置成我们填充后的 Properties对象。
settings解析

private Properties settingsAsProperties(XNode context) {
   
  if (context == null) {
   
    return new Properties();
  }
  // 获取settings节点下的所有的子节点
  Properties props = context.getChildrenAsProperties();
  // Check that all settings are known to the configuration class
  MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  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).");
    }
  }
  return props;
}

getChildrenAsProperties:获取所有节点信息

public Properties getChildrenAsProperties() {
   
  Properties properties = new Properties();
  for (XNode child : getChildren()) {
   
    // 获取对应的name和value属性
    String name = child.getStringAttribute("name");
    String value = child.getStringAttribute("value");
    if (name != null && value != null) {
   
      properties.setProperty(name, value);
    }
  }
  return properties;
}

loadCustomVfs(settings)
loadCustomVfs是获取Vitual File System的自定义实现类,比如要读取本地文件,或者FTP远程文件 的时候,就可以用到自定义的VFS类

this.loadCustomLogImpl(settings);
通过setting中配置的日志实现类set到configuration中

private void loadCustomLogImpl(Properties props) {
   
Class<? extends Log> logImpl = this.resolveClass(props.getProperty("logImpl"));
this.configuration.setLogImpl(logImpl);
}
this.typeAliasesElement(root.evalNode("typeAliases"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值