当前位置:首页 » 《休闲阅读》 » 正文

Java-16 深入浅出 MyBatis - SqlSession Executor StatementHandler 源码分析

0 人参与  2024年12月28日 14:01  分类 : 《休闲阅读》  评论

点击全文阅读


点一下关注吧!!!非常感谢!!持续更新!!!

大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html

在这里插入图片描述

# 目前已经更新到了:

MyBatis(正在更新)

架构设计

在这里插入图片描述
把 MyBatis 的功能架构分为三层:

API 接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操作数据库,接口层收到调用请求就会调用数据处理层来完成具体的数据处理。(MyBatis 与数据库的交互有两种方法,通过 MyBatis 的 API、通过 Mapper 代理的方式)数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用请求完成一次数据库的操作。基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

主要构件

在这里插入图片描述

层次结构

在这里插入图片描述

总体流程

加载配置初始化

触发条件:加载配置文件
配置来源于两个地方,一个是配置文件(主配置文件 config.xml、mapper.xml),一个是 Java 代码中的注解,将主配置内容解析封装到 Configuration,将 SQL 的配置信息加载为一个 mappedstatement 对象,存储在内存之中。

接受调用请求

触发条件:调用 MyBatis 的 API传入参数:为 SQL 的 ID 和传入参数对象处理过程:将请求传递给下层的请求处理层进行处理

处理操作请求

触发条件:API 接口层传递请求过来
传入参数:为 SQL 的 ID 和传入参数对象
处理过程:

根据 SQL 的 ID 查找对应的 MappedStatement 对象根据传入参数对象解析 MappedStatement 对象,得到最终要执行的 SQL 和执行传入的对象获取数据库连接,根据得到的最终 SQL 语句和执行传入参数到数据库进行执行,并得到结果。根据 MappedStatement 对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。释放连接资源返回处理结果

源码剖析

初始化

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 是一个接口,它有两个实现类:

DefaultSqlSessionSqlSessionManager

SqlSession 是 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 的功能和作用是:

根据传递的参数,完成 SQL 的动态解析,生成 Bound SQL 对象,供 StatementHandler 使用为查询创建缓存,来提高性能创建 JDBC 的 Statement 连接对象,传递给 StatementHandler 对象,返回 List 查询对象。

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);}

点击全文阅读


本文链接:http://zhangshiyu.com/post/208257.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1