Mybatis启动之SqlSessionFactoryBuilder

1,955 阅读4分钟

从HelloWorld来分析Mybatis配置文件的加载过程

我的Mybatis源码地址

包含了所有代码和配置文件以及DB.sql

Mybatis的启动流程大概分为几部分

1.通过Resources处理得到一个字节流或者字符流的处理类

2.通过SqlSessionFactoryBuilder构建一个SqlSessionFactory

3.通过SqlSessionFactory得到SqlSession

4.通过SqlSession执行相关Mapper文件的sql语句

这篇文章主要分析第2个步骤中的SqlSessionFactoryBuilder,SqlSessionFactory的一些源码

通过SqlSessionFactoryBuilder构建一个SqlSessionFactory

首先看下SqlSessionFactoryBuilder的源码
/**
 * Builds {@link SqlSession} instances.
 *
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {
​
  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }
​
  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }
​
  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
​
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
​
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
​
  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }
​
  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }
​
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      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.
      }
    }
  }
​
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
​
}
​

里面为我们提供了9个方法

前面8个方法在最后都会生成一个Configuration然后去调用第九个方法。

生成一个SqlSessionFactory对象

方法名参数
build(Reader reader)reader:字符流处理类
build(Reader reader, String environment)environment:数据源ID
build(Reader reader, Properties properties)properties:配置文件,一般是数据库配置文件
build(Reader reader, String environment, Properties properties)
build(InputStream inputStream)inputStream:字节流处理类
build(InputStream inputStream, String environment)
build(InputStream inputStream, Properties properties)
build(InputStream inputStream, String environment, Properties properties)
build(Configuration config)config:mybatis核心配置
下面对这些方法进行一个简单的实验
CreateDB.sql
--
--    Copyright 2009-2018 the original author or authors.
--
--    Licensed under the Apache License, Version 2.0 (the "License");
--    you may not use this file except in compliance with the License.
--    You may obtain a copy of the License at
--
--       http://www.apache.org/licenses/LICENSE-2.0
--
--    Unless required by applicable law or agreed to in writing, software
--    distributed under the License is distributed on an "AS IS" BASIS,
--    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--    See the License for the specific language governing permissions and
--    limitations under the License.
--

create database mybatis;
use mybatis;
drop table if exists student;
CREATE TABLE student (
  id     INT  primary key ,
  name   VARCHAR(255),
  age    INT
);
INSERT INTO student VALUES
  (1, 'mybatis', 10);
CreateDB2.sql
--
--    Copyright 2009-2018 the original author or authors.
--
--    Licensed under the Apache License, Version 2.0 (the "License");
--    you may not use this file except in compliance with the License.
--    You may obtain a copy of the License at
--
--       http://www.apache.org/licenses/LICENSE-2.0
--
--    Unless required by applicable law or agreed to in writing, software
--    distributed under the License is distributed on an "AS IS" BASIS,
--    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--    See the License for the specific language governing permissions and
--    limitations under the License.
--


create database mybatis2;
use mybatis2;
drop table if exists student;
CREATE TABLE student
(
    id   INT primary key,
    name VARCHAR(255),
    age  INT
);
INSERT INTO student
VALUES (1, 'mybatis2', 10);
db.properties
db.username=root
db.password=root
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false
db.url2=jdbc:mysql://localhost:3306/mybatis2?useSSL=false
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "./mybatis-3-config.dtd">

<configuration>
    <!--引入外部资源  -->
<!--    <properties resource="org/apache/ibatis/basebuilder/db.properties"/>-->
<!--    <properties>-->
<!--        <property name="driver" value="com.mysql.jdbc.Driver"/>-->
<!--    </properties>-->
    <typeAliases>
                <typeAlias  alias="student" type="org.apache.ibatis.mytest.StudentDto"/>
    </typeAliases>
    <!--设置默认的环境为开发环境  -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
        <environment id="development2">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url2}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- <package name="com.zzz.mybatis.mapper"/> -->
        <mapper resource="org/apache/ibatis/mytest/StudentMapper.xml"/>
    </mappers>
</configuration>
StudentDto.java
package org.apache.ibatis.mytest;

import lombok.Data;

/**
 * @author yangqiang
 * @date 2021-07-20 23:06
 */
