MyBatis源码的学习(20)---如何从jdbc到mybatis?

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

总之,这算时一个阶段性的总结,上面说的内容在我之前的文章中都有详细的介绍。

如果想要看全面的可以,看我的其他文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值