mybatis详细流程原理
---------------------- 第一阶段XML的解析 ----------------------------------------------------
第一部分:项目结构
user_info表:没什么好说的就3个字段

User实体类:

mapper:UserMapper 为根据id查询用户信息

UserMapper.xml

mybaitis的主配置文件:

数据库连接的属性文件:

测试类:

结构图:

第二部分:mybatis重要组件和运行流程图
-
Configuration: MyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中
-
SqlSession: 作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
-
Executor: MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
-
StatementHandler: 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等
-
ParameterHandler: 负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
-
ResultSetHandler: 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
-
TypeHandler: 负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
-
MappedStatement: MappedStatement维护一条<select|update|delete|insert>节点的封装
-
SqlSource: 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
-
BoundSql: 表示动态生成的SQL语句以及相应的参数信息

第三部分:初始化源码分析
@Component
public class TestMybatis implements CommandLineRunner{
@Override
public void run(String... args) throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
Object o = session.selectOne("com.wsdsg.spring.boot.analyze.mapper.UserMapper.selectUserById", 1);
} finally {
session.close();
}
}
}
这是mybatis操作数据库最基本的步骤,前两行代码没什么好说的就是资源加载mybatis的主配置文件获取输入流对象,我们看第三行代码
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
这行代码的意思就是根据mybatis-config.xml(主配置文件)的流对象构建一个会话工厂对象。而且还用到了建造者模式--->大致意思就是要创建某个对象不直接new这个对象而是利用其它的类来创建这个对象。mybatis的所有初始化工作都是这行代码完成,那么我们进去一探究竟
第一步:进入build方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// SqlSessionFactory 会创建 XMLConfigBuilder对象 来解析 mybatis-config.xml 配置文件
// XMLConfigBuilder 继承自 BaseBuilder抽象类,顾名思义这一系的类使用了 建造者设计模式
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析配置文件的内容 到 Configuration对象,根据 Configuration对象
// 创建 DefaultSqlSessionFactory对象,然后返回
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
}
}
}
可以看到会创建一个XMLConfigBuilder解析器对象,这个对象的作用就是解析mybatis-config.xml(主配置文件)用的。先说明一下,我们可以看出主配置文件的最外层节点是
第二步:进入 XMLConfigBuilder.parse方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 在 mybatis-config.xml配置文件 中查找 <configuration>节点,并开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
XMLConfigBuilder维护一个parsed属性默认为false,这个方法一开始就判断这个主配置文件是否已经被解析,如果解析过了就抛异常
第三步:进入parseConfiguration(...)方法
private void parseConfiguration(XNode root) {
try {
// 根据 root.evalNode("properties") 中的值就可以知道具体是解析哪个标签的方法咯
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
我们可以看出这个方法是对configuration的所有子标签挨个解析。比如常在配置文件中出现的settings属性配置,在settings会配置缓存,日志之类的。
还有typeAliases是配置别名。environments是配置数据库链接和事务。这些子节点会被一个个解析并且把解析后的数据封装在
Configuration 这个类中,可以看第二步方法的返回值就是Configuration对象。在这里我们重点分析的解析mappers这个子标签,
这个标签里面还会有一个个的mapper标签去映射mapper所对应的xxMapper.xml,可以回头看看主配置文件
3.1 解析typeHandlers标签
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
// 处理 <typeHandlers> 下的所有子标签
for (XNode child : parent.getChildren()) {
// 处理 <package> 标签
if ("package".equals(child.getName())) {
// 获取指定的包名
String typeHandlerPackage = child.getStringAttribute("name");
// 通过 typeHandlerRegistry 的 register(packageName)方法
// 扫描指定包中的所有 TypeHandler类,并进行注册
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// Java数据类型
String javaTypeName = child.getStringAttribute("javaType");
// JDBC数据类型
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
// 注册
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
3.2 解析environments标签
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
// 如果未指定 XMLConfigBuilder 的 environment字段,则使用 default属性 指定的 <environment>环境
if (environment == null) {
environment = context.getStringAttribute("default");
}
// 遍历 <environment>节点
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// 实例化 TransactionFactory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 创建 DataSourceFactory 和 DataSource
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// 创建的 Environment对象 中封装了上面的 TransactionFactory对象 和 DataSource对象
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 将 environment属性值添加到 configuration 中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
第四步:进入 XMLConfigBuilder.mapperElement方法解析mappers标签
Mybatis 初始化时,除了解析 mybatis-config.xml 主配置文件,还会解析全部的xxMapper.xml映射配置文件,
mybatis-config.xml 文件的
//解析 <mappers>节点,本方法会创建 XMLMapperBuilder对象 加载映射文件,如果映射配置文件存在
//相应的 Mapper接口,也会加载相应的 Mapper接口,解析其中的注解 并完成向 MapperRegistry 的注册
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 处理 <mappers> 的子节点
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 获取 <package>子节点 中的包名
String mapperPackage = child.getStringAttribute("name");
// 扫描指定的包目录,然后向 MapperRegistry 注册 Mapper接口
configuration.addMappers(mapperPackage);
} else {
// 获取 <mapper>节点 的 resource、url、mapperClass属性,这三个属性互斥,只能有一个不为空
// Mybatis 提供了通过包名、映射文件路径、类全名、URL 四种方式引入映射器。
// 映射器由一个接口和一个 XML配置文件 组成,XML文件 中定义了一个 命名空间namespace,
// 它的值就是接口对应的全路径。
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// 如果 <mapper>节点 指定了 resource 或是 url属性,则创建 XMLMapperBuilder对象 解析
// resource 或是 url属性 指定的 Mapper配置文件
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 如果 <mapper>节点 指定了 class属性,则向 MapperRegistry 注册 该Mapper接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

(1)这个方法一开始是一个循环,为什么要循环呢?因为一个mappers节点下面可能会有很多mapper节点。在应用中肯定不止一个mapper.xml。所以他会去遍历每一个mapper节点去解析该节点所映射的xml文件。
(2)循环下面是一个if..else判断。它先判断mappers下面的子节点是不是package节点。因为在实际开发中有很多的xml文件,不可能每一个xml文件都用一个mapper节点去映射,我们干脆会用一个package节点去映射一个包下面的所有的xml,这是多文件映射。
(3)如果不是package节点那肯定就是mapper节点做单文件映射。我们看下面的三行代码,发现单文件映射有3种方式-->1 第一种使用mapper节点的resource属性直接映射xml文件。2 第二种是使用mapper节点url属性映射磁盘内的某个xml文件。
3 第三种是使用mapper节点的class属性直接映射某个mapper接口。可以回头看看我的主配置文件的mappers节点。
(4)实际上映射xml的方式看源码可以得出有4种方式,我们先看单文件映射的resource方式,因为这种方式理解了其他三种方式就比较好理解了。
第五步:看resource方式解析xml
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
(1)第一行代码的意思是实例化一个错误上下文对象。这个对象是干什么用的呢?我们回忆一下我们使用mybatis的过程中如果出现错误会不会提示这个错误在哪个xml中,还提示这个错误在xml中的哪个sql中。这个对象的作用就是把错误信息封装起来,
如果出现错误就会调用这个对象的toString方法,感兴趣的可以去看看这个对象的源码。这个resource参数就是String类型的xml的名字,在我们的项目中是UserMapper.xml
(2) 第二行没什么好说的就是读取这个xml获取输入流对象
(3)然后创建一个mapper的xml文件解析器,你看他们这名字起的,一看就知道是什么意思。就好比那个XMLConfigBuilder一看就是解析主配置文件用的
第六步:进入XMLMapperBuilder.parse()方法:
public void parse() {
// 是否已经加载过该配置文件
if (!configuration.isResourceLoaded(resource)) {
// 解析 <mapper>节点
configurationElement(parser.evalNode("/mapper"));
// 将 resource 添加到 configuration 的 loadedResources属性 中,
// 该属性是一个 HashSet<String>类型的集合,其中记录了已经加载过的映射文件
configuration.addLoadedResource(resource);
// 注册 Mapper接口
bindMapperForNamespace();
}
// 处理 configurationElement()方法 中解析失败的 <resultMap>节点
parsePendingResultMaps();
// 处理 configurationElement()方法 中解析失败的 <cacheRef>节点
parsePendingCacheRefs();
// 处理 configurationElement()方法 中解析失败的 <statement>节点
parsePendingStatements();
}
(1)一开始就一个判断这个xml是否被解析过了。因为onfiguration对象会维护一个String类型的set集合loadedResources,这个集合中存放了所有已经被解析过的xml的名字,我们在这里是没有被解析的,所以进入if中
第七步:进入 XMLMapperBuilder.configurationElement方法
private void configurationElement(XNode context) {
try {
// 获取 <mapper>节点 的 namespace属性
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 使用 MapperBuilderAssistant对象 的 currentNamespace属性 记录 namespace命名空间
builderAssistant.setCurrentNamespace(namespace);
// 解析 <cache-ref>节点,后面的解析方法 也都见名知意
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache")); // 开启二级缓存的标签
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
二级缓存的属性设置
private void cacheElement(XNode context) {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
二级缓存的对象创建
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache; // 将二级缓存对象设置到 currentCache 属性中
return cache;
}
这个方法就是解析一个mapper.xml所有节点数据。比如解析namespace,resultMap等等。重点是最后一句
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
我们进入这个 XMLMapperBuilder.buildStatementFromContext 方法中
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
没什么好说的,继续进入buildStatementFromContext()遍历XML节点一个一个获取
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
(1)这个方法一开始是一个循环,遍历一个list,这个list里装的是xml中的所有sql节点,比如select、insert、update、delete ,每一个sql是一个节点。循环解析每一个sql节点
(2)创建一个xml的会话解析器去解析每个节点
第八步:进入XMLStatementBuilder.parseStatementNode方法
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect); // 开启二级缓存
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 这里做SQL处理,将 select * from user where id = #{id} 转为 select * from user where id = ?
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
看到这个方法很长,其实大致意思就是解析这个sql标签里的所有数据,并把所有数据通过addMappedStatement这个方法封装在MappedStatement这个对象中,
这个对象中封装了一条sql所在标签的所有内容,比如这个sql标签的id ,sql语句,入参,出参,等等。
<select id="findById" resultType="com.atguigu.mybatis.entity.User" parameterType="java.lang.Integer">
select * from user where id = #{id}
</select>
我们要牢记一个sql的标签对应一个MappedStatement对象
XMLScriptBuilder#parseScriptNode用于解析动态sql:
public SqlSource parseScriptNode() {
// 解析select\insert\ update\delete标签中的SQL语句,最终将解析到的SqlNode封装到MixedSqlNode中的List集合中
// ****将带有${}号的SQL信息封装到TextSqlNode
// ****将带有#{}号的SQL信息封装到StaticTextSqlNode
// ****将动态SQL标签中的SQL信息分别封装到不同的SqlNode中
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
// 如果SQL中包含${}和动态SQL语句,则将SqlNode封装到DynamicSqlSource
// 最终结果是:select id from blog where id = ${id}
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
// 如果SQL中包含#{},则将SqlNode封装到RawSqlSource中,并指定parameterType
// 最终的结果是:select id from blog where id = ?
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
parseDynamicTags解析sql语句,
判断具体走new DynamicSqlSource(configuration, rootSqlNode) 还是走new RawSqlSource(configuration, rootSqlNode, parameterType) ?
解析select\insert\ update\delete标签中的SQL语句,最终将解析到的SqlNode封装到MixedSqlNode中的List集合中
// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<>();
//获取<select>\<insert>\<update>\<delete>4个标签的子节点,子节点包括元素节点和文本节点
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
// 获取标签内的原始自定义sql:select * from blog where id = #{id}
XNode child = node.newXNode(children.item(i));
// 处理文本节点
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
String data = child.getStringBody("");
// 将文本内容封装到SqlNode中,还是原始sql
TextSqlNode textSqlNode = new TextSqlNode(data);
// SQL语句中带有${}的话,就表示是dynamic的
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
// SQL语句中(除了${}和下面的动态SQL标签),就表示是static的
// StaticTextSqlNode的apply只是进行字符串的追加操作
contents.add(new StaticTextSqlNode(data));
}
//处理元素节点
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
// 动态SQL标签处理器
// 策略模式
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
// 动态SQL标签是dynamic的
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
如果SQL中包含${}和动态SQL语句,则走以下代码:sqlSource = new DynamicSqlSource(configuration, rootSqlNode)
将SqlNode封装到DynamicSqlSource中,最终返回结果是:select id from blog where id = ${id},但要将 select id from blog where id = ${id}转为
select id from blog where id = 1,
再第三阶段openSession.selectOne("findById", 1)的第四步:获取BoundSql对象,BoundSql boundSql = sqlSource.getBoundSql(parameterObject),此时的sqlSource为DynamicSqlSource
如果SQL中包含#{},则走以下代码:sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType)
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
// 解析SQL语句
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
// 获取入参类型
Class<?> clazz = parameterType == null ? Object.class : parameterType;
// 开始解析
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
SqlSourceBuilder#parse方法解析sql
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
// 创建分词解析器
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
// 解析#{},最终sql解析为select * from blog where id = ?
String sql = parser.parse(originalSql);
// 将解析之后的SQL信息,封装到StaticSqlSource对象中
// SQL字符串是带有?号的字符串,?相关的参数信息,封装到ParameterMapping集合中
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
第九步:进入MapperBuilderAssistant.addMappedStatement方法
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache, // 使用二级缓存,在<select>标签中设置useCache属性,默认true
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect)) // 使用二级缓存,在<select>标签中设置useCache属性,默认true
.cache(currentCache); // 将二级缓存保存到MappedStatement对象中
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
}
乍一看这个方法很长,我们只看最后三行代码:
(1) MappedStatement statement = statementBuilder.build();通过解析出的参数构建了一个MapperStatement对象。
(2)configuration.addMappedStatement(statement); 这行是把解析出来的MapperStatement装到Configuration维护的Map集合中。
key值是这个sql标签的id值,我们这里应该就是selectUserById,value值就是我们解析出来的MapperStatement对象。
其实我们解析xml的目的就是把每个xml中的每个增删改查的SQL语句标签解析成一个个MapperStatement并把解析出来的这些对象装到Configuration的Map中备用
第十步: 返回第六步的代码:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
刚才到第九步都是在执行configurationElement(parser.evalNode("/mapper"));这行代码,接下来看下一行代码configuration.addLoadedResource(resource);
到第九步的时候我们已经把一个xml完全解析完了,所以在此就会把这个解析完的xml的名字装到configuration中
第十一步:进入bindMapperForNamespace()方法
bindMapperForNamespace()方法作用:将每个xxMapper.xml【映射配置文件】的命名空间与对应 Mapper 接口的绑定,并注册到 MapperRegistry 中
private void bindMapperForNamespace() {
// 获取映射配置文件的命名空间
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
// 解析命名空间对应的类型
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
}
if (boundType != null) {
// 是否已加载 boundType接口
if (!configuration.hasMapper(boundType)) {
// 追加个 "namespace:" 的前缀,并添加到 Configuration 的 loadedResources集合中
configuration.addLoadedResource("namespace:" + namespace);
// 添加到 Configuration的mapperRegistry集合中
configuration.addMapper(boundType);
}
}
}
}
(1)一开始获取名称空间,名称空间一般都是我们mapper的全限定名,通过它反射获取这个Mapper接口的代理对象
(2)if判断,Configuration中也维护了一个knownMappers对象,
key值是由namespace通过条件反射生成的mapper的class对象,value是由namespace产生的对应的mapper的MapperProxyFactory代理对象工厂
第十二步:进入addMapper()方法
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
我们可以看出mapperRegistry这个类维护的Map的名字是knownMappers---->(已知的mapper--->就是注册过的mapper).
我们看他的put,key是由namespace命名空间生成的class对象,value是MapperProxy的代理工厂new MapperProxyFactory<>(type)
到此mybatis根据主配置文件初始化就完成了,那说了这么久到底做了什么呢?我们总结一下:
1、总的来说就是解析主配置文件(mybatis-configuration.xml)把主配置文件里的所有信息包括映射文件信息(xxMapper.xml)封装到Configuration这个对象中
2、稍微细一点就是 通过XmlConfigBuilder解析主配置文件,然后通过XmlMapperBuild解析mappers下映射的所有xml文件(循环解析)。
把每个xml中的各个sql解析成一个个MapperStatement对象装在Configuration维护的一个名为mappedStatements的Map集合中(key值是sql标签的id值,value是mapperstatement对象)
----->每个xxMapper.xml【映射配置文件】的命名空间与对应 Mapper 接口的绑定是维护在Configuration对象 -->
mapperRegistry-->knownMappers的Map集合中(key是:namespace命名空间的class对象,value是:namespace命名空间对应的代理工厂)
---------------------- 第二阶段SqlSession的创建 ----------------------------------------------------
第一步:获取session会话对象源码分析
SqlSession session = sqlSessionFactory.openSession();
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取配置的Environment对象
final Environment environment = configuration.getEnvironment();
// 从environment中获取TransactionFactory对象,如果没有,就创建一个ManagedTransactionFactory实例并返回
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 从事务工厂中获取一个事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据事务对象tx和配置的Executor类型execType创建Executor实例
// ExecutorType是个枚举类型,有三个值 SIMPLE, REUSE, BATCH,分别对应了SimpleExecutor、ReuseExecutor、BatchExecutor
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession对象
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
我们看第二段代码:因为我们解析主配置文件把所有的节点信息都保存在了configuration对象中,它开始直接或得Environment节点的信息,这个节点配置了数据库连接和事务。
之后通过Environment创建了一个事务工厂,然后通过事务工厂实例化了一个事务对象。 ------> 最后他创建了一个执行器Executor ,
我们知道session是与数据库交互的顶层api,session中会维护一个Executor 来负责sql生产和执行和查询缓存等。我们再来看看new这个执行器的时候的过程
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
-
SimpleExecutor: 简单执行器,是 MyBatis 中默认使用的执行器,每次执行 SQL 都会创建一个新的 Statement 对象,执行完毕后立即关闭
-
ReuseExecutor: 可重用执行器,缓存同一个 SQL 对应的 Statement 对象,避免重复创建
-
BatchExecutor: 批处理执行器,将多个 UPDATE/INSERT/DELETE 操作添加addBatch() 缓存中,调用 flushStatements() 时一次性提交
public SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } protected BaseExecutor(Configuration configuration, Transaction transaction) { this.transaction = transaction; // 事务 this.deferredLoads = new ConcurrentLinkedQueue<>(); this.localCache = new PerpetualCache("LocalCache"); // 一级缓存 this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache"); this.closed = false; this.configuration = configuration; this.wrapper = this; } executor = new CachingExecutor(executor); public CachingExecutor(Executor delegate) { this.delegate = delegate; // SimpleExecutor执行器 delegate.setExecutorWrapper(this); } // 创建DefaultSqlSession对象 new DefaultSqlSession(configuration, executor, autoCommit); public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; // CachingExecutor执行器 this.dirty = false; this.autoCommit = autoCommit; }
创建一个DefaultSqlSession,这里面维护了Configuration和Executor
---------------------- 第三阶段openSession.selectOne("findById", 1)解析 ----------------------------------------------------
第一步:进入selectOne()方法
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
第二步:进入selectList()方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 从configuration中取出封装了sql标签信息的MapperStatement对象
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
第三步:进入CachingExcutor缓存执行器的query方法
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取BoundSql对象
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建CacheKey对象,该对象由多个参数组装而成
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
// query方法的重载,进行后续处理
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
第四步:获取BoundSql对象
public BoundSql getBoundSql(Object parameterObject) {
// 获取组装完成的sql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
第五步:执行DynamicSqlSource的getBoundSql方法
public BoundSql getBoundSql(Object parameterObject) {
// 将参数封装成动态上下文,DynamicContext中sqlBuilder就是最后组装的sql
DynamicContext context = new DynamicContext(this.configuration, parameterObject);
// 使用了责任链模式,根据条件,动态组装sql
this.rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
// 将#{参数}替换为?进行占位
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
Map var10000 = context.getBindings();
Objects.requireNonNull(boundSql);
var10000.forEach(boundSql::setAdditionalParameter);
return boundSql;
}
第六步:返回第三步,创建CacheKey对象
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 可以看到CacheKey对象由MappedStatement的id、RowBounds的offset和limit
// sql语句(包含占位符"?")、用户传递的实参组成
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// 获取用户传入的实参,并添加到CacheKey对象中
for (ParameterMapping parameterMapping : parameterMappings) {
// 过滤掉输出类型的参数
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 将实参添加到CacheKey对象中
cacheKey.update(value);
}
}
// 如果configuration的environment不为空,则将该environment的id
// 添加到CacheKey对象中
if (configuration.getEnvironment() != null) {
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
第七步:返回第三步,调用CachingExecutor#query()方法
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache(); // 真正开启二级缓存的开关
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) { // 使用二级缓存,在<select>标签中设置useCache属性,默认true
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key); // 从二级缓存中获取数据
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // 将数据加入二级缓存中
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
delegate在以下地方赋值
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor); // 赋值delegate
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
public CachingExecutor(Executor delegate) {
this.delegate = delegate; // 赋值delegate
delegate.setExecutorWrapper(this);
}
我们点进去发现是进入的Executor接口,不慌,找他的实现类,它先走的是CachingExcutor缓存执行器,我们研究一下代码,
我们看第二段代码他一开始从MapperStatement中获取BoundSql 这个对象,因为真正的sql语句封装在这个对象中,
而且这个对象也负责把sql中的占位符替换成我们传的参数,只是MapperStatement维护了BoundSql 的引用而已
第八步:delegate.query:则是调用了SimpleExcutor简单执行器#query()方法,因为SimpleExcutor的引用维护到CachingExcutor中
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// 检查当前Executor是否已关闭
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
// 非嵌套查询,且<select>节点配置的flushCache属性为true时,才会清空一级缓存
clearLocalCache();
}
List<E> list;
try {
// 增加查询层数
queryStack++;
// 根据传入的CacheKey对象 查询一级缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 在一级缓存命中时,获取缓存中保存的输出类型参数
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 缓存未命中,则从数据库查询结果集,其中会调用doQuery()方法完成数据库查询操作
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
// 当前查询完成,查询层数减少
queryStack--;
}
if (queryStack == 0) {
// 延迟加载的相关内容
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
一开始声明了一个集合list,然后通过我们之前创建的缓存key去一级缓存localCache中查询是否有缓存,下面判断,如果集合不是null就处理一下缓存数据直接返回list,
如果没有缓存,他回从数据库中查,你看他们这名字起的一看就知道是什么意思queryFromDatabase,
我们现在执行的是第一条selectOne,没有缓存我们进入queryFromDatabase方法
第九步:进入queryFromDatabase方法,将查询的数据放入localCache【一级缓存中】
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
你看这段代码,先在本地缓存中占个位,然后执行doQuery从数据库中查数据,然后移除刚才的缓存中的占位,最后把查出来的数据put进本地缓存中,
我不知道他这个占位又移除到底想搞什么幺蛾子,反正我们明白,那不重要,重要的是他执行了doQuery从数据库中查到数据并放入缓存中,
我们接着看一下doQuery这个方法的代码
第十步:调用SimpleExecutor执行器的doQuery方法
SimpleExecutor 继承了 BaseExecutor 抽象类,它是最简单的 Executor 接口实现。Executor 组件使用了模板方法模式,
一级缓存等固定不变的操作都封装到了 BaseExecutor 中,在 SimpleExecutor 中就不必再关心一级缓存等操作,只需要专注实现 4 个基本方法的实现即可
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
// 获取配置对象
Configuration configuration = ms.getConfiguration();
// 创建StatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 完成Statement的创建和初始化,该方法首先会调用StatementHandler的prepare()方法
// 创建Statement对象,然后调用StatementHandler的parameterize()方法处理占位符
stmt = prepareStatement(handler, ms.getStatementLog());
// 调用StatementHandler的query()方法,执行sql语句,并通过ResultSetHandler
// 完成结果集的映射
return handler.query(stmt, resultHandler);
} finally {
// 关闭Statement对象
closeStatement(stmt);
}
}

