上篇文章我们介绍了将JDBC源码改造成简单的Mybatis V1版本,这个V1版本的目的是打开我们手写Mybatis源码的思路。接下来,这篇文章中我们要来分析并手写Mybatis V2版本的源码了。
将Mybatis V1版本升级成 Mybatis V2 版本
1、将 properties配置文件升级成 xml文件
2、按照 Mybatis的用法,将 xml配置文件分成 全局配置文件和映射文件
3、对 xml配置文件进行解析
解析Mybatis的全局配置文件和映射文件
1、全局配置文件
全局配置文件包含了Mybatis所有的配置信息,包括全局配置文件信息和映射文件信息,我们来看一下我们的全局配置文件都有哪些东西(这里的配置文件指的是我们按照Mybatis的架构自己写的简单版的配置文件,映射文件也一样)。
从图中我们可以看出,Mybatis全局配置文件有一个根标签,这个标签在被解析后会被封装到一个Configuration对象中,这个Configuration对象就包含了Mybatis的全部配置信息,包括全局配置信息和映射信息。
2、映射文件
映射文件中包含的是sql语句相关的信息,这里可以将映射文件分成几个大块,我们用不同颜色的方框给标示出来,下面我们对每个部位进行一一讲解:
1、黑色框 表示的是整个mapper映射文件,根标签是mapper,他有一个很重要的属性 namespace,它是 statementId的重要组成部分。
2、红色框 表示的是 、、、 标签,他们表示的是 SQL的 CRUD相关的信息,每个 CRUD标签都是一个 MappedStatement对象。
以标签为例,它封装了 SQL语句,入参类型,映射结果类型,还有statement的类型。也就是标签的内容和属性。
3、橙色框 表示的是 sqlSource,其中封装的SQL源信息,并且还可以对 #{} 和 ${} 进行解析,每个 statement对应一个 sqlSource,而每个sqlSource对应一颗 sqlNode树。
4、紫色框 表示 sqlNode,它封装了SQL的节点信息。sqlNode之间可以平行,也可以相互嵌套,这样多个sqlNode节点,就组成了一颗sqlNode树,也就是组成了一个sqlSource。
5、注意:namespace和statement标签的id属性共同组成了statementId,这是MappedStatement的唯一标识。
开始手写 Mybatis-V2版本源码
1、创建Configuration类
根据前面对全局配置文件的分析,我们可以来创建Configuration类了,Configuration类中两个重要的属性:
- 封装了数据库信息的 DataSource数据源,
- 封装了 映射文件的 Map集合
package com.mybatis.v2.config;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @author :白银杰
* @version :
* @date :Created in 2020/12/7 14:43
* @description :封装了 Mybatis中 xml文件的所有配置信息
* @modified by :
*/
public class Configuration {
/**
* 封装了数据源相关的信息
*/
private DataSource dataSource;
/**
* 封装了所有statement标签的信息
* key 是 namespace + statement标签的id属性组成的 statementId
* value 是 MappedStatement对象
*/
private Map<String, MappedStatement> mappedStatements = new HashMap<String, MappedStatement>();
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 通过 MappedStatementId获取 MappedStatement对象
*
* @param mappedStatementId
* @return
*/
public MappedStatement getMappedStatementById(String mappedStatementId) {
return mappedStatements.get(mappedStatementId);
}
/**
* 向map集合中添加 statementId 和 statement对象
*
* @param mappedStatementId namespace + statement的id属性 = statementId
* @param mappedStatement statement
*/
public void setMappedStatement(String mappedStatementId, MappedStatement mappedStatement) {
this.mappedStatements.put(mappedStatementId, mappedStatement);
}
}
在这里,我们用到了 MappedStatement这个类,这个类标示的就是我们映射文件中是 statement标签(CRUD标签),这里封装了Mybatis映射文件中 statement标签的全部信息。
package com.mybatis.v2.config;
import com.mybatis.v2.sqlsource.SqlSource;
/**
* @author :白银杰
* @version :
* @date :Created in 2020/12/7 15:03
* @description :
* @modified by :
*/
public class MappedStatement {
private String statementId;
private String resultType;
private Class resultTypeClass;
private String statementType;
private SqlSource sqlSource;
/**
* @param statementId MappedStatement的唯一标识,由 namespace + StatementId 组成
* @param resultType 返回结果类型
* @param resultTypeClass
* @param statementType statement类型,包括 statement、PreparedStatement、CallableStatement等
* @param sqlSource sql源信息,包括对#{} 和 ${} 的解析,是个接口
*/
public MappedStatement(String statementId, String resultType,
Class resultTypeClass, String statementType, SqlSource sqlSource) {
this.statementId = statementId;
this.resultType = resultType;
this.resultTypeClass = resultTypeClass;
this.statementType = statementType;
this.sqlSource = sqlSource;
}
public String getStatementId() {
return statementId;
}
public void setStatementId(String statementId) {
this.statementId = statementId;
}
public String getResultType() {
return resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public Class getResultTypeClass() {
return resultTypeClass;
}
public void setResultTypeClass(Class resultTypeClass) {
this.resultTypeClass = resultTypeClass;
}
public String getStatementType() {
return statementType;
}
public void setStatementType(String statementType) {
this.statementType = statementType;
}
public SqlSource getSqlSource() {
return sqlSource;
}
public void setSqlSource(SqlSource sqlSource) {
this.sqlSource = sqlSource;
}
}
而在MappedStatement类中,我们又用到了sqlSource接口,那么我们再去创建一个sqlSource接口:
package com.mybatis.v2.sqlsource;
/**
* @author :白银杰
* @date :Created in 2020/12/7 15:26
* @description: 这是一个接口,每个statement都表示一个SQLSource,
* 每个SQLSource都表示一颗sqlNode树,
* 每颗sqlNode树都由多个sqlNode节点组成
* @modified by:
* @version:
*/
public