MyBatis 源碼系列:MyBatis 體系結構、六大解析器

體系結構

MyBatis是一個持久層框架,其體系結構分爲三層:基礎支持層、核心處理層和接口層。

基礎支持層包括數據源模塊、事務管理模塊、緩存模塊、Binding模塊、反射模塊、類型轉換模塊、日誌模塊、資源加載模塊和解析器模塊。這些模塊爲MyBatis提供了基礎功能,爲核心處理層提供了良好的支撐。

核心處理層包括配置解析、參數映射、SQL解析、SQL執行、結果集映射和插件。這些組件共同完成了MyBatis的核心處理邏輯,包括將SQL語句與輸入參數進行映射,解析SQL語句,執行SQL語句,並將結果映射回Java對象等。

接口層是MyBatis與上層應用交互的橋樑,包括SqlSession接口。SqlSession是MyBatis中最重要的接口之一,它定義了MyBatis保留給應用的API,用於執行各種數據庫操作,如查詢、插入、更新和刪除等。

在MyBatis的體系結構中,還有一個重要的組成部分是全局配置文件。全局配置文件包含了MyBatis的運行環境等信息,通過解析全局配置文件,MyBatis可以完成基本配置的初始化,並根據配置信息實例化相應的組件。

總的來說,MyBatis的體系結構是一個層次分明、模塊化的結構,各層之間相互依賴、協同工作,共同完成了MyBatis的核心功能和操作數據庫的邏輯。通過這種結構,MyBatis能夠有效地封裝底層的JDBC操作,讓用戶專注於SQL語句的編寫和數據庫操作,提高了開發效率和代碼的可維護性。

image

六大解析器

MyBatis的六大解析器Builder分別是:

  1. XMLConfigBuilder:用於構建 MyBatis 的配置信息,包括數據源、事務管理器等信息。
  2. XMLMapperBuilder:用於構建 Mapper 的 XML 配置文件。該 Builder 讀取 Mapper 的 XML 文件,將其解析爲 SqlSessionFactory 的一個實例,以便在後續的操作中使用。
  3. MapperBuilderAssistant:用於協助構建 Mapper 的 XML 配置文件。該 Builder 提供了一系列方法,用於簡化 Mapper XML 文件的構建過程。
  4. XMLStatementBuilder:用於構建 SQL 語句的 Builder。該 Builder 負責解析 SQL 語句,並將其轉換爲 MyBatis 可以執行的內部表示形式。
  5. XMLScriptBuilder:用於構建 XML 腳本的 Builder。該 Builder 提供了一些方法,用於構建執行 DDL(數據定義語言)操作的 XML 腳本,例如創建表、視圖等。
  6. SqlSourceBuilder:用於構建 SQL 語句的源代碼。該 Builder 讀取 MyBatis 的映射文件中的 <sql> 標籤,將其解析爲 SqlSource 對象,以便在執行 SQL 語句時使用。

image

注意:最終解析的都會存入 Configuration 對象中

package org.apache.ibatis.session;

/**
 * @author Clinton Begin
 */
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;
  protected boolean shrinkWhitespacesInSql;
  protected boolean nullableOnForEach;
  protected boolean argNameBasedConstructorAutoMapping;

  ...
}

image

全部代碼

SQL 腳本

create database test;
create table test.user
(
    id          int auto_increment
        primary key,
    user_name   varchar(50) not null,
    create_time datetime    not null
);

