mybatis核心组件及其流程

组件

Executor

负责SQL语句的生成和查询缓存的维护,三种类型SIMPLE(默认), REUSE, BATCH

//SIMPLE 每次执行update和query就新建一个Statement
//REUSE 每次执行重复使用Statement
//BATCH 用于将多个SQL一次性输出到数据库
public interface Executor {
	// 结果集转换器
	ResultHandler NO_RESULT_HANDLER = null;
	// 更新操作执行 Statement的executeUpdate
	int update(MappedStatement ms, Object parameter) throws SQLException;
	// 查询操作执行 Statement的executeQuery
	<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
}

MapperMethod

代理对象通过该类执行execute方法调用sqlSession

SqlCommand

表示sql命令,持有name(namespace+id)+type(select|insert|update|delete),通过SqlCommand来判断代理对象执行方法的类型。

MethodSignature

方法签名,标记返回类型,返回值为Void、Many、Cursor等

StatementHandler

创建Statement对象的处理器

public abstract class BaseStatementHandler implements StatementHandler {
  // 在构造方法中创建的参数解析器和结果集处理器
  protected final ResultSetHandler resultSetHandler;
  protected final ParameterHandler parameterHandler;
  // 持有的执行器
  protected final Executor executor;
  protected final MappedStatement mappedStatement;
  protected final RowBounds rowBounds;
  // 真实sql select * from user where username = ?
  protected BoundSql boundSql;
}
// 执行器中创建Statement处理器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  // 拦截器插件处理
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

ParameterHandler

sql中?占位符对应参数设置,根据传入参数的类型来调用对应的类型处理器设置参数值,如Integer类型,则调用ps.setInt(i, parameter);

DefaultParameterHandler调用setParameters设置sql中?的参数值,遍历类型为IN或INOUT的参数,
1.从AdditionalParameter中获得
2.null值判断
3.使用类型转换器处理入参,如只是一个Integer类型,那么在可以输入#{任意符号}来查询
4.MetaObject中获取,根据入参Object生成对应objectWrapper(Map、Collection使用对应Wrapper处理),调用objectWrapper来匹配propertyName获得入参
5.最后调用typeHandler.setParameter(ps, i + 1, value, jdbcType);设置对应?位置的参数值

ResultSetHandler

将ResultSet转换为对应的java对象

1.使用ResultSetWrapper装配ResultSet,构造函数获得ResultSetMetaData、列数columnCount、获得列名、列的jdbc类型、列的JAVA对象className
2.从MappedStatement中获取ResultMap(用于结果集映射)
3.校验ResultMap.size()是否<1
4.遍历ResultMap处理,调用ResultHandler来处理返回值(默认DefaultResultSetHandler),根据ResultMap中的type创建对象,遍历为对象属性赋值

SqlSource

存放原始的sql信息以及相应的参数信息

select * from user where username=#{username}解析走StaticSqlSource
解析结果:sql:select * from user where username=?,parameterMappings存放参数名、类型等信息
 
select * from user where <if test=‘username!=null’> username=#{username} </if>存在动态sql或${}注入走DynamicSqlSource,最后也会解析为StaticSqlSource

通过SqlNode存放每行数据,TextSqlNode存${},StaticTextSqlNode存静态文本,SetSqlNode、IfSqlNode就是动态语法解析语句

BoundSql

存放实际sql

通过SqlSource获得具体的sql(存在占位符?),parameterMappings(参数名与类型),parameterObject(参数值),additionalParameters(存放_databaseId和_parameter)
动态sql不满足条件则不解析对应sql
完成对动态sql的转换(ognl),_databaseId和_parameter为传入参数值,如果入参parameterObject不为Map则可使用对象字段名作为动态sql中的参数值

普通对象动态解析,传入的为Integer类型,所以可以用Integer类型中value字段获得传入参数值
<select id="selectRoleById" parameterType="java.lang.Integer" resultType="top.king.model.Role">
	<if test="value = 0">
		select * from t_role where role_id=#{dddas}
	</if>