public interface StatementHandler {
//预编译处理器,声明Statement,通过JDBC中的Connection声明
Statement prepare(Connection connection) throws SQLException;
//绑定参数
void parameterize(Statement statement) throws SQLException;
//批量操作
void batch(Statement statement) throws SQLException;
//更新操作
int update(Statement statement) throws SQLException;
//查询操作
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
//获取当前操作的动态SQL
BoundSql getBoundSql();
//获取当前操作的参数处理器
ParameterHandler getParameterHandler();
}
StatementHandler接口的实现类有三个分别是:
-
SimpleStatementHandler:创建普通的Statement简单处理器,执行静态SQL
-
PreparedStatementHandler:创建PrepareStatement预编译处理器,可以进行预处理,设置预编译参数,防止SQL注入
-
CallableStatementHandler:创建CallabelStatement存储过程处理器,是用来执行存储过程的,设置出参和读取出参

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
// 构造参数
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 核心配置对象
this.configuration = mappedStatement.getConfiguration();
// 记录执行SQL语句的Executor对象
this.executor = executor;
// 记录SQL语句信息的MappedStatement对象
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) {
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
// 记录SQL语句对应的BoundSql对象
this.boundSql = boundSql;
// 创建参数处理器
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
// 创建结果集处理器
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
StatementHandler其操作流程如下:
1、使用Configuration.newStatementHandler()创建 ,主要逻辑是基于MappedStatement的sql语句配置的StatementType(statement、prepared、callable)创建对应类型的StatementHandler
2、根据不同类型的StatementHandler,创建不同类型的Statement(PreparedStatement、CallableStatement)
3、将请求入参填充到SQL语句中,由parameterHandler参数处理器进行处理,最终底层是由不同的TypeHander类型处理器实现参数填充 ps.setInt(i, parameter)-- java-->JDBC;
4、执行SQL语句查询操作,ps.execute();
5、获得结果集,由resultSetHandler结果集处理器进行处理,
第十一步:返回第十步的prepareStatement(handler, ms.getStatementLog())方法
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 创建Statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 处理占位符
handler.parameterize(stmt);
return stmt;
}
// 创建Statement对象
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
// 直接调用JDBC Connection的prepareStatement()方法,创建PreparedStatement对象。当mappedStatement.getKeyGenerator()返回的类型为Jdbc3KeyGenerator时,
// 根据keyColumnNames包含的字段来初始化PreparedStatement对象
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
//返回数据库生成的主键
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); // 相当于JDBC获取操作对象
} else {
//在insert语句执行完成之后,会将keyColumnNames指定的列返回
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}