@Data
public class StudentDto {
    private Integer id;
    private String  name;
    private  Integer age;
}
StudentMapper.java
package org.apache.ibatis.mytest;

import org.apache.ibatis.annotations.Mapper;

import java.util.*;

/**
 * @author yangqiang
 * @date 2021-07-20 23:08
 */
@Mapper
public interface StudentMapper {

    public List<StudentDto> getStudentsByAlias();
}
StudentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.mytest.StudentMapper">

    <cache-ref namespace="org.apache.ibatis.mytest.StudentMapper"/>
    <cache type="PERPETUAL" eviction="LRU" flushInterval="60000" readOnly="true" size="512" blocking="false">
        <property name="name" value="zhangsan"/>
    </cache>

    <select id="getStudentsByAlias" resultType="student" useCache="false" >
        select id,name,age from student
    </select>
</mapper>
SqlSessionFactoryBuilderTest.java
package org.apache.ibatis.mytest;
​
import org.apache.ibatis.BaseDataTest;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
​
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Properties;
​
/**
 * @author yangqiang
 * @date 2021-07-20 23:14
 */
public class SqlSessionFactoryBuilderTest {
    private static SqlSessionFactory sqlSessionFactory;
​
    @BeforeAll
    static void setUp() throws Exception {
        //准备工作 创建数据库和表插入一条数据
        Properties properties = Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties");
        String driver = properties.getProperty("db.driver");
        String url = properties.getProperty("db.url");
        String url2 = properties.getProperty("db.url2");
        String userName = properties.getProperty("db.username");
        String password = properties.getProperty("db.password");
        DataSource mybatisDS = new PooledDataSource(driver, url, userName, password);
        DataSource mybatis2DS = new PooledDataSource(driver, url2, userName, password);
        BaseDataTest.runScript(mybatisDS,"org/apache/ibatis/mytest/CreateDB.sql");
        BaseDataTest.runScript(mybatis2DS,"org/apache/ibatis/mytest/CreateDB2.sql");
    }
​
    @Test
    public void readerTest() throws IOException {
        //得到一个Reader处理类(字符流)
        try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/mytest/mybatis-config.xml")) {
            // environment:数据源ID 在mybatis-config.xml <environments> 标签里面配置的 id 为development的数据源信息(非必传)
            // properties :配置文件信息。一般是数据库配置信息。可以在这里进行配置 也可以在mybatis-config.xml 通过<properties> 标签进行引入properties配置文件
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader,"development", Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<StudentDto> rs = sqlSession.selectList("org.apache.ibatis.mytest.StudentMapper.getStudentsByAlias", null);
        System.out.println(rs.toString());
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        rs = mapper.getStudentsByAlias();
        System.out.println(rs.toString());
    }
​
    @Test
    public void streamTest() throws IOException {
        //得到一个InputStream处理类(字节流)
        try (InputStream inputStream = Resources.getResourceAsStream("org/apache/ibatis/mytest/mybatis-config.xml")) {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "development2",Resources.getResourceAsProperties("org/apache/ibatis/mytest/db.properties"));
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<StudentDto> rs = sqlSession.selectList("org.apache.ibatis.mytest.StudentMapper.getStudentsByAlias", null);
        System.out.println(rs.toString());
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        rs = mapper.getStudentsByAlias();
        System.out.println(rs.toString());
    }
}
结果1:
[StudentDto(id=1, name=mybatis, age=10)]
[StudentDto(id=1, name=mybatis, age=10)]
结果2
[StudentDto(id=1, name=mybatis2, age=10)]
[StudentDto(id=1, name=mybatis2, age=10)]

这样就完成了一个Mybatis整个启动流程以及调用。 下一篇将研究SqlSessionFactoryBuilder.builder中的parser.parse()方法