mybaits-cofig.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 resource="db.properties"></properties>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <plugins>
        <plugin interceptor="com.mcode.plugin.ExamplePlugin" ></plugin>
    </plugins>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!--//  mybatis內置了JNDI、POOLED、UNPOOLED三種類型的數據源,其中POOLED對應的實現爲org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自帶實現的一個同步、線程安全的數據庫連接池 一般在生產中,我們會使用c3p0或者druid連接池-->
            <dataSource type="POOLED">
            <property name="driver" value="${mysql.driverClass}"/>
            <property name="url" value="${mysql.jdbcUrl}"/>
            <property name="username" value="${mysql.user}"/>
            <property name="password" value="${mysql.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--1.必須保證接口名(例如IUserDao)和xml名(IUserDao.xml)相同,還必須在同一個包中-->
        <package name="com.mcode.mapper"/>

        <!--2.不用保證同接口同包同名
         <mapper resource="com/mybatis/mappers/EmployeeMapper.xml"/>

        3.保證接口名(例如IUserDao)和xml名(IUserDao.xml)相同,還必須在同一個包中
        <mapper class="com.mybatis.dao.EmployeeMapper"/>

        4.不推薦:引用網路路徑或者磁盤路徑下的sql映射文件 file:///var/mappers/AuthorMapper.xml
         <mapper url="file:E:/Study/myeclipse/_03_Test/src/cn/sdut/pojo/PersonMapper.xml"/>-->
    </mappers>
</configuration>

db.properties

mysql.driverClass=com.mysql.cj.jdbc.Driver
mysql.jdbcUrl=jdbc:mysql://127.0.0.1/test?characterEncoding=utf8
mysql.user= root
mysql.password= 123456

UserMapper.xml

<?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.mcode.mapper.UserMapper">
    <cache ></cache>
    <!-- Mybatis 是如何將 sql 執行結果封裝爲目標對象並返回的?都有哪些映射形式?-->
    <resultMap id="result" type="com.mcode.entity.User">
        <id column="id" jdbcType="INTEGER" property="id"/>
        <result column="user_name" jdbcType="VARCHAR" property="userName"/>
        <result column="create_time" jdbcType="DATE" property="createTime"/>
        <!--<collection property="" select=""-->

    </resultMap>
    <!--namespace根據自己需要創建的的mapper的路徑和名稱填寫-->
    <select id="selectById" resultMap="result">
        select * from user
        <where>
            <if test="id > 0">
                and id = #{id}
            </if>
        </where>
    </select>
</mapper>

User.java

package com.mcode.entity;

import java.time.LocalDateTime;

/**
 * ClassName: User
 * Package: com.mcode.entity
 * Description:
 *
 * @Author: robin
 * @Version: v1.0
 */
public class User {
    private int id;
    private String userName;

    private LocalDateTime createTime;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }
}

UserMapper.java

package com.mcode.entity;

import java.time.LocalDateTime;

/**
 * ClassName: User
 * Package: com.mcode.entity
 * Description:
 *
 * @Author: robin
 * @Version: v1.0
 */
public class User {
    private int id;
    private String userName;

    private LocalDateTime createTime;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }
}

ExamplePlugin.java

package com.mcode.plugin;

/**
 * ClassName: ExamplePlugin
 * Package: com.mcode.plugin
 * Description:
 *
 * @Author: robin
 * @Version: v1.0
 */

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;


@Intercepts({@Signature( type= Executor.class,  method = "query", args ={
        MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
})})
public class ExamplePlugin implements Interceptor {

    //  分頁   讀寫分離    Select  增刪改

    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("代理");
        Object[] args = invocation.getArgs();
        MappedStatement ms= (MappedStatement) args[0];
        // 執行下一個攔截器、直到盡頭
        return invocation.proceed();
    }

}

App.java

package com.mcode;

import com.mcode.entity.User;
import com.mcode.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.Reader;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        String resource = "mybatis-config.xml";
        Reader reader;
        try {
            //將XML配置文件構建爲Configuration配置類
            reader = Resources.getResourceAsReader(resource);
            // 通過加載配置文件流構建一個SqlSessionFactory  DefaultSqlSessionFactory
            SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
            // 數據源 執行器  DefaultSqlSession
            SqlSession session = sqlMapper.openSession();
            sqlMapper.getConfiguration();
            try {
                UserMapper mapper = session.getMapper(UserMapper.class);
                System.out.println(mapper.getClass());
                User user = mapper.selectById(1);
                System.out.println(user.getUserName());
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                session.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章