一遍文章带你看懂:《MyBatis 源代码分析》
【MyBatis 执行过程】

传统的 JDBC 程式设计查询数据库的程式码和过程总结:
(一)、载入驱动。
(二)、建立连线,Connection 物件。
(三)、根据 Connection 建立 Statement 或者 PreparedStatement 来执行 sql 语句。
(四)、返回结果集到 ResultSet 中。
(五)、手动将 ResultSet 对映到 JavaBean 中。

编码方式实现 MyBatis 查询数据库,方便大家理解,不使用 SpringMybatis,加入 Spring 后整体流程会复杂很多。使用 MyBatis 后能将原来的传统的 JDBC 程式设计编的如此简单,具体流程总结:
(一)、使用配置档案构建 SqlSessionFactory。
(二)、使用 SqlSessionFactory 获得 SqlSession,SqlSession 相当于传统 JDBC 的 Conection。
(三)、使用 SqlSession 得到 Mapper。
(四)、用 Mapper 来执行 sql 语句,并返回结果直接封装到 JavaBean 中。

总结

【MyBatis 源代码分析】
下面来具体分析 MyBatis 程式码的执行过程(一)、整体架构

(二)、源代码分析
大部分框架的程式码流程:

(三)、我们的配置档案:


(四)、SqlSession 的实现流程

SqlSession 的界面定义:(里面定义了增删改查和提交回滚等方法)

接下来用 sqlSession 获取对应的 Mapper:

DefaultSqlSession 的 getMapper 实现:

MapperRegistry 里 getMapper 的最终实现:
这里就要说明一下,我们的接口里面只定义了抽象的增删改查,而这个界面并没有任何实现类,那么这个 xml 到底是如何与界面关联起来并生成实现类那?

接下来我们看看 newInstance 的具体实现:

(五)、正常流程的动态代理:

与传统的动态代理相比,MyBatis 的界面是没有实现类的,那么它又是怎么实现动态代理的那?
MapperProxy 的源代码:

MapperMethod 的定义:

进入 DefaultSqlSession 执行对应的 sql 语句:

Executor 的实现类里面执行 query 方法:

【手动实现一个简单的 MyBatis】

(一)、建立 SqlSessionFactory 例项。
(二)、例项化过程,载入配置档案建立 Configuration 物件。
(三)、通过 factory 建立 SqlSession。
(四)、通过 SqlSession 获取 mapper 界面动态代理。
(五)、动态代理回拨 SqlSession 中某查询方法。
(六)、SqlSession 将查询方法转发给 Executor。
(七)、Executor 基于 JDBC 访问数据库获取资料。
(八)、Executor 通过反射将资料转换成 POJO并返回给 SqlSession。
(九)、将资料返回给呼叫者。
专案整体使用 Maven 构建,mybatis-demo 是脱离 Spring 的 MyBatis 使用的例子。paul-mybatis 是我们自己实现的 mybatis 框架。

首先按照我们以前的使用 mybatis 程式码时的流程,建立 mapper 界面,xml 档案,和 POJO以及集一些配置档案。
1、界面:TUserMapper

2、xml 档案:

3、实体类,属性应该与数据库想匹配:

4、数据库连线配置档案、db.properties:

关注 xml 档案,mapper 档案里的 namespace,id,resultType 和 sql 语句都要储存起来,我们定义一个 POJO 来储存这些资讯。

建立一个 Configuration 类,用来储存所有配置档案和 xml 档案里的资讯:

有了配置类之后,我们可以通过这个配置类构建一个 SqlSessionFactory 了。
5、 SqlSessionFactory 抽象模版

Default 实现类主要完成了两个功能,载入配置资讯到 Configuration 物件里,实现建立 SqlSession 的功能。
package com.paul.mybatis.factory;
import com.paul.mybatis.confiuration.Configuration;
import com.paul.mybatis.confiuration.MappedStatement;
import com.paul.mybatis.sqlsession.DefaultSqlSession;
import com.paul.mybatis.sqlsession.SqlSession;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
*
* 1.初始化时就完成了 configuration 的例项化
* 2.工厂类,生成 sqlSession
*
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory{
//希望Configuration 是单例子并且唯一的
private final Configuration configuration = new Configuration();
// xml 档案存放的位置
private static final String MAPPER_CONFIG_LOCATION = "mappers";
// 数据库资讯存放的位置
private static final String DB_CONFIG_FILE = "db.properties";
public DefaultSqlSessionFactory() {
loadDBInfo();
loadMapperInfo();
}
private void loadDBInfo() {
InputStream db = this.getClass().getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
Properties p = new Properties();
try {
p.load(db);
} catch (IOException e) {
e.printStackTrace();
}
//将配置资讯写入Configuration 物件
configuration.setJdbcDriver(p.get("jdbc.driver").toString());
configuration.setJdbcUrl(p.get("jdbc.url").toString());
configuration.setJdbcUsername(p.get("jdbc.username").toString());
configuration.setJdbcPassword(p.get("jdbc.password").toString());
}
//解析并载入xml档案
private void loadMapperInfo(){
URL resources = null;
resources = this.getClass().getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
File mappers = new File(resources.getFile());
//读取资料夹下面的档案资讯
if(mappers.isDirectory()){
File[] files = mappers.listFiles();
for(File file:files){
loadMapperInfo(file);
}
}
}
private void loadMapperInfo(File file){
SAXReader reader = new SAXReader();
//通过read方法读取一个档案转换成Document 物件
Document document = null;
try {
document = reader.read(file);
} catch (DocumentException e) {
e.printStackTrace();
}
//获取根结点元素物件
Element e = document.getRootElement();
//获取名称空间namespace
String namespace = e.attribute("namespace").getData().toString();
//获取select,insert,update,delete子节点列表
List selects = e.elements("select");
List inserts = e.elements("select");
List updates = e.elements("select");
List deletes = e.elements("select");
List all = new ArrayList();
all.addAll(selects);
all.addAll(inserts);
all.addAll(updates);
all.addAll(deletes);
//遍历节点,组装成 MappedStatement 然后放入到configuration 物件中
for(Element ele:all){
MappedStatement mappedStatement = new MappedStatement();
String id = ele.attribute("id").getData().toString();
String resultType = ele.attribute("resultType").getData().toString();
String sql = ele.getData().toString();
mappedStatement.setId(namespace+"."+id);
mappedStatement.setResultType(resultType);
mappedStatement.setNamespace(namespace);
mappedStatement.setSql(sql);
configuration.getMappedStatement().put(namespace+"."+id,mappedStatement);
}
}
@Override
public SqlSession openSession() {
return new DefaultSqlSession(configuration);
}
}
在 SqlSessionFactory 里建立了 DefaultSqlSession,我们看看它的具体实现。SqlSession里面应该封装了所有数据库的具体操作和一些获取 mapper 实现类的方法。使用动态代理生成一个加强类。这里面最终还是把数据库的相关操作转给 SqlSession,使用 mapper 能使程式设计更加优雅。
SqlSession 界面,定义模版方法

Default 的 SqlSession 实现类。里面需要传入 Executor,这个 Executor 里面封装了 JDBC 操作数据库的流程。我们重点关注 getMapper 方法。

动态代理的 InvocationHandler

最后来看我们的测试类

整个专案的源代码在专案源代码,如果你觉得或多或少对你有些帮助。