文章目录
组件
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;
}
流程
总流程
- SqlSessionFactoryBuilder调用build方法,通过传入的流数据生成Configuration对象,传入Configuration对象创建DefaultSqlSessionFactory
- DefaultSqlSessionFactory调用openSqlSession方法,通过Configuration获得Environment创建Transaction,传入Transaction对象创建Executor执行器,传入Executor创建DefaultSqlsession
- DefaultSqlsession最终都是调用selectList、update方法来执行,本质上是调用Executor执行sql
DefaultSqlsession调用过程
- 生成代理对象执行
调用getMapper方法,从mapperRegistry中获取代理对象(jdk动态代理)
生成的代理对象持有当前sqlSession,做一个参数名称转换(@Param解析,不存在则使用方法参数名arg0···),根据类型(select|insert|update|delete)调用sqlSession的select、update方法获得结果
- 直接输入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);
}
}