一.基本介绍
本节分析的主要内容是:Mybatis如何将传入的参数填充到Sql语句当中。举个例子:
映射文件: <mapper namespace="org.apache.ibatis.mytest.MyDao"> <insert id="insertAuthor" parameterType="org.apache.ibatis.domain.blog.Author"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert> <select id="selectAll" parameterType="java.lang.String" resultType="java.util.HashMap"> select * from Author where id = #{id} and username = #{username} </select> <update id="updateAuthorIfNecessary" parameterType="org.apache.ibatis.domain.blog.Author"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update> </mapper>
接口:
public interface MyDao { void insertAuthor(Author author); void updateAuthorIfNecessary(Author author); Map<String, Object> selectAll(@Param("id") String id, @Param("username")String username); } 测试代码
@Test public void update(){ try (SqlSession session = sqlMapper.openSession()) { MyDao mapper = session.getMapper(MyDao.class); Author expected = new Author(500, "cbegin", "******", null, null, null); mapper.updateAuthorIfNecessary(expected); } }
上述mapper.updateAuthorIfNecessary(expected) 最终会调用到映射文件中的 id为updateAuthorIfNecessary的update标签,
sql语句最终解析为:update Author set username = ? ,password = ?, email = ? where id = ?
看看如何将 Author 对象填充到sql语句中对于的参数位置。
二.准备阶段
通过MyDao mapper = session.getMapper(MyDao.class) 这种方式最终得到的结果是一个 代理对象,MapperProxy
MapperProxy: @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (method.isDefault()) { if (privateLookupInMethod == null) {//jdk1.8 接口默认方法 return invokeDefaultMethodJava8(proxy, method, args); } else { return invokeDefaultMethodJava9(proxy, method, args); } } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } // 从缓存中获取 MapperMethod 对象,若缓存未命中,则创建 MapperMethod 对象 final MapperMethod mapperMethod = cachedMapperMethod(method); // 调用 execute 方法执行 SQL return mapperMethod.execute(sqlSession, args); }
MapperMethod:
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { 创建 SqlCommand 对象,该对象包含一些和 SQL 相关的信息 this.command = new SqlCommand(config, mapperInterface, method); // 创建 MethodSignature 对象 this.method = new MethodSignature(config, mapperInterface, method); }
MethodSignature:
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) { //获取返回类型 Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); if (resolvedReturnType instanceof Class<?>) { this.returnType = (Class<?>) resolvedReturnType; } else if (resolvedReturnType instanceof ParameterizedType) { this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType(); } else { this.returnType = method.getReturnType(); } this.returnsVoid = void.class.equals(this.returnType); this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray(); this.returnsCursor = Cursor.class.equals(this.returnType); this.returnsOptional = Optional.class.equals(this.returnType); // 解析 @MapKey 注解,获取注解内容 this.mapKey = getMapKey(method); this.returnsMap = this.mapKey != null; //解析RowBounds参数 位置 this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); //解析 ResultHandler 位置 this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); //解析参数列表,重点关注 this.paramNameResolver = new ParamNameResolver(configuration, method); }
ParamNameResolver:
public ParamNameResolver(Configuration config, Method method) { //获取参数类型列表 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++) { // 检测当前的参数类型是否为 RowBounds 或 ResultHandler if (isSpecialParameter(paramTypes[paramIndex])) { // skip special parameters continue; } String name = null; for (Annotation annotation : paramAnnotations[paramIndex]) { if (annotation instanceof Param) { hasParamAnnotation = true; // 获取 @Param 注解内容 name = ((Param) annotation).value(); break; } } if (name == null) { // @Param was not specified. // 检测是否设置了 useActualParamName 全局配置,默认开启 if (config.isUseActualParamName()) { // 通过反射获取参数名称。此种方式要求 JDK 版本为 1.8+, // 且要求编译时加入 -parameters 参数,否则获取到的参数名 // 仍然是 arg0, arg1, ..., argN name = getActualParamName(method, paramIndex); } if (name == null) { // use the parameter index as the name ("0", "1", ...) // gcode issue #71 /* * 使用 map.size() 返回值作为名称,思考一下为什么不这样写: * name = String.valueOf(paramIndex); * 因为如果参数列表中包含 RowBounds 或 ResultHandler,这两个 * 参数会被忽略掉,这样将导致名称不连续。 * * 比如参数列表 (int p1, int p2, RowBounds rb, int p3) * - 期望得到名称列表为 ["0", "1", "2"] * - 实际得到名称列表为 ["0", "1", "3"] */ name = String.valueOf(map.size()); } } //参数下标 -- 》 参数名,后续根据该下标获取真正的参数对象 map.put(paramIndex, name); } //转成sortedMap,只读 names = Collections.unmodifiableSortedMap(map); }
可以看到ParamNameResolver里面保存了 调用方法 参数索引位置和参数名的映射。
如: void getInfoById(String id), 没有 @Param注解,
映射关系为:0 -- > arg0
void getInfoById(@Param("id") String id)
映射关系为:0 -- > id
获取到了方法参数索引和参数名称的映射关系,接下来要结合具体入参值,获取到参数名称 --->参数值 的映射关系了
MapperMethod:
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()) { // 如果方法返回值为 void,但参数列表中包含 ResultHandler,表明 // 使用者想通过 ResultHandler 的方式获取查询结果,而非通过返回值 // 获取结果 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { // 执行查询操作,并返回多个结果 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // 执行查询操作,并将结果封装在 Map 中返回 result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { // 执行查询操作,并返回一个 Cursor 对象 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()); } // 如果方法的返回值为基本类型,而返回值却为 null,此种情况下应抛出异常 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; }
public Object convertArgsToSqlCommandParam(Object[] args) { return paramNameResolver.getNamedParams(args); }
ParamNameResolver:
public Object getNamedParams(Object[] args) { final int paramCount = names.size(); if (args == null || paramCount == 0) { return null; } else if (!hasParamAnnotation && paramCount == 1) { /* * 如果方法参数列表无 @Param 注解,且仅有一个非特别参数,则返回该 * 参数的值。比如如下方法: * List findList(RowBounds rb, String name) * names 如下: * names = {1 : "arg1"} * 此种情况下,返回 args[names.firstKey()],即 args[1] */ return args[names.firstKey()]; } else { final Map<String, Object> param = new ParamMap<>(); int i = 0; for (Map.Entry<Integer, String> entry : names.entrySet()) { // 添加 <参数名, 参数值> 键值对到 param 中 param.put(entry.getValue(), args[entry.getKey()]); // add generic param names (param1, param2, ...) final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1); // ensure not to overwrite parameter named with @Param // 检测 names 中是否包含 genericParamName,什么情况下会包含? // 答案如下: // 使用者显式将参数名称配置为 param1,即 @Param("param1") if (!names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } }
在ParamNameResolver的getNamedParams,传入Object[] args,也就是入参,将会根据 names,即参数索引 -- 参数名 映射关系,创建 参数名--- 参数值 映射关系 (一个参数,且无@Param注解除外)
举例:void getInfoById(String id) 一个参数,且无Param注解
参数索引 -- 参数名: 0 ---> arg0
不创造参数名--- 参数值 的映射关系,直接返回 id值
void getInfoById(@Param("id") String id, @Param("age") String age )
参数索引 -- 参数名: 0 ---> id
1 ---> age
参数名--- 参数值 id ----> agrs[0]
age -----> args[1]
三. 解析sql中的参数
构造好了 参数名--- >参数值 映射关系后,需要将其填充到 sql语句中,即 select * from a where id = #{id}, 将 id替换成具体值。
以<update>标签执行sql过程为例
DefaultSqlSession:
public int update(String statement, Object parameter) { try { //parameter 是已经封装过的入参 dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
BaseExecutor:
public int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } clearLocalCache();//清除缓存 //模板方法 return doUpdate(ms, parameter); }
SimpleExecutor:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //会解析 sql,将 #{a} 替换成 ? StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); //获取连接 ,填充参数 stmt = prepareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally { closeStatement(stmt); } }
Configuration:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { //创建具有路由功能StatementHandler StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); //将插件应用在StatementHandler statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
RoutingStatementHandler:
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()); } }
PreparedStatementHandler:
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); }
BaseStatementHandler:
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { this.configuration = mappedStatement.getConfiguration(); this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement //获取 KeyGenerator,执行 processBefore逻辑 generateKeys(parameterObject); //根据传入参数解析动态 sql boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); }
重点关注 mappedStatement.getBoundSql(parameterObject), 根据入参解析动态sql
MappedStatement:
public BoundSql getBoundSql(Object parameterObject) { //如果是DynamicSqlSource,需要处理?,拼接字符串, 对于RawSqlSource,StaticSqlSource //在 mapper.xml文件解析阶段已经解析好了 BoundSql boundSql = sqlSource.getBoundSql(parameterObject); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { // 创建新的 BoundSql,这里的 parameterMap 是 ParameterMap 类型。 // 由<ParameterMap> 节点进行配置,该节点已经废弃,不推荐使用。 // 默认情况下,parameterMap.getParameterMappings() 返回空集合 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; }
以动态sql为例:DynamicSqlSource
@Override public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); // 解析 SQL 片段,并将解析结果存储到 DynamicContext 中,会将条件和传入参数结合,如test的判断条件 rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); // 构建 StaticSqlSource,在此过程中将 sql 语句中的占位符 #{} 替换为问号 ?, // 并为每个占位符构建相应的 ParameterMapping SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); // 将 DynamicContext 的 ContextMap 中的内容拷贝到 BoundSql 中,additionalParameter,额外参数 context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; }
SqlSourceBuilder:
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);//将 #{}替换成?,并且构造parameterMappings,入参结构,有序 return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
GenericTokenParser :
public String parse(String text) { if (text == null || text.isEmpty()) { return ""; } // search open token int start = text.indexOf(openToken); if (start == -1) { return text; } char[] src = text.toCharArray(); int offset = 0; final StringBuilder builder = new StringBuilder(); StringBuilder expression = null; while (start > -1) { if (start > 0 && src[start - 1] == '\\') { // this open token is escaped. remove the backslash and continue. builder.append(src, offset, start - offset - 1).append(openToken); offset = start + openToken.length(); } else { // found open token. let's search close token. if (expression == null) { expression = new StringBuilder(); } else { expression.setLength(0); } builder.append(src, offset, start - offset); offset = start + openToken.length(); int end = text.indexOf(closeToken, offset); while (end > -1) { if (end > offset && src[end - 1] == '\\') { // this close token is escaped. remove the backslash and continue. expression.append(src, offset, end - offset - 1).append(closeToken); offset = end + closeToken.length(); end = text.indexOf(closeToken, offset); } else {//获取真实表达式, 如${abc} ,expression = "abc" expression.append(src, offset, end - offset); break; } } if (end == -1) { // close token was not found. builder.append(src, start, src.length - start); offset = src.length; } else {//这一步将表达式 转为?,或者真实值,看具体实现类,同时保存 参数相关信息 builder.append(handler.handleToken(expression.toString())); offset = end + closeToken.length(); } } start = text.indexOf(openToken, offset); } if (offset < src.length) { builder.append(src, offset, src.length - offset); } return builder.toString(); }
ParameterMappingTokenHandler:
@Override public String handleToken(String content) { //根据 表达式构造ParameterMapping, parameterMappings.add(buildParameterMapping(content)); return "?"; }
/* * 将 #{xxx} 占位符中的内容解析成 ParameterMapping,举例说明一下。如下: * * #{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler} * * 上面占位符中的内容最终会被解析成如下的结果: * * { * "property": "age", * "typeHandler": "MyTypeHandler", * "jdbcType": "NUMERIC", * "javaType": "int" * } * */ 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 { // 代码逻辑走到此分支中,表明 parameterType 是一个自定义的类, // 比如 Article,此时为该类创建一个元信息对象 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); // 将 propertyType 赋值给 javaType 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,则以用户的配置为准 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 " + PARAMETER_PROPERTIES); } } if (typeHandlerAlias != null) { builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias)); } return builder.build(); }
看看 ParameterMapping 的字段
private Configuration configuration;
//参数名称
private String property;
private ParameterMode mode;
private Class<?> javaType = Object.class;
private JdbcType jdbcType;
private Integer numericScale;
private TypeHandler<?> typeHandler;
private String resultMapId;
private String jdbcTypeName;
private String expression;
最重要的是 property,代表 #{user}中的 user, 后续根据 property 从之前 参数名称和参数值映射关系集合 获取具体值。
四.填充sql参数
SimpleExecutor:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //会解析 sql,将 #{a} 替换成 ? StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); //填充参数 stmt = prepareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally { closeStatement(stmt); } }
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; }
PreparedStatementHandler:
public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); }
DefaultParameterHandler:
public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); // 从 BoundSql 中获取 ParameterMapping 列表,每个 ParameterMapping // 与原始 SQL 中的 #{xxx} 占位符一一对应 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) {//输入参数,ParameterMode.IN Object value; //获取属性名 String propertyName = parameterMapping.getProperty(); // 检测 BoundSql 的 additionalParameters 是否包含 propertyName 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())) { // 若运行时参数的类型有相应的类型处理器 TypeHandler,则将 // parameterObject 设为当前属性的值。 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 = JdbcType.OTHER 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); } } } } }
看看 value = metaObject.getValue(propertyName);
public Object getValue(String name) { PropertyTokenizer prop = new PropertyTokenizer(name); if (prop.hasNext()) { //获取 #{user.id}中 user的值 MetaObject metaValue = metaObjectForProperty(prop.getIndexedName()); if (metaValue == SystemMetaObject.NULL_META_OBJECT) { return null; } else { //获取 user中的 id值,id属性是 user的children return metaValue.getValue(prop.getChildren()); } } else {//直接获取属性值, 通过反射 执行 get方法 或者 直接用 map.get(),看包装对象objectWrapper的类型 return objectWrapper.get(prop); } }
通过遍历 ParameterMapping ,取 propertyName, 在之前 构造好的传入参数中寻找对应的值,最后通过 TypeHandler 设置到PreparedStatement 中
BaseTypeHandler:
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 { 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); } } }
CharacterTypeHandler:
public void setNonNullParameter(PreparedStatement ps, int i, Character parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter.toString()); }
参数填充到此结束,其中有很多细节没有细讲,但是流程已经给出来了,可以按照这个流程自行调试。
四.总结
参数填充,其实就是将输入参数整理好,将 sql中引用的参数整理好,然后整合到sql中,其中sql的解析过程,各种动态节点的解析结合入参需要自己多调试。下一章准备分析Mybatis的缓存机制,如一级缓存,二级缓存。
结语:守得云开见月明!