第十二步:返回第十一步,执行handler.parameterize(stmt)方法
// PreparedStatementHandler#parameterize方法
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
/**
* parameterHandler.setParameters((PreparedStatement) statement);
* 则实质调用的是BaseStatementHandler类里的:this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql)的setParameters
*
*/
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 从boundSql中获取参数映射列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 获取类型处理器,主要是根据方法入参类型确定的
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 使用类型处理器,为预处理语句设置参数值
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
typeHandler.setParameter(ps, i + 1, value, jdbcType)该方法的作用:该方法 setParameter 负责将查询参数设置到 PreparedStatement对象中。
它从 boundSql 对象中获取ParameterMapping参数映射器,使用 TypeHandler 将ParameterMapping参数映射器中的参数设置到 PreparedStatement中,
这样可以确保在执行 SQL 查询时,参数会被正确地传递到数据库中
调用BaseTypeHandler.setParameter方法
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
try {
// 根据参数的类型,调用不同XXTypeHandler进行传参处理
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
例如参数类型为Integer,则调用IntegerTypeHandler.setNonNullParameter方法
public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
throws SQLException {
// 传参设值处理
ps.setInt(i, parameter);
}
第十三步:返回第十步执行handler.query(stmt, resultHandler)方法,实则是调用PreparedStatementHandler#query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//1.执行语句,调用 PreparedStatement 的execute方法
ps.execute();
// 通过 ResultSetHandler 对象封装结果集,映射成 JavaBean
return resultSetHandler.<E> handleResultSets(ps);
}
到这里就差不多了,代码最后回调用PreparedStatement#execute方法去执行,执行的结果会保存到PreparedStatement中,然后通过ResultSetHandler去处理结果集,
则实质调用的是BaseStatementHandler类里的:this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
默认实现为DefaultResultSetHandler代码来到org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets
第十四步:执行handleResultSets方法,处理 ResultSet的结果,将其转换成一个 List<java对象>
public List<Object> handleResultSets(Statement stmt) throws SQLException {
// 记录活动日志
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// 初始化结果列表,创建一个列表 multipleResults 来存储处理后JavaBean的结果集
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//1、Statement 对象中获取第一个结果集的封装对象 ResultSetWrapper
ResultSetWrapper rsw = getFirstResultSet(stmt);
//2、从mappedStatement中拿到ResultMap,也就是我们在mapper.xml总定义的<resultMap/>结果集映射器
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//3、真正处理结果集映射
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//4、从mappedStatement中拿到ResultSet,也就是我们在mapper.xml总定义的<resultSets/>结果集映射器
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
核心流程:
-
1、mappedStatement.getResultMaps(); //获取ResultMap
-
2、handleResultSet(rsw, resultMap, multipleResults, null); //根据规则(resultMap)处理 ResultSet,将结果集转换为Object列表
-
3、return collapseSingleResultList(multipleResults); //返回结果集
第十五步:DefaultResultSetHandler#handleResultSet方法
// 根据resultMap或ResultSet结果集,将结果集转换为Object列表,并保存到multipleResults
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
//1、非存储过程的情况,parentMapping为null。handleResultSets方法的第一个while循环传参就是null
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
//1.2、如果没有自定义的resultHandler,则使用默认的DefaultResultHandler对象
if (resultHandler == null) {
//1.3、创建DefaultResultHandler对象
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//1.4、(核心方法)处理resultMap或ResultSet返回的每一行Row,里面会循环处理全部的结果集
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//1.5、将defaultResultHandler的处理结果,添加到multipleResults中
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
核心流程:
-
1、handleRowValues //主要处理结果集
-
2、multipleResults.add //添加结果集到对应集合
第十六步:DefaultResultSetHandler#handleRowValues
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
//1、处理嵌套映射的情况
if (resultMap.hasNestedResultMaps()) {
//1.1、校验RowBounds
ensureNoRowBounds();
//1.2、校验自定义resultHandler
checkResultHandler();
//1.3、处理嵌套映射的结果集
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//2、简单映射的情况
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
核心流程:
-
1.handleRowValuesForNestedResultMap //处理嵌套映射
-
2.handleRowValuesForSimpleResultMap //处理简单映射
第十七步:DefaultResultSetHandler#handleRowValuesForSimpleResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {
//1.创建DefaultResultContext
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
//2.获得ResultSet对象,并跳到 rowBounds 指定的开始位置
skipRows(rsw.getResultSet(), rowBounds);
//3.循环处理结果集(shouldProcessMoreRows校验context是否已经关闭和是否达到limit,rsw获取下一条记录)
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
//4.根据该行记录和ResultMap.discriminator ,决定映射使用的 ResultMap 对象
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
//5.根据确定的ResultMap将ResultSet中的该行记录映射为Java对象(处理一行)
Object rowValue = getRowValue(rsw, discriminatedResultMap);
//6.将映射得到的Java对象添加到ResultHandler.resultList中
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
核心流程:
-
1、skipRows:定位到正确位置
-
2、shouldProcessMoreRows:校验,并判断是否超过limit限制
-
3、resolveDiscriminatedResultMap:处理鉴别器信息
-
4、getRowValue:将一行记录转换为一个Java对象
-
5、storeObject:将转化得到的Java对象保存
第十八步:DefaultResultSetHandler#getRowValue()
// 根据确定的ResultMap将ResultSet中的记录映射为Java对象(处理一行)
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
//创建ResultLoaderMap,和懒加载相关
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//1.创建结果对象,但还没赋值
Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
//2.如果hasTypeHandlerForResultObject(rsw, resultMap.getType())返回 true ,意味着rowValue是基本类型,无需执行下列逻辑。
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
//3.根据结果对象类型的不同,创建不同的MetaObject对象,用于访问rowValue对象
final MetaObject metaObject = configuration.newMetaObject(resultObject);
//4.foundValues代表,是否成功映射任一属性。若成功,则为true ,若失败,则为false
boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
//5.判断是否开启自动映射功能
if (shouldApplyAutomaticMappings(resultMap, false)) {
//6.自动映射未明确的列,将结果映射到java对象中
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
//7.映射ResultMap中明确映射的列,至此,ResultSet的该行记录的数据已经完全映射到结果对象resultObject的对应属性中
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
//8.如果映射属性失败,则置空 resultObject 对象。
resultObject = foundValues ? resultObject : null;
return resultObject;
}
return resultObject;
}
核心流程:
-
1、createResultObject:根据映射规则(resultMap的type信息)创建Java对象
-
2、shouldApplyAutomaticMappings->applyAutomaticMappings: 如果使用了自动映射,那么就会处理未明确映射关系的属性
-
3、applyPropertyMappings: 处理映射关系明确的属性
-
4、映射失败则会把结果置为null
第十九步: DefaultResultSetHandler#applyAutomaticMappings
// 自动映射未明确的列
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//1.获得UnMappedColumnAutoMapping数组
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (autoMapping.size() > 0) {
//2.遍历UnMappedColumnAutoMapping数组
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//3.获得指定字段的值
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
//4.若非空,标记foundValues有值
if (value != null) {
foundValues = true;
}
//5.将映射列字段,保存metaObject对象中
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
调用BaseTypeHandler.getResult方法
public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
}
根据结果集的参数类型,调用不同XXTypeHandler.getNullableResult方法,例如:IntegerTypeHandler.getNullableResult方法
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
int result = rs.getInt(columnName);
return result == 0 && rs.wasNull() ? null : result;
}
核心流程:
-
1、首先会在一个集合里面讲未明确映射的列保存起来,先将他获取出来。
-
2、然后从数据库记录中,根据列的名称获取到列对应的Value
-
3、通过反射模块提供的metaObject设值到parameterObject中。注意metaObject是Mybatis反射模块以及封装好的相关类,给属性设值提供简单的使用方式
总结:第十四步--->第十九步代码解析
会先从mappedStatement中拿到ResultMap,也就是我们在mapper.xml总定义的结果集映射器,然后调用DefaultResultHandler#handleResultSet方法结果集处理
在DefaultResultHandler方法中会拿到ResultMap说指定的type也就是
然后会拿到根据ResultMap结果映射的每一个column , 再根据column数据类型,使用对应的TypeHandler从结果集中取出对应的值,并赋值给对象的实例。到此结果集映射成对象完毕并保持到DefaultResultSetHandler的multipleResults对象中。最后返回结果。最后关闭Statement
---------------------- 第四阶段总结一下Mybatis的执行流程 ----------------------------------------------------
-
当我们执行SqlSession的select方法时,会从Configuration中拿到MappedStatement(包括SQL,参数映射,结果集映射等)。然后调用executor去执行query方法
-
接着代码来到executor执行器,它会先创建CacheKey缓存的key,缓存的key是 statementId ; 分页 ;SQL,参数值 一起组成的,也就是说只要是同一SQL,分页条件也相同,参数也相同的话,就可以命中缓存。
-
如果有开启二级缓存的话,会尝试执行二级缓存,有就返回,没有就从数据库查询,然后再把结果添加到二级缓存。二级缓存在TransactionalCacheManager中管理起来的。
-
假设没有二级缓存,尝试从数据库查询此刻会尝试从一级缓存查询数据,有就返回,没有就从数据库查询。一级缓存在SqlSession中的BaseExecutor.PerpetualCache中,所以是先执行二级缓存再执行一级缓存。
-
如果还是没有命中缓存,就会通过Connection创建一个PrepareStatement,然后调用StatementHandler#query去执行PrepareStatement。
-
紧接着StatementHandler方法底层会调用PrepareStatement#execute查询结果,然后调用ResultSetHandler处理结果。
-
ResultSetHandler会先拿到结果集,然后找到配置的ResultMap。根据ResultMap中配置的type也就是实体类的权限定名,使用ObjectFactory对象工厂使用反射创建对象实例。
-
再接着就会拿到ResultMap总映射的column列,根据column类型找到对应的TypeHanlder拿到值,使用反射赋值给对象实例。最后返回对象列表

浙公网安备 33010602011771号