mybatis源码解读,基本简单的流程已经解析的差不多了,这里做个总结:从架构的角度,分析下如何把jdbc封装成myBatis的。
原生的jdbc代码步骤:
1.获取连接(连接信息等可以从配置文件中获取)
2.sql
3.创建statement对象
4.设置参数
5.执行statement
6.结果集映射为java类型
7.释放资源
从架构的角度看问题:
首先,连接信息需要的驱动,数据源信息。可以抽取到配置文件中。
第二,那么sql语句是否也可以提取到配置文件,然后我们从配置文件读sql语句
第三,我们sql中的参数如何赋值
第四,reseultSet如何转为我们需要的java类型
其实,mybatis就是帮我们解决了这四个问题的。
我们的配置信息需要存储:Confguration,
我们的数据库连接时信息需要存储 :DataSource
然后我们的sql怎么存储呢?
Mapper.xml文件,还有每一个sql语句这些信息需要存储
XMLMapperBuilder MapperBuilderAssistant XMLStatementBuilder MappedStatement XMLScriptBuilder
然后我们的sql信息的存储
SqlSource SqlNode BoundSql
我们的参数的存储
public class BoundSql {
private final String sql;
//这里存放我们的入参
private final List<ParameterMapping> parameterMappings;
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
}
参数怎样从java类型转为jdbc类型,并完成赋值操作
ParameterHandler
DefaultParameterHandler 实现类,赋值操作委托给typeHandler,会进行类型处理器的推断,如果
//没有指定jdbc类型时。会推断出处理器时哪一个
TypeHandler 负责赋值操作,java到jdbc,或jdbc到java
最后,我们的返回结果如何转为java类型
ResultSetHandler
ResultHandler
TypeHandler 负责将jdbc类型的值,转为java类型值
我们的myBatis的源码学习中,比较重要的就是sql语句的存储。涉及到,SqlSource(提供可以直接供statement使用的sql语句 即 Boundsql,既有行为,同时也存储数据SqlNode(动态sql时))。SqlNode中存储sql节点,使用组合模式,apply方法进行各自sql片段的解析操作。
结论:1.如果XML的一个crud标签中没有 ${} ,动态标签(<if>等),那么我们生成的是一个RawSqlSource类型,它里面成员变量:StaticSqlSource。在我们new RawSqlSource()操作时,会将 '#{}'处理为占位符“?”.
2.对于动态标签,我们的解析工作发生在执行getBoundSql方法的时候,最终会给“${}‘赋值,处理动态标签。
3.对于一个sql文本中同时存在"$和”#“,由于我们的判断逻辑时当作动态sql处理的,所以"#{}"的还没处理。所以处理完动态sql之后,还需要对"#{}"进行处理。
@Override
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对象 传递给BoundSql对象
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
上面的参数传递 等价于
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
Mapper的代理:getMapper-->mapperRegistry.getMapper-->MapperProxyFactory.newInstance-->Proxy.newProxyInstance
sql的执行:Mapper方法调用-->mapperProxy.invoke-->MapperMethodInvoker.invoke-->MapperMethod.execute-->sqlsession.selectOne--->executor.query--->statementHandler.query-->statement.execute
入参和结果的处理:statementHandler--->paramterHandler(入参处理)---->typeHandler.setParameter
--->resultSetHandler(结果集处理)---->typeHandler.getResult(resultSet,column)
sql数据的存储:mappedStatement中成员变量 sqlSource.getBoundSql(入参)
sqlSource--->(动态sql才有sqlNode)sqlNode---->BoundSql
Mapper方法入参的传递:mapperProxy中的参数数组 Object args --->【传递给sqlSession时】map结构数据(入参为非集合)或list或array(入参是list或数组时)---->【传递给执行器时】对list或arry进行包装,转为map结构--->作为
getBoundSql方法的入参--->boundSql中封装了入参
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
之所以这样处理,主要是我们如果本来俩个参数,最后的参数列表中会翻倍,多默认的 param1,param2这样的出来参数。
package org.apache.ibatis.type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* @param columnName Colunm name, when configuration <code>useColumnLabel</code> is <code>false</code>
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
另外,如果想要做扩展,我们的mybatis提供了对四大对象的切入点
执行器Executor ,StatementHandler,ParameterHandler,ResultSetHandler
实现接口:Interceptor(需要添加注解@Intercepts)
或者我们继承它的实现类:ExamplePlugin
总之,这算时一个阶段性的总结,上面说的内容在我之前的文章中都有详细的介绍。
如果想要看全面的可以,看我的其他文章。