文章目录
- 1. 相关代码
- 2. SQL 语句解析全流程
- 2.1 涉及到的重要类
- 2.2 解析标签
- 2.2.1 \<include>
- 2.2.2 \<selectKey>
- 2.2.3 处理 SQL 语句
- 3. 获取真正执行的sql
1. 相关代码
package com.boge.mapper;
import com.boge.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectUserList(User user);
User selectUserById(Integer id);
int updateById(User user);
}
<?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.boge.mapper.UserMapper">
<cache/>
<resultMap id="BaseResultMap" type="com.boge.pojo.User">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userName" column="user_name" jdbcType="VARCHAR"/>
<result property="realName" column="real_name" jdbcType="VARCHAR"/>
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
<result property="dId" column="d_id" jdbcType="INTEGER"/>
</resultMap>
<sql id="baseSQL">
id,user_name,real_name,password,age,d_id
</sql>
<select id="selectUserById" resultType="com.boge.pojo.User">
select
<include refid="baseSQL"></include>
from
t_user
where
id = #{id}
</select>
<select id="selectUserList" resultMap="BaseResultMap">
select
<include refid="baseSQL"></include>
from
t_user t
<where>
<if test="userName != null and userName.trim() != ''">
and t.user_name like concat('%', #{userName},'%')
</if>
<if test="age != null">
and t.age = {age}
</if>
</where>
</select>
<update id="updateById">
update t_user set user_name = #{userName} where id = #{id}
</update>
</mapper>
2. SQL 语句解析全流程
2.1 涉及到的重要类
- XMLStatementBuilder。映射文件由<select>、<insert>、<delete>、<update>等标签是由XMLStatementBuilder.parseStatementNode()进行解析,不在由XMLMapperBuilder解析。
- SqlSource。用来表示解析之后的sql语句。
- MappedStatement 。用来表示解析之后的sql标签,包含sqlSource和sqlCommandType,分别记录了SQL 标签中定义的 SQL 语句和 SQL 语句的类型(INSERT、UPDATE、DELETE、SELECT 或 FLUSH 类型)。
public void parseStatementNode() {
// 获取SQL标签的id以及databaseId属性
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
// 若databaseId属性值与当前使用的数据库不匹配,则不加载该SQL标签
// 若存在相同id且databaseId不为空的SQL标签,则不再加载该SQL标签
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 获取节点的名称 select insert delete update
String nodeName = context.getNode().getNodeName();
// 获取到具体的 sql 命令 类型
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 解析 include标签 替换include 标签 完成 ${} 解析
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的加载解析 同时记录了 sql中的占位符 ParameterMapping
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);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
// >> 关键的一步: MappedStatement 的创建
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
2.2 解析标签
2.2.1 <include>
<include>作用是引入由<sql>标签定义的sql片段,<sql>实际在XMLMapperBuilder.sqlElement()解析。
<include> 标签由XMLIncludeTransformer.applyIncludes()处理,同时还会处理<include> 标签下的<property>标签和“${}"占位符。
2.2.2 <selectKey>
略。
2.2.3 处理 SQL 语句
// 动态SQL的加载解析 同时记录了 sql中的占位符 ParameterMapping
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
3. 获取真正执行的sql
在CachingExecutor.query()生成真正待执行的sql。
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 获取SQL
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 创建CacheKey:什么样的SQL是同一条SQL? >>
// select * from t_user select id,username,password form t_user
// 根据特定的规则生成一个key 保证不冲突
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
进入getBoundSql(),
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);
}
// check for nested result maps in parameter mappings (issue #30)
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;
}