目录
在JDBC用PreparedStatement编程访问数据库时,都需要先用带有占位符(如果存在参数的话)sql创建PreparedStatement,然后在根据数据库类型设置值。这块在mybatis是如何实现的呢。
初始化阶段
构造MixedSqlNode
解析标签
具体的sql解析是在解析mapper.xml的四种标签select|insert|update|delete时进行的。
代码在org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode下
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//获取标签名,根据标签名取得sql命令类型
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//是否是查询
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//默认情况,select的useCache为true,其他会刷新缓存flushCache为false
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
//应用<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");
//默认 XMLLanguageDriver
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
//解析<selectKey /> 标签
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;
}
//默认 XMLLanguageDriver
//动态DynamicSqlSource
//静态RawSqlSource #1
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");
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
#1处就是对sql进行处理,其中langDriver为XMLLanguageDriver,代码如下
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
//动态
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
//非动态
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
构造SqlNode
会对标签进行解析,把我们在mapper.xml写的sql语句拆分封装为一个个的SqlNode并返回混合SqlNode(MixedSqlNode),当为动态sql时创建DynamicSqlSource(动态的SqlSource),当为非动态时创建RawSqlSource(原始的SqlSource)。
如何判断一个sql是否是动态sql呢,代码如下
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
XNode child = node.newXNode(children.item(i));
//这两种表示节点内容,纯sql #1
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
String data = child.getStringBody("");
TextSqlNode textSqlNode = new TextSqlNode(data);
//带有${}算动态sql #2
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
//静态sql #3
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
//节点下的子元素
String nodeName = child.getNode().getNodeName();
//获取子元素的处理器
/**
* nodeHandlerMap.put("trim", new TrimHandler()); TrimSqlNode
* nodeHandlerMap.put("where", new WhereHandler()); WhereSqlNode
* nodeHandlerMap.put("set", new SetHandler()); SetSqlNode
* nodeHandlerMap.put("foreach", new ForEachHandler()); ForEachSqlNode
* nodeHandlerMap.put("if", new IfHandler()); IfSqlNode
* nodeHandlerMap.put("choose", new ChooseHandler()); ChooseSqlNode
* nodeHandlerMap.put("when", new IfHandler()); IfSqlNode
* nodeHandlerMap.put("otherwise", new OtherwiseHandler()); MixedSqlNode
* nodeHandlerMap.put("bind", new BindHandler()); VarDeclSqlNode
*/
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
//处理,根据不同的标签构建不同的sqlNode #4
handler.handleNode(child, contents);
//标记
isDynamic = true;
}
}
//#5
return new MixedSqlNode(contents);
}
#1处表示文本类型的sql包括带有${}和#{}的
#2处判断是否是动态的,其中把带有${}标识为动态,创建TextSqlNode
#3处带有#{}或纯sql为静态的创建StaticTextSqlNode。
#4处表示有动态sql标签,按标签内容创建不同的SqlNode,比如IfSqlNode,WhereSqlNode,ForEachSqlNode,SetSqlNode等,因为标签内部又可以存在标签或者sql所以,这些个SqlNode又持有一个MixedSqlNode。
#5处最后把创建的SqlNode们,封装为MixedSqlNode返回
例子
更直观的感受下,举几个例子
例1:
<select id="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author where id=${author.id}
</select>
MixedSqlNode中的SqlNodeList为TextSqlNode如图
例2:
<select id="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author where id=#{author.id}
</select>
MixedSqlNode中的SqlNodeList为StaticTextSqlNode如图
例3:
<select id="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author
<if test="param2 !=null and param2 !=''">
and id=#{param2}
</if>
</select>
MixedSqlNode中的SqlNodeList为StaticTextSqlNode,IfSqlNode(这里面的SqlNodeList为StaticTextSqlNode),StaticTextSqlNode(if标签后面也会解析出一个StaticTextSqlNode对应的是\n)如图
例4:
<select id="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author where id=#{author.id}
<where>
<if test="param2!=null">
and id=${author.id}
</if>
<if test="param2!=null">
and id=#{author.id}
</if>
</where>
and id=#{author.id}
</select>
如图,标签的前后各有一个\n的标签
阶段总结1
在解析4中标签时,把标签内的内容封装为一个个的SqlNode,并按顺序添加的一个List中,最后把List封装为MixedSqlNode,最后再把MixedSqlNode封装为SqlSource.
构造SqlSource
代码如下
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
//动态 #1
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
//非动态 #2
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
#1处动态的SqlSource创建很简单,直接调用构造方法
#2处静态的SqlSource有点意思我们看下
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
DynamicContext context = new DynamicContext(configuration, null);
//StaticTextSqlNode#apply静态的直接字符串拼接
rootSqlNode.apply(context);
return context.getSql();
}
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
//对sql解析,把#{}替换为?并且把#{}里面的内容封装成map,StaticSqlSource.parameterMappings
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
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;
if (configuration.isShrinkWhitespacesInSql()) {
sql = parser.parse(removeExtraWhitespaces(originalSql));
} else {
sql = parser.parse(originalSql);
}
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
实际RawSqlSource持有一个StaticSqlSource,其操作都会委托给StaticSqlSource进行
1、首先获取到sql,因为是静态的,实际调用的是org.apache.ibatis.scripting.xmltags.StaticTextSqlNode#apply,即把sql直接拿来用
2、对sql解析,把#{}替换为?,并把#{}里的内容封装为ParameterMapping。其中的ParameterMapping会用作获取实体中的具体值,进行设置属性。
例如:sql语句
<select id="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author where id=#{author.id}
</select>
最终的sql会变为
select id, username, password, email, bio, favourite_section from author where id=?
并把#{}里面的内容解析为ParameterMapping,里面的property 就是要获取参数的属性,一般就是调用类的get方法。如下图
获取BoundSql
无论是RawSqlSource还是DynamicSqlSource,都有个getBoundSql方法,用于获取sql。RawSqlSource因为是静态的,在初始花式sql已经确定,不需要传递参数的参与。
DynamicSqlSource是动态的,可能有各种标签,需要参数参与,所以只有在执行的时候,根据参数值才能拼出最终的sql,代码如下
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
结果就是解析各种sqlnode,用ognl表达式匹配,生成最终的sql.
阶段总结2
在构造RawSqlSource会把sql中的#{}替换为?,并把#{}里的内容封装为ParameterMapping以供后续获取属性值来用,最后根据替换的sql和ParameterMapping构造一个StaticSqlSource,针对RawSqlSource操作都会委托给StaticSqlSource。其实可以看到如果是静态的sql,带有占位符的sql已经有了,只要在请求的时候设置好参数就好。
执行阶段
参数转化
这里要做的是把我们在mapper方法中设置的参数转化为sql的参数。
我们获取的mapper其实是代理类org.apache.ibatis.binding.MapperProxy
执行方法是org.apache.ibatis.binding.MapperProxy#invoke
最终会执行org.apache.ibatis.binding.MapperMethod#execute方法
代码如下
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
其中的convertArgsToSqlCommandParam为参数转化,用到了ParamNameResolver类
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
收集参数名
ParamNameResolver构造方法如下
public ParamNameResolver(Configuration config, Method method) {
this.useActualParamName = config.isUseActualParamName();
final Class<?>[] paramTypes = method.getParameterTypes();
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
if (useActualParamName) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
构造方法的作用是把mapper的查询参数收集起来,如果有注解也会标注出来,比如代码
Author selectAuthor( @Param("author") Author author,int id);
最后收集起来的names如下图
key对应出现顺序从0开始,name为名称,如果有@Param注解取名称,没有的话取arg+顺序数
转化参数
转化参数代码如下
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
//#1
Object value = args[names.firstKey()];
//#2
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
//#3
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
if (object instanceof Collection) {
ParamMap<Object> map = new ParamMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
} else if (object != null && object.getClass().isArray()) {
ParamMap<Object> map = new ParamMap<>();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
}
return object;
}
#1处根据mapper方法中的参数个数,为一个时直接返回该类,并根据是不是集合类型去包装。
#3处mapper方法中的参数为多个时,把参数封装为map返回。最终用到的也是这个map,sql中用于设置的参数值
例如mapper代码
Author selectAuthor( @Param("author") Author author,int id);
收集参数名结果为 0->author,1->arg1
转化参数结果为{{"author", author},{"param1", author} ,{"param2",id}}
执行sql
现在我们有了sql了(静态情况下的完整sql语句,动态情况下的一批SqlNode),有了参数映射,有了实参了,接下来要做的就是通过PreparedStatement去执行sql.
org.apache.ibatis.binding.MapperMethod#execute中随便找一个执行看下,例如
result = sqlSession.selectOne(command.getName(), param);
其中param就是转化参数结果,command.getName()为MappedStatement的id为namespace+id(select|update|delete|insert标签的id)
代码如下,最终会调用到org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)方法
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
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;
}
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
}
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
根据id获取MappedStatement,然后通过Executor去执行
query代码如下,可以看到我们获取了BoundSql,这样就有了sql
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续跟代码
org.apache.ibatis.executor.CachingExecutor#query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
org.apache.ibatis.executor.BaseExecutor#query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
最终跟到org.apache.ibatis.executor.SimpleExecutor#doQuery,代码如下
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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//获取sql 创建PreparedStatement,设置参数
stmt = prepareStatement(handler, ms.getStatementLog());
//执行返回结果
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//创建PreparedStatement
stmt = handler.prepare(connection, transaction.getTimeout());
//设置参数
handler.parameterize(stmt);
return stmt;
}
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//创建statement
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);
}
}
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);
} else {
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);
}
}
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
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;
//这里的propertyName 就是我们sql中#{}里面的值,比如param1,author.id #1
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 {
//这里的parameterObject是转化后的参数
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//获取参数值
value = metaObject.getValue(propertyName);
}
//获取TypeHandler
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);
}
}
}
}
}
在org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement可以看到通过BoundSql获取sql并通过connection.prepareStatement(sql)返回PreparedStatement
在org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters可以看到获取了BoundSql的ParameterMapping(在前面已经讲过了),然后反射获取实际参数,通过TypeHandler去设置值的。一般我们不会设置具体的TypeHandler,mybatis通过传入的参数找到合适的TypeHandler然后进行设置值。比如你的入参是Integer类型,他会找到IntegerTypeHandler然后进行设置值。