• mybatis全局配置文件详解


    一,orm介绍

    o <===> object
    r <===> relational
    m <===> mapping
    
    • 1
    • 2
    • 3

    MyBatis:是一个持久层框架,使用简单,学习成本较低。可以执行自己手写的sql语句,比较灵活,但是mybatis的自动程度画不高,移植性也不高,从一个数据库迁移到另外一个数据库的时候需要修改配置,所以被称为半自动化ORM框架

    Hibernate:底层实现了很多add方式,所以被称为全自动ORM框架

    2,JDBC

    2.1四大核心对象

    1,加载驱动

    Class.forName("com.mysql.jdbc.Driver")
    
    • 1

    2,创建连接

    COnnection con = DriverManager.getConnection(...)
    
    • 1

    3,获取sql执行者,执行操作

    con.prepareStament(sql)
    
    • 1

    4,执行查询

    execute();
    
    • 1

    5,关闭数据库,关闭连接

    .close()
    
    • 1

    2.1 JDBC出现一些的问题

    1,数据库配置,sql语句在代码中出现硬编码的问题,mybatis通过xml解决
    2,jdbc频繁的创建和关闭数据库链接,对数据库是一个很大的消耗,mybatis通过数据库连接池解决。
    3,参数设置不方便,mybatis 通过动态sql查询
    4,处理结果集不方便,mybatis 可以自定义 resultMap 进行映射

    三,mybatis功能架构

    API接口层:提供给外部使用的接口api,可以通过这些api来操纵数据库

    数据处理层:负责具体的sql查找、解析、执行和映射结果处理等

    基础支撑层:负责最基础功能的支撑,如一些连接管理,事务管理,配置加载,缓存等。

    可以通过这份笔记来对这个mybatis有一个基本的了解:https://note.youdao.com/ynoteshare/index.html?id=5d41fd41d970f1af9185ea2ec0647b64&type=notebook&_time=1662564167792

    四,mybatis源码解析

    1,构造对象流程

    String resource = "mybatis-config.xml";
    Reader reader;
    //将XML配置文件构建为Configuration配置类
    reader = Resources.getResourceAsReader(resource);
    // 通过加载配置文件流构建一个SqlSessionFactory  DefaultSqlSessionFactory
    SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
    // 数据源 执行器  DefaultSqlSession
    SqlSession session = sqlMapper.openSession();
    //解析配置类
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    //其底层就是专门用来解析xml的,xml里面的任何一个配置文件,都是通过node存储的
    //可以通过node来获取里面的属性和里面的值,可以获取配置文件下面的所有的值
    Node node = (Node)new XPathParser();
    //调用parse方法解析 这个全局配置文件
    parser.parse()
    //解析mybatis-config.xml的node节点
    parseConfiguration(parser.evalNode("/configuration"))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2,加载流程源码分析

    1,先查看这个SqlSessionFactoryBuilder看看底层的具体流程,其主要就是加载配置文件,将这些文件加载一个configuration的一个对象里面。

    SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
    
    • 1

    2,这个Configuration会包含很多东西,如一些环境,缓存等等等

    public class Configuration {
        //环境
     	protected Environment environment;	
     	protected boolean safeRowBoundsEnabled;
     	protected boolean safeResultHandlerEnabled = true;
     	protected boolean mapUnderscoreToCamelCase;
     	protected boolean aggressiveLazyLoading;
     	protected boolean multipleResultSetsEnabled = true;
     	protected boolean useGeneratedKeys;
     	protected boolean useColumnLabel = true;
        //缓存
     	protected boolean cacheEnabled = true;
     	protected boolean callSettersOnNulls;
     	protected boolean useActualParamName = true;
     	protected boolean returnInstanceForEmptyRow;
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3,其build方法如下,主要是通过这个责任链模式,单参调双参,双参调三参。

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
            //获取配置文件
        	XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          	//解析配置文件
            return build(parser.parse());
        }finally{
            reader.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这个解析配置文件的方法里面,会有一个parseConfiguration的类

    public Configuration parse() {
    	parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    
    • 1
    • 2
    • 3
    • 4

    接下来主要查看这个parseConfiguration类,就是对应xml里面的configuration标签里面的一些标签
    在这里插入图片描述

    private void parseConfiguration(XNode root) {
        //解析properties标签
        propertiesElement(root.evalNode("properties"));
        //解析settings标签
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        //解析environments环境标签
        environmentsElement(root.evalNode("environments"));
        //解析typeAliases别名标签
        typeAliasesElement(root.evalNode("typeAliases"));
        //解析typeHandlers标签
        typeHandlerElement(root.evalNode("typeHandlers"));
        //解析mappers标签
        mapperElement(root.evalNode("mappers"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4,解析这个配置文件的propertiesElement方法如下,里面的parse解析方法采用的是一个责任链模式

    private void propertiesElement(XNode context) throws Exception {
    	if (context != null) {
            //获取子结点
    		Properties defaults = context.getChildrenAsProperties();
    		//获取资源
            String resource = context.getStringAttribute("resource");
            //最后将这个Properties对象加入到这个configuration的配置文件里面
            configuration.setVariables(defaults);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其他的标签也是会通过这个责任链模式,对这些标签进行解析,并最终会加入到这个configuration的配置里面

    5,一直到最后一个,会去解析这个mapper里面的mappers这个配置文件。会通过这个mapper的这个类,比如说UserMapper,然后找到对应的xml文件。

    <mappers>
        <package name="com.tuling.mapper"/>
    </mappers>
    
    • 1
    • 2
    • 3

    其源码实现如下,在解析这个config.xml的配置文件之后,会解析这个mapper.xml的映射文件

    private void mapperElement(XNode parent) throws Exception {
        //创建读取XmlMapper构建器对象
        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
        //开始真正的解析mapper的xml文件
       	mapperParser.parse();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    进入这个解析映射文件的方法

    public void parse() {
        //获取mapper映射文件的所有内容,并对映射文件进行解析
    	configurationElement(parser.evalNode("/mapper"));
    }
    
    • 1
    • 2
    • 3
    • 4

    在进入这个configurationElement的这个方法

    private void configurationElement(XNode context) {
        //会先解析mapper标签里面的这个namespace的这个标签
        String namespace = context.getStringAttribute("namespace");
        //设置到这个build的助手里面
        builderAssistant.setCurrentNamespace(namespace);
        //解析当前的缓存的引用
        cacheRefElement(context.evalNode("cache-ref"));
        //解析这个二级缓存
        cacheElement(context.evalNode("cache"));
        //解析resultMapper文件
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        //解析sql片段
        sqlElement(context.evalNodes("/mapper/sql"));
        //解析数据的增删改查
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6,再查看解析mapper里面的二级缓存,cacheElement方法如下,缓存的过期策略为一个LRU

    private void cacheElement(XNode context) {
        //解析cache节点的type属性
        String type = context.getStringAttribute("type", "PERPETUAL");
        //根据type的String获取class类型
        Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
        //获取缓存过期策略:默认是LRU
        String eviction = context.getStringAttribute("eviction", "LRU");
        Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
        //flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
        Long flushInterval = context.getLongAttribute("flushInterval");
        //size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
        Integer size = context.getIntAttribute("size");
        //只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false
        boolean readWrite = !context.getBooleanAttribute("readOnly", false);
        boolean blocking = context.getBooleanAttribute("blocking", false);
        Properties props = context.getChildrenAsProperties();
        //把缓存节点加入到Configuration中
        builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    7,再查看这个解析数据的增删改查,会对文件里面的增删改查语句进行解析

    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    	for (XNode context : list) {
            //解析增删改查结点
    		final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
        	try {
    			statementParser.parseStatementNode();
        	} catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
        	}
        }
    }      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    五,总结

    1,首先会创建一个SqlSessionFactoryBuilder 的对象,用于解析配置文件
    2,会创建一个解析xml配置类的一个构造器,会解析xmlconfig里面的全部标签,并保存在configuration里面
    3,会创建一个解析xml映射类的一个构造器,专门解析对应mapper.xml里面的全部标签
    4,在解析mapper.xml的时候会将里面增删改查的元素全部解析出来,存在一个MapperStatement里面
    5,所有的信息解析完之后,都会生成在一个configuration对象,所有的信息都会加入到这个configuration里面
    6,最后会创建一个SqlSessionFactory,会将所有的配置信息,都加入到这个SqlSessionFactory里面

  • 相关阅读:
    CAS核心思想、底层实现
    并行与分布式 第七章 体系结构 上
    Linux内核中ideapad-laptop.c文件全解析9
    新能源行业供应商协同管理系统:可视化管理供应商,助力企业降本生效
    怎么让孩子树立时间观念
    Logback 日志配置
    基于Php门禁系统设计与实现
    Linux 命令(11)—— tcpdump
    docker之centos7安装docker
    超大规模系统下,MySQL到Redis的数据同步也不难吧?
  • 原文地址:https://blog.csdn.net/zhenghuishengq/article/details/126755914