</select>
Map对象动态解析,传入Map类型会被封装一层_parameter,通过_parameter获得实际的Map对象来查找属性
<select id="selectRoleByMap" parameterType="java.util.Map" resultType="top.king.model.Role">
	<if test="id!=null">
		select * from t_role where role_id=#{id}
	</if>
</select>

RowBounds

mybatis实现分页组件

public class RowBounds {
  public static final int NO_ROW_OFFSET = 0;
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;
}  

流程

总流程

  1. SqlSessionFactoryBuilder调用build方法,通过传入的流数据生成Configuration对象,传入Configuration对象创建DefaultSqlSessionFactory
  2. DefaultSqlSessionFactory调用openSqlSession方法,通过Configuration获得Environment创建Transaction,传入Transaction对象创建Executor执行器,传入Executor创建DefaultSqlsession
  3. DefaultSqlsession最终都是调用selectList、update方法来执行,本质上是调用Executor执行sql

DefaultSqlsession调用过程

  1. 生成代理对象执行

调用getMapper方法,从mapperRegistry中获取代理对象(jdk动态代理)
生成的代理对象持有当前sqlSession,做一个参数名称转换(@Param解析,不存在则使用方法参数名arg0···),根据类型(select|insert|update|delete)调用sqlSession的select、update方法获得结果

  1. 直接输入NameSpace+ID调用sqlSession执行

Executor执行过程

执行之前参数已经进行两次处理

  • 如果是通过代理对象调用的sqlSession则会执行@Param参数名解析
  • 如果参数为Collection或Array对象,则返回Map。(“collection”:“Collection对象” “list”:“list对象” “array”:“数组对象”)

2级缓存处理

默认cacheEnabled为true,所以执行器为CachingExecutor
首先通过Statement获得BoundSql,生成缓存CacheKey,如果当前Mapper存在cache标签说明启用2级缓存,在Statement的cache存放缓存。
如果存在cache(存放在TransactionalCacheManager中),则从2级缓存中获得查询结果(commit后其他sqlSession才能查询到缓存),否则通过真实的执行器来执行sql

1级缓存处理

BaseExecutor执行,缓存存在于PerpetualCache localCache中。
commit、rollback会清空localCache缓存
不存在缓存中则执行queryFromDatabase方法从数据库中获得值

doQuery数据库中查询

首先创建StatementHandler,创建的都是RoutingStatementHandler,其中的delegate表示真实的StatementHandler,构造函数中包含的ResultSetHandler和ParameterHandler的创建
 
然后创建Statement,这里ReuseExecutor使用statementMap保存了创建Statement,供下次直接调用。通过事务获得Connection,通过Connection创建Statement,设置Timeout(查询超时时间seconds)和FetchSize(每次从数据库读取的数据条数)
 
Statement参数化处理parameterize。调用DefaultParameterHandler处理?占位符

结果集处理

statement调用对应execute方法后,使用ResultSetHandler来处理Statement的返回值
DefaultResultSetHandler将ResultSet转换为java对象

插件

PageHelper拦截Executor的query方法,通过PageHelper.startPage等方法使当前线程持有对应的Page类,拦截query方法修改SqlSource,更改生成的sql(mysql添加limit,oracle添加rownum来完成分页)

Interceptor

拦截器

public interface Interceptor {
  // 执行拦截器方法
  Object intercept(Invocation invocation) throws Throwable;
  // 为被拦截对象target生成代理对象
  Object plugin(Object target);
  // 配置plugin所需参数
  void setProperties(Properties properties);

}

InterceptorChain

拦截器链

public class InterceptorChain {
  // mybatis中所有配置的拦截器
  private final List<Interceptor> interceptors = new ArrayList<>();
  // 遍历执行生成代理对象
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

Plugin

插件,用于拦截真实对象方法,继承InvocationHandler,使用其invoke方法完成代理方法的执行

public class Plugin implements InvocationHandler {
  // 被代理对象
  private final Object target;
  // 拦截器
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      if (methods != null && methods.contains(method)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
}

Invocation

public class Invocation {
    private final Object target;
    private final Method method;
    private final Object[] args;
	// 调用真实对象的方法
	public Object proceed() throws InvocationTargetException, IllegalAccessException {
	    return this.method.invoke(this.target, this.args);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值