点一下关注吧!!!非常感谢!!持续更新!!!
大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html
# 目前已经更新到了:
MyBatis(正在更新)架构设计
把 MyBatis 的功能架构分为三层:
主要构件
层次结构
总体流程
加载配置初始化
触发条件:加载配置文件
配置来源于两个地方,一个是配置文件(主配置文件 config.xml、mapper.xml),一个是 Java 代码中的注解,将主配置内容解析封装到 Configuration,将 SQL 的配置信息加载为一个 mappedstatement 对象,存储在内存之中。
接受调用请求
触发条件:调用 MyBatis 的 API传入参数:为 SQL 的 ID 和传入参数对象处理过程:将请求传递给下层的请求处理层进行处理处理操作请求
触发条件:API 接口层传递请求过来
传入参数:为 SQL 的 ID 和传入参数对象
处理过程:
源码剖析
初始化
Inputstream inputstream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
初始化的 build 方法中,如下:
// 1. 最初调用的 build 方法public SqlSessionFactory build(InputStream inputStream) { // 调用了重载方法,传入默认的环境和属性 return build(inputStream, null, null);}// 2. 重载的 build 方法public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // XMLConfigBuilder 负责解析 MyBatis 的配置文件 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); // 解析配置文件并返回 Configuration 对象,再调用重载的 build 方法 return build(parser.parse()); } catch (Exception e) { // 异常处理:将异常包装成一个运行时异常并抛出 throw ExceptionFactory.wrapException("Error building SqlSession.", e); }}
MyBatis 在初始化的时候,会将 MyBatis的配置信息全部加载到内存中,使用 org.apache.ibatis.session.Configuration 实例来维护。
/** * 解析 XML 配置并转换为 Configuration 对象。 */public Configuration parse() { // 如果已解析,抛出 BuilderException 异常 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } // 标记为已解析 parsed = true; // 解析 XML 配置根节点 parseConfiguration(parser.evalNode("/configuration")); return configuration;}/** * 解析 XML 配置节点并逐一处理各个标签。 */private void parseConfiguration(XNode root) { try { // 按顺序解析各个 XML 配置部分 // 1. 解析 <properties /> 标签 propertiesElement(root.evalNode("properties")); // 2. 解析 <settings /> 标签并转换为 Properties 对象 Properties settings = settingsAsProperties(root.evalNode("settings")); // 3. 加载自定义 VFS 实现类 loadCustomVfs(settings); // 4. 解析 <typeAliases /> 标签 typeAliasesElement(root.evalNode("typeAliases")); // 5. 解析 <plugins /> 标签 pluginElement(root.evalNode("plugins")); // 6. 解析 <objectFactory /> 标签 objectFactoryElement(root.evalNode("objectFactory")); // 7. 解析 <objectWrapperFactory /> 标签 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 8. 解析 <reflectorFactory /> 标签 reflectorFactoryElement(root.evalNode("reflectorFactory")); // 9. 将解析得到的 settings 配置赋值给 Configuration 对象 settingsElement(settings); // 10. 解析 <environments /> 标签 environmentsElement(root.evalNode("environments")); // 11. 解析 <databaseIdProvider /> 标签 databaseldProviderElement(root.evalNode("databaseldProvider")); // 12. 解析 <typeHandlers /> 标签 typeHandlerElement(root.evalNode("typeHandlers")); // 13. 解析 <mappers /> 标签 mapperElement(root.evalNode("mappers")); } catch (Exception e) { // 捕获解析过程中可能出现的异常并抛出自定义异常 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); }}
介绍一下 MappedStatement:MappedStatement 与 Mapper 配置文件中的一个select/update/insert/delete 节点相对应。
mapper 中配置的标签都被封装到了此对象中,主要用途是描述一条 SQL 语句。
刚开始介绍配置文件的过程中,会对 mybatis-config.xml 中的各个标签都进行解析,其中有 mappers 标签用引入 mapper.xml 文件或者配置 mapper 接口目录。
<select id="getUser" resultType="user" > select * from user where id=#{id}</select>
一个 Select 标签会在初始化配置文件时,被解析封装成一个 MappedStatement 对象,然后存储在 Configuration 对象的 MappedStatements 属性中,它是一个 HashMap,存储时 key=全限定类名+方法名,value=MappedStatement 对象。
在 XMLConfigBuilder 中的处理:
private void parseConfiguration(XNode root) { try { // 省略其他标签的处理 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); }}
到此 XML 配置文件的解析就结束了,回到步骤 2 中调用重载的 build 方法。
public SqlSessionFactory build(Configuration config) { // 创建了 DefaultSqlSessionFactory 对象 return new DefaultSqlSessionFactory(config);}
SqlSession
先简单介绍 SqlSession,SqlSession 是一个接口,它有两个实现类:
DefaultSqlSessionSqlSessionManagerSqlSession 是 MyBatis 中用于和数据库交互的顶层类,通常将它与 ThreadLocal 绑定,一个回话使用一个 SqlSession,结束之后需要进行 close。
public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor; // 省略}
SqlSession 中最重要的参数:
Configuration 与初始化的时候相同Executor 为执行器Executor
Executor 也是一个接口,有三个常用实现类:
BatchExecutor 重用语句并执行批量更新ReuseExecutor 重用预处理语句 prepared statmentsSimpleExecutor 普通的执行器(默认的)当我们获取到 SqlSession 的时候:
// 6. 进入 openSession 方法。public SqlSession openSession() { // getDefaultExecutorType() 传递的是 SimpleExecutor // 通过 openSessionFromDataSource 方法创建 SqlSession return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}// 7. 进入 openSessionFromDataSource 方法。// ExecutorType 为 Executor 的类型,TransactionIsolationLevel 为事务隔离级别,autoCommit 是否开启事务// openSession 的多个重载方法可以指定获得的 SqlSession 的 Executor 类型和事务的处理private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 获取当前环境配置 final Environment environment = configuration.getEnvironment(); // 获取事务工厂 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 创建事务对象,传入数据源、事务隔离级别和是否自动提交 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 根据指定的 Executor 类型和事务创建 Executor final Executor executor = configuration.newExecutor(tx, execType); // 返回一个 DefaultSqlSession 对象,传入配置、executor 和 autoCommit 状态 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { // 捕获异常,关闭事务,可能已经获取了数据库连接,因此需要关闭 closeTransaction(tx); // 重新抛出异常,或处理异常逻辑 throw new RuntimeException("Error occurred while opening session", e); }}
后续可跟进 query 方法,最后会创建一个 StatementHandler 对象,然后将必要的参数传递给 StatementHandler,使用 StatementHandler 来完成对数据库的查询,最终返回 List 结果集。
从上面的代码中我们可以看出,Executor 的功能和作用是:
StatementHandler
StatementHandler 对象主要完成了两个工作:
对于 JDBC 的 PreparedStatement 类型的对象,创建的过程中,我们使用的是 SQL 语句字符串会包含若干个占位符,StatementHandler 通过 parameter(statement)方法对 Statement 进行设值StatementHandler 通过 List query 方法来完成执行 Statement,将 Statement 对象返回的 resultSet 封装成 List进入到 StatementHandler 的 parameterize 方法的实现:
public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement);}