• Mybatis源码日记(一)


    Mybatis执行对数据库的操作主要通过Sqlsession完成的,Sqlsession是个接口,它有两个实现类,DefaultSqlSession和SqlSessionManager。

    • DefaultSqlSession:Mybatis在构建的时候,默认创建了DefaultSqlSessionFactory,DefaultSqlSessionFactory的openSesstion()方法返回DefaultSqlSession也就是说Mybatis默认的SqlSession实现通常使用的就是这个,非线程安全。

    • SqlSessionManager:实现了SqlSession,SqlSessionFactory,这个可以开启线程安全的Sqlsession,通过startManagedSession()方法开启线程安全的SqlSession。也可以直接使用sqlSessionProxy对象执行sql语句。通过startManagedSession()开启线程安全后,切记不能openSession(),或者startManagedSession()再次获取新的SqlSession,而是通过getConnection()获取数据库连接来执行Sql。

    SqlSessionManager有三个成员变量

    // 用来创建Sqlsession
    private final SqlSessionFactory sqlSessionFactory;
    // SqlSession的实现通过这个动态代理session实例执行
    private final SqlSession sqlSessionProxy;
    // 先通过ThreadLocal<SqlSession> localSqlSession获取sqlsession,如果获取不到则通过:openSession新开一个session,如果存在就使用。
    private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();
    

    一般情况下,我们通过以下方法可以获取到Sqlsession对象

    Reader resourceAsStream = Resources.getResourceAsReader("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    

    在调用SqlSessionFactoryBuilder的build()方法时,通过源码可以发现,build方法被重载了9次。

    org.apache.ibatis.session.SqlSessionFactoryBuilder:

    public SqlSessionFactory build(Reader reader);
    public SqlSessionFactory build(Reader reader, String environment);
    public SqlSessionFactory build(Reader reader, Properties properties);
    // 上面三种方法其实最后都会调用这个方法
    public SqlSessionFactory build(Reader reader, String environment, Properties properties);
    
    public SqlSessionFactory build(InputStream inputStream);
    public SqlSessionFactory build(InputStream inputStream, String environment);
    public SqlSessionFactory build(InputStream inputStream, Properties properties);
    // 上面这三种方法最后都会调用这个方法,和第4行方法的区别是,这里接收的是一个字节流,而第4行接收的是一个字符流
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties);
    
    // 这个方法最终都会被第4个和第8个方法通过build(parser.parse())方法调用
    public SqlSessionFactory build(Configuration config) {
        // 这里返回了DefaultSqlSessionFactory
        return new DefaultSqlSessionFactory(config);
    }
    

    第4行和第8行的具体实现其实都是一样的,只是接收的流不一样,一个是字节流,一个是字符流,以第8行方法实现为例:

    org.apache.ibatis.session.SqlSessionFactoryBuilder:

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 解析mybatis-config.xml配置文件
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 调用第9行的build方法返回DefaultSqlSessionFactory对象,通过DefaultSqlSessionFactory的openSession()方法可以返回DefaultSqlSession对象
            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.
            }
        }
    }
    

    先来简单看下XMLConfigBuilder,可以看到XMLConfigBuilder和SqlSessionFactoryBuilder有些类似,XMLConfigBuilder的构造方法被重载了7次。

    public XMLConfigBuilder(Reader reader);
    public XMLConfigBuilder(Reader reader, String environment);
    // 上面两个构造方法也是调用这个,用的是字符流
    public XMLConfigBuilder(Reader reader, String environment, Properties props);
    
    public XMLConfigBuilder(InputStream inputStream);
    public XMLConfigBuilder(InputStream inputStream, String environment);
    // 上面两个构造方法也是调用这个,用的是字节流
    public XMLConfigBuilder(InputStream inputStream, String environment, Properties props);
    
    // 第3行和第6行方法都是调用的这个方法。这个方法被私有化了,应该是作者不允许有人从外部直接调用
    private XMLConfigBuilder(XPathParser parser, String environment, Properties props);
    

    第7行XMLConfigBuilder构造方法实现

    org.apache.ibatis.builder.xml.XMLConfigBuilder

    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        // parsed标识是否被解析
        this.parsed = false;
        this.environment = environment;
        // xpath解析器
        this.parser = parser;
    }
    

    回到SqlSessionFactoryBuilder,接下来是parser.parse()实现,这个方法主要是对mybatis-config.xml配置文件做了解析

    org.apache.ibatis.builder.xml.XMLConfigBuilder:

    public Configuration parse() {
        // 判断是否已经被解析过了
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        // 接下来要开始解析了,所以提前将它标识为已解析,避免后续调用parse时又被解析一遍
        parsed = true;
        // mybatis-config.xml根节点就是configuration
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
    

    mybatis-config.xml配置文件结构:

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties></properties>
        <settings>
            <setting name="" value=""/>
        </settings>
        <typeAliases></typeAliases>
        <plugins>
            <plugin interceptor=""></plugin>
        </plugins>
        <objectFactory type=""></objectFactory>
        <objectWrapperFactory type=""></objectWrapperFactory>
        <reflectorFactory type=""></reflectorFactory>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value=""/>
                    <property name="url" value=""/>
                    <property name="username" value=""/>
                    <property name="password" value=""/>
                </dataSource>
            </environment>
        </environments>
        <databaseIdProvider type=""></databaseIdProvider>
        <typeHandlers></typeHandlers>
        <mappers>
            <!-- <mapper resource=""/> -->
            <!-- mapper和package二选一 -->
            <package name=""/>
        </mappers>
    </configuration>
    

    最后看下parseConfiguration()方法做了哪些事,parseConfiguration()将mybatis-config.xml配置所有的子节点一一解析。

    org.apache.ibatis.builder.xml.XMLConfigBuilder:

    private void parseConfiguration(XNode root) {
        try {
            propertiesElement(root.evalNode("properties"));
            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"));
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            typeHandlerElement(root.evalNode("typeHandlers"));
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
    

    最后,parse()方法返回Configuration对象,在SqlSessionFactoryBuilder中build方法接收这个对象并调用,返回DefaultSqlSessionFactory对象。通过openSession()方法可以返回DefaultSqlSession,DefaultSqlSession继承自SqlSession。

    参考:

    https://mybatis.org/mybatis-3/zh/index.html

    https://blog.csdn.net/u012746134/article/details/94658840

  • 相关阅读:
    AOJ 718.计算GPA
    AOJ 11.Rails
    AOJ 592.神奇的叶子
    AOJ 10.目标柏林
    洛谷P1030求先序排列
    vijos1514天才的记忆
    洛谷2016战略游戏
    LOJ10155数字转换
    洛谷2014选课
    洛谷2015二叉苹果树
  • 原文地址:https://www.cnblogs.com/dagger9527/p/11994956.html
Copyright © 2020-2023  润新知