前几篇主要从外表看mybatis,这次就要拿起手术刀,一点一点细细看。本篇主要把所有sql语句的写法、解析、执行过程进行分析,篇幅会略长,主要有个方面内容:crud及动态sql的使用,sql解析及执行,之前的专题中若和内容核心内容相关性不大则被mark下,不过从此以后,和核心内容无关的也会深入研究下。
1:CityMapper.xml的sql配置
<?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.hhg.jerry.dao.CityDao">
<resultMap id="cityResultMap" type="City">
<id property="id" column="ID" javaType="long" jdbcType="INTEGER" />
<result property="name" column="Name"/>
<result property="countryCode" column="CountryCode"/>
<result property="district" column="district"/>
<result property="population" column="population"/>
</resultMap>
<cache blocking="true" eviction="LRU" flushInterval="500" readOnly="true" size="100" type="perpetual"/>
<select id="getById" resultMap="com.hhg.jerry.dao.CityDao.cityResultMap">
SELECT * FROM city where id = #{id}
</select>
<select id="getByNameAndCountryCode" resultType="City">
SELECT * FROM city where Name = #{name} and CountryCode = #{cCode,javaType=string,jdbcType=VARCHAR}
</select>
<select id="getByName" resultType="City">
SELECT * FROM city where Name like '%${name}%'
</select>
<select id="getCityAsMapById" resultType="java.util.Map">
SELECT * FROM city where id = #{id}
</select>
<select id="getListLTId" resultType="City" parameterType="java.lang.Long"
fetchSize="20">
select * from city where id <= #{maxId}
</select>
<select id="getListCDATALtId" resultType="City" parameterType="java.lang.Long">
select * from city where id <![CDATA[<=]]> #{maxId}
</select>
<select id="getListBetweenIds" resultType="City">
select * from city where id between #{arg0} and #{arg1} order by ${arg2}
</select>
<select id="getCityMappedById" resultType="City">
select * from city where id <= #{maxId}
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into city(Name,CountryCode,District,Population) values (#{name}, #{countryCode}, #{district}, #{population})
</insert>
<update id="update" parameterType="City">
update city set Name=#{name},CountryCode=#{countryCode},District=#{district},Population=#{population} where ID = #{id}
</update>
<delete id="delete">
delete from city where ID = #{id}
</delete>
<select id="getCityIf" resultType="City">
select * from city where CountryCode=#{countryCode}
<if test="district != null">
and District like #{district}
</if>
</select>
<select id="getCityIfLike" resultType="City">
<bind name="likeDistrict" value="'%' + district + '%'" />
select * from city where CountryCode=#{countryCode}
<if test="district != null">
and District like #{likeDistrict}
</if>
</select>
<select id="getCityChoose" resultType="City" parameterType="City">
select * from city where CountryCode=#{countryCode}
<choose>
<when test="name != null">
and Name = #{name}
</when>
<when test="district != null">
and District = #{district}
</when>
<otherwise>
and Name = 'Peking'
</otherwise>
</choose>
</select>
<select id="getCityWhere" resultType="City" parameterType="City">
select * from city
<where>
<if test="countryCode != null">CountryCode=#{countryCode}</if>
<if test="name != null">and Name=#{name}</if>
<if test="district != null">and District=#{district}</if>
</where>
</select>
<select id="getCityTrim" resultType="City" parameterType="City">
select * from city
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="countryCode != null">CountryCode=#{countryCode}</if>
<if test="name != null">and Name=#{name}</if>
<if test="district != null">and District=#{district}</if>
</trim>
</select>
<update id="updateSet" parameterType="City">
update city
<set>
<if test="name != null">Name=#{name},</if>
<if test="countryCode != null">CountryCode=#{countryCode},</if>
<if test="district != null">District=#{district},</if>
<if test="population != null">Population=#{population}</if>
</set>
where ID = #{id}
</update>
<update id="updateTrim" parameterType="City">
update city
<trim prefix="SET" suffixOverrides=",">
<if test="name != null">Name=#{name},</if>
<if test="countryCode != null">CountryCode=#{countryCode},</if>
<if test="district != null">District=#{district},</if>
<if test="population != null">Population=#{population}</if>
</trim>
where ID = #{id}
</update>
<!-- index是集合索引,item为该索引的元素 -->
<select id="getCityForEachList" resultType="City" parameterType="java.util.List">
select * from city where ID in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item},#{index}
</foreach>
</select>
<!-- index是集合索引,item为该索引的元素 -->
<select id="getCityForEachArray" resultType="City">
select * from city where ID in
<foreach item="item" index="index" collection="array" open="(" separator="," close=")">
#{item},#{index}
</foreach>
</select>
<!-- index是集合索引,item为该索引的元素 -->
<select id="getCityForEachSet" resultType="City" parameterType="java.util.Set">
select * from city where ID in
<foreach item="item" index="index" collection="collection" open="(" separator="," close=")">
#{item},#{index}
</foreach>
</select>
<select id="getCityForEachMap" resultType="City">
select * from city where ID in
<foreach item="item" index="index" collection="ids" open="(" separator="," close=")">
#{item},#{index}
</foreach>
</select>
</mapper>
差不多就这些,对每个方法建议先自行实验下,在配置过程中,肯定会有很多疑惑,如resultMap元素下result标签有javaType,jdbcType,sql语句标签属性有resultMap或resultType,parameterType,#{param}中可添加javaType,jdbcType也可以不加,insert如何返回id,如何返回map,如何批量插入,${}和#{}有什么不同等等等等,接下来从源码中发现这些问题的答案吧。
先定位到解析mapper的sql语句代码,XMLStatementBuilder的parseStatementNode方法,贴下这个方法(context就是sql语句的xml元素):
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
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());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
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;
}
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
挑出几个需要说明的:
fetchSize,从数据库一次获取多少条记录(比如select出了1w条,每次我取100条),不同的jdbc支持不同,有兴趣可搜索下深入研究
StatementType:枚举,有STATEMENT,PREPARED,CALLABLE,就是JDBC可创建的statement对象,默认是PREPARED(JDBC中:connection.prepareStatement())
ResultSetType : ResultSet类型,默认是Forward_only(JDBC中,while(resultSet.next()),其它两种好像支持向前迭代等功能,同样有兴趣可进一步研究
XMLInCludeTransformer被我无情的忽视了
processSelectKeyNodes也同上,它和id生成有关吧。。。
KeyGenerator, 帮你insert时插入id
MappedStatement,解析后都保存在我这
SqlSource,parsetStatementNode方法的主角,sql语句信息全在我这里,看下SqlSource:
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
哇,就一个方法的接口...有3个实现类(还有一个deprecated了)DynamicSqlSource,RawSqlSource,StaticSqlSource,接下来看如何构建SqlSource,langDriver中RawLanguageDriver继承XMLLanguageDriver且Raw的注释以说明(你没有必要用我了),那就直接移步XMLLanguageDriver的方法:
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
parameterType就是sql元素的属性,不配置就为null,再移步XMLScriptBuiler:
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
这个方法需要看仔细喽,先解析sql节点为MixedSqlNode,再跟进是否为dynamic来生成DynamicSqlSource或者RawSqlSource,DynamicSqlSource构造函数:
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
啥也没做,只是保存了构造参数,RawSqlSource整个类搬过来了:
public class RawSqlSource implements SqlSource {
private final SqlSource sqlSource;
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<String, Object>());
}
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
DynamicContext context = new DynamicContext(configuration, null);
rootSqlNode.apply(context);
return context.getSql();
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return sqlSource.getBoundSql(parameterObject);
}
}
先获取sql语句,在调用另外一个构造函数,改方法中,生成了sqlSource,到底生成啥?
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql = parser.parse(originalSql);
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
StaticSqlSource,就是它。回过头来看看生成MixedSqlNode的方法:
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<SqlNode>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
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("");
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
MixedSqlNode是啥?实现了SqlNode接口,SqlNode接口:
public interface SqlNode {
boolean apply(DynamicContext context);
}
该方法就是转换sql。生成SqlSource就是上面的几个函数,生成后如何使用呢?BaseExecutor的query方法:
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
这里先通过MappedStatement拿到BoundSql,其是就是调用SqlSource的getBoundSql方法,boundSql再哪里使用呢?
public class DefaultParameterHandler implements ParameterHandler {
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
private final Object parameterObject;
private final BoundSql boundSql;
private final Configuration configuration;
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
@Override
public Object getParameterObject() {
return parameterObject;
}
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
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 e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
跟代码发现boundSql保存在DefaultParameterHandler,这个类中的方法setParameter方法就是通过boundSql保存的信息来设置sql语句的参数(JDBC问号?设置参数)。整个sql解析、执行流程已经分析完,下面具体从不同的sql语句构建SqlSource,先从简单的入手:
<select id="getById" resultMap="com.hhg.jerry.dao.CityDao.cityResultMap">
SELECT * FROM city where id = #{id}
</select>
直接到生成SqlSource处,类XMLScriptBuilder:
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<SqlNode>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
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("");
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
先看生成MixedSqlNode,nodeType如果是TEXT_NODE,则构建TextSqlNode,并判断是否是动态,若不是,则构建StaticTextSqlNode,此处判断是否为动态sql,通过debug发现如果sql语句有${},则认为是动态。这里getById为非动态,则生成RawSqlSource,前面分析过RawSqlSource会构建StaticSqlSource,还是贴出来代码吧:
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql = parser.parse(originalSql);
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
这个方法就是解析token(这里token是#{,})当遇见左右token,parse(GenericTokenParse)就把token中间的文本交给handler(ParameterMappingTokeHandler)处理,并吧handler返回的文本append到builder中(ParameterMappingTokenHandler返回?),handler在处理context(token之间的文本,id)会构建ParameterMapping,看下这个类定义得知它保存的参数相关的信息(java类型,typeHandler,小数级别等),生成方法:
private ParameterMapping buildParameterMapping(String content) {
Map<String, String> propertiesMap = parseParameterMapping(content);
String property = propertiesMap.get("property");
Class<?> propertyType;
if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
propertyType = metaParameters.getGetterType(property);
} else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
propertyType = parameterType;
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
propertyType = java.sql.ResultSet.class;
} else if (property == null || Map.class.isAssignableFrom(parameterType)) {
propertyType = Object.class;
} else {
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
if (metaClass.hasGetter(property)) {
propertyType = metaClass.getGetterType(property);
} else {
propertyType = Object.class;
}
}
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
Class<?> javaType = propertyType;
String typeHandlerAlias = null;
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
if ("javaType".equals(name)) {
javaType = resolveClass(value);
builder.javaType(javaType);
} else if ("jdbcType".equals(name)) {
builder.jdbcType(resolveJdbcType(value));
} else if ("mode".equals(name)) {
builder.mode(resolveParameterMode(value));
} else if ("numericScale".equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if ("resultMap".equals(name)) {
builder.resultMapId(value);
} else if ("typeHandler".equals(name)) {
typeHandlerAlias = value;
} else if ("jdbcTypeName".equals(name)) {
builder.jdbcTypeName(value);
} else if ("property".equals(name)) {
// Do Nothing
} else if ("expression".equals(name)) {
throw new BuilderException("Expression based parameters are not supported yet");
} else {
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + parameterProperties);
}
}
if (typeHandlerAlias != null) {
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
}
return builder.build();
}
纯文本解析,由于我们的参数没有指定javaType和jdbcType,它会认为javaType是Object,jdbcType也会生成一个UnkownTypeHandle:
private void resolveTypeHandler() {
if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
Configuration configuration = parameterMapping.configuration;
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
}
}
至此我们的getById就解析完成了,继续分析下一个sql解析之前,我们先总结一下getById:
构建mapper -> 构建Statement -> 构建SqlSource -> 构建MixedSqlNode -> (由于非动态)构建RawSqlSource -> 构建StaticSqlSource
需要注意的点:
构建MixedSqlNode时我们的children(node.getChildNodes())的length就是1,且nodetype为Node.TEXT_NODE,所以生成了TextSqlNode,继而根据TextSqlNode继续判断是否是动态,不是的话就生成StaticTextSqlNode,并加到队列中作为MixedSqlNode的构造函数参数。这里的TextSqlNode判断是否动态直接判断sql语句是否包含${},通过返回的MixedSqlNode继续判断是否动态,不是的话就生成RawSqlSource,RawSqlSource直接把SqlNode转换为sql语句,how? 还记得SqlNode的接口方法吗?apply(DynamicContext context)就会把sql放置到context的StringBuilder中(暂时只能看到StaticSqlSource是这么做的),此时sql仍然是select * from city where id = #{id},在通过SqlSourceParse把#{}转换为“?”,并通过ParameterMappingHandler收集#{}里面的信息并存在ParameterMapping的集合中。
select中配置的resultMap何时解析呢? 额,SqlSource表示:虽然传入给我的是context(整个sql节点元素),但并不归我管,是构建MappedStatement解析的,我只是用了sql元素中的parameterType属性。
上面这一坨其是还没分析完,构建SqlSource,缺什么呢?如何解析参数,如何使用SqlSource,回到解析参数,我们有两种方式调用这个方法,其一是直接调用接口方法CityDao.getById(Long),另外是sqlSession.selectOne,先看接口方法,goto there:
特殊param,跳过,注解,跳过,userActualParamName,默认为true,getActualParamName里面会判断是否有Reflect这个类(1.8的),如果有,我们的参数就叫arg0,没有就是0,接着看MapperMethod中转换参数的方法convertArgsToSqlCommandParam:
一个参数就返回了,从这里看#{id}并没有啥用,你也可以写成#{what},然后就调用了sqlSession.selectOne方法,证实了缺失可以随意写.继续跟到设置参数处:
parameterObject就是传入的Long,value就是这个参数,直接通过typeHandler(这里是UnKnownTypeHandler)给prepareStatement设置参数的值,虽然是UnKnowTypeHandler:
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException {
TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
handler.setParameter(ps, i, parameter, jdbcType);
}
但还是会根据parameter的type找到typeHandler(这里是LongTypeHandler)再来设置参数:
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType)
throws SQLException {
ps.setLong(i, parameter);
}
好了,还是那个方法getById就分析了这么多,不过接下来的方法和该方法类似,抬走,下一个