读书笔记——MyBatis核心解析和运行原理
构建SqlSessionFactory的过程
1、构建Configuration
Configuration的作用:
- 读入配置文件,包括基础配置的 XML 和映射器 XML (或注解〉。
- 初始化一些基础配置,比如 MyBatis 的别名等,一些重要的类对象(比如插件、映射器、 Object 工厂、 typeHandlers 对象等)。
- 提供单例,为后续创建 SessionFactorγ 服务,提供配置的参数。
- 执行一些重要对象的初始化方法。
Confinguration 是通过 XMLConfigBuilder 去构建的,首先它会读出所有 XML配置的信息,然后把它们解析并保存在 Configuration 单例中。它会做如下初始化:
- properties 全局参数
- typeAliases 别名
- Plugins 插件
- objectFactory 对象工厂
- objectWrapperFactory 对象包装工厂
- reflectionFactory 反射工厂
- settings 环境设置
- environments 数据库环境
- databaseldProvider 数据库标识
- typeHandlers 类型转换器
- Mappers 映射器
2、构建映射器的内部组成
当 XMLConfigBuilder 解析 XML 时,会将每一个 SQL 和其配置的内容保存起来,那么它是怎么保存的呢?
在 MyBatis 中一条SQL和它相关的配置信息是由3个部分组成的,它们分别是 MappedStatement 、SqlSource 、BoundSql
-
MappedStatement 作用是保存一个映射器节点( selectlinse deletelupdate )的内容。
它是一个类,包括许多我们配置的SQL、SQL的Id 、缓存信息、resultMap、parameterType、resultType、resultMap、languageDriver等重要配置内容。它还有一个重要的属性 sqISource, MyBatis通过读取它来获得某条SQL 配置的所有信息 -
SqlSource 是提供 BoundSql 对象的方法,它是 MappedStatement 的一个属性。注意,它是一个接口,而不是一个实现类,对它而言有这么重要的几个实现 DynamicSqlSource 、ProviderSqISource 、RawSqlSource、StaticSqlSource。它的作用是根据上下文和参数解析生成需要的 SQL ,比如动态SQL采取了DynamicSqISource配合参数进行解析后得到的,这个接口只定义了 个接口方法 getBoundSql(parameterObject),使用它就可以得到 BoundSql 对象
-
BoundSql 是一个结果对象,也就是 SqlSource 通过对 SQL 参数的联合解析得到的 SQL 和参数,它是建立 SQL 和参数的地方,它有3个常用的属性:sql、parameterObject、parameterMappings
三者的关系如下图:
其中 BoundSql 3个主要的属性作用如下:
- parameterObject 参数本身,可以传递简单对象、 POJO 、Map、 @Param 注解的参数
- parameterMappingsList 它的每一个元素都是 ParameterMapping 象。对象会描述参数,参数包括属性名称、表达式, javaType 、jdbcType、 typeHandler 等重要信息。通过它就可以实现参数和 SQL 结合,以便 PreparedStatement 能够通过它找到 parameterObject 对象的属性设置参, 使得程序能准确运行
- sql 属性就是书写在映射器里面的一条被 SqlSource 解析后的 SQL
3、构建SqlSessionFactory
MyBatis 会根据文件流先生成 Configuration 对象,进而构建SqlSessionFactory 对象。真正的难点在于构建 Configuration 象。
SqlSessionFactory =new SqlSessionFactoryBuilder() .build(inputStream);
SqlSession运行过程
1、映射器(Mapper)的动态代理
获取Mapper的代码
RoleMapper roleMapper = sqlSessioη.getMapper(RoleMapper.class);
其中的getMapper方法:
public class DefaultSqlSession implements SqlSession {
.......
Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this)
}
.......
}
使用Configuration对象的getMapper方法来回去对应接口的对象,继续追踪这个方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
它又运用了映射器的注册器 MapperRegistry 来获取对应的接口对象,如下:
public c lass MapperRegistry {
@SuppressWarnings (” unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//根据Mapper生成它对应的代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type) ;
if (mapperProxyFactory == null) {
throw new BindingException (” Type ” + type + ” i s not known to the MapperRegistry . ” );
}
try {
//根据Mapper的代理工厂生成代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException (”Error getting mapper instance. Cause: ”+ e , e);
}
}
.......
}
首先它判断是否注册一个 Mapper ,如果没有则会抛出异常信息,如果有,就会启用MapperProxyFactory 工厂来生成一个代理实例,生成代理实例的方法如下:
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface ;
private final Map<Method , MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod> ();
public MapperProxyFactory(Class<T> mapperInterface ) {
this.mapperInterface = mapperInterface ;
}
public Class<T> getMapperinterface( ) {
return mapperinterface;
}
public Map<Method , MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings (”unchecked”}
protected T new nstance(MapperProxy<T> rnapperProxy) {
//动态代理
return (T) Proxy.newProxyinstance(mapperInterface .getClassLoader(),new Class[]{mapperInterface }, rnapperProxy);
}
public T newinstance(SqlSession sqlSession) {
//根据mapperInterface生成一个代理对象
final MapperProxy<T> mapperProxy= new MapperProxy<T>(sqlSession,mapperInterface ,methodCache);
return newinstance(mapperProxy);
}
Mapper 映射是通过动态代理来实现的。这里可以看到动态代理对接口的绑定,它的作用就是生成动态代理对象(占位),而代理的方法则被放到了 MapperProxy 类中。再探讨一下 MapperProxy 源码:
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method , Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method . invoke (this, args);
} catch (Throwable t) {
throw ExceptionUtil . unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession , args);
}
.......
}
可以看到这里的 invoke 方法逻辑。如果 Mapper 是一个JDK 动态代理对象,那么它就会运行到 invoke 方法里面。 invoke 首先判断是否是一个类,这里 Mapper 是一个接口不是一个类,所以判定失败。然后会生成 MapperMethod 对象,它是通过 cachedMapperMethod 方法对其初始化的。最后执行 execute 方法,把 SqlSession 当前运行的参数传递进去。execute 方法中最后其实是通过Sqlsession 对象去运行SQL。
Mapper 的 XML 文件的命名空间对应的是这个接口的全限定名,而方法就是那条 SQL的id ,这样 MyBatis 就可以根据全路径和方法名,将其和代理对象绑定起来。通过动态代理技术,让这个接口运行起来起来式。最后使用 SqISession 接口的方法使得它能够执行对应的 SQL 。
2、SqlSession下的四大对象
实际上 SqlSession 的执行过程是Executor、StatementHandler、ParameterHandler、ResultSetHandler 来完成数据库操作和结果返回的,简称为四大对象。
-
Executor(执行器) 代表执行器 由它调度 StatementHandler、ParameterHandler、ResultSetHandler等来执行对应的 SQL ,在构建 Executor 时会通过 interceptorChain.pluginAll(executor) 这样的一行代码创建一层层的动态代理对象,可以修改在调度真实的Executor 方法之前执行配置插件的代码,这就是插件的原理。
-
StatementHandler(数据库会话器) 的作用是使用数据库 Statement ( PreparedStatement )执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
-
ParameterHandler(参数处理器) 是用来处理SQL参数的。
-
ResultSetHandler(结果处理器) 是进行数据集( ResultSet )的封装返回处理的。
一条查询 SQL 的执行过程:Executor 先调用 StatementHandler prepare()方法预编译
SQL ,同时设置 些基本运行的参数。然后用 parameterize() 方法启用 ParameterHandler
置参数,完成预编译,执行查询, update()也是这样的 。如果是查询 MyBatis 会使用
ResultSetHandler 封装结果返回给调用者。
3、SqlSession运行总结
SqlSession 是通过执行器 Executor 调度 StatementHandler 来运行的 StatementHandler
经过3步:
- prepared 预编译 SQL
- parameterize 设置参数
- quey/update 执行 SQL
其中, parameterize 是调用 parameterHandler 的方法设置的,而参数是根据类型处理器
typeHandler 处理的。 query/update 方法通过 ResultSetHandler 进行处理结果的封装,如果是update 语句,就返回整数,否则就通过 typeHandler 处理结果类型,然后用 ObjectFactory 提供的规则组装对象,返回给调用者。这便是 SqI Session 执行的过程。
本文地址:https://blog.csdn.net/zw791029369/article/details/110954856