Mybatis源码分析1-初始化原理

2020/12/22

1、MyBatis 自动配置原理

数据源连接

mybatis是如何操作数据的,我们先来创建一个简单的springboot工程,使用 idea的spring初始化创建springboot工程mybatis-sources

源码:/study-demo/mybatis-sources

1)pom.xml导入依赖如下:

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoding</groupId>
	<artifactId>mybatis-sources</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>mybatis-sources</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!--<dependency>-->
			<!--<groupId>org.mybatis.spring.boot</groupId>-->
			<!--<artifactId>mybatis-spring-boot-starter</artifactId>-->
			<!--<version>2.2.0</version>-->
		<!--</dependency>-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.4</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

springboot 2.x版本后自动依赖的mysql驱动是mysql8.0版本的

2)配置文件appliction.properties,配置数据库连接

spring.datasource.url=jdbc:mysql://localhost:3306/icoding_mall?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=mysql8013
#spring.datasource.driver-class-name=

driver-class-name属性不传的话,springboot会自动决定,看源码

进入方法fromJdbcUrl()

public static DatabaseDriver fromJdbcUrl(String url) {
  if (StringUtils.hasLength(url)) {
    Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'");
    String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(Locale.ENGLISH);
    for (DatabaseDriver driver : values()) {
      for (String urlPrefix : driver.getUrlPrefixes()) {
        String prefix = ":" + urlPrefix + ":";
        if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
          return driver;
        }
      }
    }
  }
  return UNKNOWN;
}

代码中的values指定了DatabaseDriver的多个枚举值,定义根据url获取driverClassName ,枚举值如下:

3)测试数据源连接 ,单元测试类:

@SpringBootTest
class MybatisSourcesApplicationTests {
	@Autowired
	DataSource dataSource;

	@Test
	void contextLoads() throws SQLException {
		Connection connection = dataSource.getConnection();
		System.out.println(connection);
		connection.close();
	}
}

执行测试方法

可见,springboot的配置类DataSourceConfiguration默认配置的数据源是HikariDataSource

操作数据库表

参考官方文档:https://mybatis.org/mybatis-3/zh/getting-started.html

第一步,先要创建一个mybatis的全局配置文件,它的目的是构建``SqlSessionFactory`,官网是这么说的:

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory的实例为核心的,SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder获得,而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的Configuration 实例来构建出 SqlSessionFactory 实例。

方式一,从 XML 配置文件构建SqlSessionFactory

从 XML 文件中构建 SqlSessionFactory的实例非常简单,建议使用类路径下的资源文件进行配置,但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

官方文档,给出一个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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

注意 XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则包含了一组映射器(mapper),这些映射器的XML 映射文件包含了 SQL 代码和映射定义信息。

方式二,不使用 XML 构建 SqlSessionFactory

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

例中,configuration 添加了一个映射器类(mapper class)。映射器类是 Java 类,它们包含 SQL 映射注解从而避免依赖 XML 文件。不过,由于 Java 注解的一些限制以及某些 MyBatis 映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用 XML 配置。有鉴于此,如果存在一个同名XML 配置文件,MyBatis 会自动查找并加载它(在这个例子中,基于类路径和 BlogMapper.class 的类名,会加载BlogMapper.xml)。

从 SqlSessionFactory 中获取 SqlSession

有了 SqlSessionFactory,我们就可以从中获得 SqlSession 的实例(工厂模式)。SqlSession提供了在数据库执行 SQL 命令所需的所有方法。你可以通过SqlSession实例来直接执行已映射的 SQL 语句。例如:

try (SqlSession session = sqlSessionFactory.openSession()) {
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如BlogMapper.class)

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

上面说了mybatis的入门,更详细了解需要看官方文档:https://mybatis.org/mybatis-3/zh/getting-started.html

简单的Mybatis框架

前面我们创建了工程mybatis-sources,

第一步,在resources/mybatis目录下创建配置文件mybatis-config.xml

第二步,创建实体类UmsAdmin.java

@Data
public class UmsAdmin {
	private Long id;
	private String username;
	private String password;
	private String email;
	private String nickName;
}

第三步,创建映射器UmsAdminMapper.java,注意它是一个接口,底层会使用cglib代理实现接口

public interface UmsAdminMapper {
	UmsAdmin getAdminById(Long id);
}

第四步,创建映射器的XML映射文件UmsAdminMapper.xml,在resources/mybatis/mapper目录下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
		PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
		"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.icoding.mapper.UmsAdminMapper">
	<select id="getAdminById" resultType="com.icoding.bean.UmsAdmin">
    	select * from ums_admin where id = #{id}
  </select>
</mapper>

注意命名空间namespace是UmsAdminMapper

准备工作完毕,下面我们来构建 SqlSessionFactory获取SqlSession来在数据库执行SQL语句

@SpringBootTest
class MybatisSourcesApplicationTests {
    /**
     * 1、根据全局配置文件获取 SqlSessionFactory
     * @return
     * @throws IOException
     */
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis/mybatis-config.xml";
      	// 输入流代表了整个mybatis的配置信息
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //1、拿到全局配置
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        //准备环境信息
        Environment environment = new Environment("development", transactionFactory, dataSource);
        //2、使用我们的数据源
        sqlSessionFactory.getConfiguration().setEnvironment(environment);
        //使用mybatis-config.xml + Spring的数据源
        return sqlSessionFactory;
    }

  
  @Test
    public void getFromDb() throws IOException {
        //1、得到 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        //2、得到 sqlSession ,代表和数据库一次回话
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3、得到真正操作数据库的Dao
        CityMapper mapper = sqlSession.getMapper(CityMapper.class);
        City city = mapper.getCityById(1L);

        System.out.println(city);

        sqlSession.close();
    }


}
  • 所有Mapper接口的实现类都是 org.apache.ibatis.binding.MapperProxy
    • public class MapperProxy implements InvocationHandler, Serializable
    • MyBatis自动为我们所有的Mapper接口创建出MapperProxy的实现,这个实现就是一个动态代理

2、MyBatis组件架构

核心对象分析

Mybatis核心对象 解释
SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
根据不同的Sql创建不同的statement。
ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数
负责参数预编译设置等工作
ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
负责处理返回结果,要根据方法的不同返回值类型进行判断的
TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
负责预编译以及结果集中数据库数据与java类型的转换
MappedStatement MappedStatement维护了一条mapper.xml文件里面 select 、update、delete、insert节点的封装
每个方法的详情封装
SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql 表示动态生成的SQL语句以及相应的参数信息
Configuration MyBatis所有的配置信息都维持在Configuration对象之中

jdbc基础

//JDBC基础代码
Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select count(*) from world.city");

while (resultSet.next()) {
  long aLong = resultSet.getLong(1);
  System.out.println(aLong);
}
resultSet.close();
connection.close();

3、Mybatis初始化流程

1-解析全局配置文件信息

2-返回DefaultSqlSession

3-得到Mapper接口的动态代理对象

底层为每个mapper接口创建动态代理对象

Post Directory