欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

读书笔记——MyBatis核心解析和运行原理

程序员文章站 2022-03-10 18:28:56
构建SqlSessionFactory的过程1、构建ConfigurationConfiguration的作用:读入配置文件,包括基础配置的 XML 和映射器 XML (或注解〉。初始化一些基础配置,比如 MyBatis 的别名等,一些重要的类对象(比如插件、映射器、 Object 工厂、 typeHandlers 对象等)。提供单例,为后续创建 SessionFactorγ 服务,提供配置的参数。执行一些重要对象的初始化方法。Confinguration 是通过 XMLConfigBui...

构建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

三者的关系如下图:
读书笔记——MyBatis核心解析和运行原理

其中 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运行总结

读书笔记——MyBatis核心解析和运行原理
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