Java-22 深入浅出 MyBatis - 手写ORM框架3 手写SqlSession、Executor 工作原理

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

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

在这里插入图片描述

目前已经更新到了:

  • MyBatis(正在更新)

框架实现

上节已经实现了部分内容 下面我们继续

SqlSession 相关

SqlSessionFactoryBuilder

public class SqlSessionFactoryBuilder {private Configuration configuration;public SqlSessionFactoryBuilder() {configuration = new Configuration();}public SqlSessionFactory build(InputStream inputStream) throws DocumentException, PropertyVetoException, ClassNotFoundException {XMLConfigerBuilder xmlConfigerBuilder = new XMLConfigerBuilder(configuration);Configuration conf = xmlConfigerBuilder.parseConfiguration(inputStream);return new DefaultSqlSessionFactory(conf);}}

SqlSessionFactory

public interface SqlSessionFactory {SqlSession openSession();}

SqlSession

public interface SqlSession {<E> List<E> selectList(String statementId, Object ...params) throws Exception;<T> T selectOne(String statementId, Object ...params) throws Exception;void close() throws Exception;
}

DefaultSqlSession

@AllArgsConstructor
public class DefaultSqlSession implements SqlSession {private Configuration configuration;private Executor simpleExecutor = new SimpleExecutor();public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;}@Overridepublic <E> List<E> selectList(String statementId, Object... params) throws Exception {MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);// System.out.println("DefaultSqlSession => selectList => + " + "statementId: " + statementId + ", mappedStatement: " + mappedStatement);return simpleExecutor.query(configuration, mappedStatement, params);}@Overridepublic <T> T selectOne(String statementId, Object... params) throws Exception {List<Object> objects = selectList(statementId, params);if (objects.size() == 1) {return (T) objects.get(0);}throw new RuntimeException("DefaultSqlSession selectOne 返回结果不唯一: " + statementId);}@Overridepublic void close() throws Exception {simpleExecutor.close();}@Overridepublic <T> T getMapper(Class<?> mapperClass) {Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}String className = method.getDeclaringClass().getName();String statementId = className + "." + methodName;Type genericReturnType = method.getGenericReturnType();if(genericReturnType instanceof ParameterizedType){List<Object> objects = selectList(statementId, args);return objects;}return selectOne(statementId,args);}});return (T) proxyInstance;}
}

请添加图片描述

类定义与注解

AllArgsConstructor:
用于生成包含所有字段的构造方法,简化代码。
表示可以用所有字段直接构造一个 DefaultSqlSession 对象。

DefaultSqlSession:
实现了 SqlSession 接口,作为 MyBatis 的核心会话管理类。
包含配置 (Configuration) 和执行器 (Executor) 的实例。

属性

  • private Configuration configuration:保存配置信息,例如 MappedStatement 映射等,负责管理 SQL 的元数据。
  • private Executor simpleExecutor = new SimpleExecutor();默认执行器,用于执行 SQL 语句并返回结果。初始化为 SimpleExecutor 实例。

工作原理总结

查询流程:

  • 用户通过 Mapper 接口调用方法。
  • 动态代理拦截调用,根据方法签名生成 statementId。
  • 调用 selectList 或 selectOne 执行查询。
  • 返回查询结果。

核心组件:

  • Configuration:管理配置信息。
  • MappedStatement:描述 SQL 语句和其映射信息。
  • Ezecutor:负责执行 SQL 并返回结果。
  • Dynamic Proxy:动态生成 Mapper 接口实现,简化用户调用。

异常处理:

  • 当查询结果不唯一时,selectOne 方法会抛出异常。
  • 这种约束确保单条查询返回的结果始终是明确的。

构造方法

DefaultSqlSession(Configuration configuration):

  • 接受一个 Configuration 对象并赋值给类中的 configuration 属性。
  • 确保该对象的实例在初始化时具备必要的配置信息。

Executor 相关

Executor

public interface Executor {<E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception;void close() throws Exception;
}

请添加图片描述

DefaultExecutor

public class SimpleExecutor implements Executor {private Connection connection;@Overridepublic <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws Exception {connection = configuration.getDataSource().getConnection();String sql = mappedStatement.getSql();BoundSql boundSql = getBoundSql(sql);PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());String parameterType = mappedStatement.getParameterType();Class<?> parameterTypeClass = getClassType(parameterType);List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();int n = 0;for (ParameterMapping pm : parameterMappingList) {String content = pm.getName();Field declaredField = parameterTypeClass.getDeclaredField(content);declaredField.setAccessible(true);Object object = declaredField.get(params[0]);preparedStatement.setObject(n + 1, object);}// 执行sqlResultSet resultSet = preparedStatement.executeQuery();String resultType = mappedStatement.getResultType();Class<?> resultTypeClass = getClassType(resultType);List<Object> objects = new ArrayList<>();// 封装返回结果集while (resultSet.next()) {Object o = resultTypeClass.newInstance();// 元数据ResultSetMetaData metaData = resultSet.getMetaData();for (int i = 1; i <= metaData.getColumnCount(); i ++) {// 字段名String columnName = metaData.getColumnName(i);// 字段的值Object value = resultSet.getObject(columnName);// 反射 根据数据库和实体 完成PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);Method writeMethod = propertyDescriptor.getWriteMethod();writeMethod.invoke(o, resultTypeClass.getDeclaredField(columnName).getType().cast(value));}objects.add(o);}return (List<E>) objects;}private BoundSql getBoundSql(String sql) {// 标记处理类 配置标记解析器来完成对占位符的解析处理工作ParameterMappingTokenHandler parameterMappingHandler = new ParameterMappingTokenHandler();GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}",parameterMappingHandler);// 解析出来的sqlString parseSql = genericTokenParser.parse(sql);// #{} 里边的参数List<ParameterMapping> parameterMapping = parameterMappingHandler.getParameterMappings();BoundSql boundSql = new BoundSql(parseSql, parameterMapping);System.out.println("SimpleExecutor getBoundSql: " + boundSql.getSqlText());return boundSql;}private Class<?> getClassType(String parameterType) throws ClassNotFoundException {if(parameterType != null){return Class.forName(parameterType);}return null;}@Overridepublic void close() throws Exception {connection.close();}
}

请添加图片描述

类的作用

SimpleExecutor 类的主要作用是:

  • 根据传入的 Configuration 和 MappedStatement 来执行 SQL 查询。
  • 通过 JDBC 操作数据库,并使用反射将查询结果映射为指定类型的 Java 对象。
  • 支持参数绑定和动态 SQL 解析(如 #{} 占位符的处理)。

代码逻辑解析

  • 类成员:Connection connection: 用于管理数据库连接。

核心方法:query

实现 Executor 接口的查询功能,逻辑分为以下几步:

建立数据库连接:

connection = configuration.getDataSource().getConnection();

获取并解析 SQL:

  • 通过 MappedStatement 获取 SQL 模板(含占位符 #{})。
  • 调用 getBoundSql 方法,将 #{} 替换为 ? 并获取参数信息。

创建 PreparedStatement 并绑定参数:

  • 根据参数类型信息,通过反射获取参数值。
  • 使用 JDBC 的 PreparedStatement 完成 SQL 的参数设置。

执行查询并处理结果集:

  • 通过 ResultSet 获取查询结果。
  • 使用反射动态构建结果对象,将结果集中的数据填充到指定的 Java 类型中。

适用场景

该类是 MyBatis 的核心实现之一,适合用来:

  • 实现数据库操作的封装。
  • 提供动态代理支持的 Mapper 接口。
  • 管理 SQL 查询的执行过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/889182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 逆向/反编译/Hook修改应用行为 基础实现

前言&#xff1a;本文通过一个简单的情景案例实现安卓逆向的基本操作 一、情景描述 本文通过一个简单的情景案例来实现安卓逆向的基本操作。在这个案例中所使用的项目程序是我自己的Demo程序&#xff0c;不会造成任何的财产侵害&#xff0c;本文仅作为日常记录及案例分享。实…

IDEA创建Spring Boot项目配置阿里云Spring Initializr Server URL【详细教程-轻松学会】

1.首先打开idea选择新建项目 2.选择Spring Boot框架(就是选择Spring Initializr这个) 3.点击中间界面Server URL后面的三个点更换为阿里云的Server URL Idea中默认的Server URL地址&#xff1a;https://start.spring.io/ 修改为阿里云Server URL地址&#xff1a;https://star…

基于MATLAB的信号处理工具:信号分析器

信号&#xff08;或时间序列&#xff09;是与特定时间相关的一系列数字或测量值&#xff0c;不同的行业和学科将这一与时间相关的数字序列称为信号或时间序列。生物医学或电气工程师会将其称为信号&#xff0c;而统计学家或金融定量分析师会使用时间序列这一术语。例如&#xf…

Plugin - 插件开发03_Spring Boot动态插件化与热加载

文章目录 Pre方案概览使用插件的好处流程CodePlugin 定义Plugin 实现Plugin 使用方动态加载插件类加载器注册与卸载插件配置文件启动类测试验证 小结 Pre 插件 - 通过SPI方式实现插件管理 插件 - 一份配置&#xff0c;离插件机制只有一步之遥 插件 - 插件机制触手可及 Plug…

ECharts柱状图-阶梯瀑布图,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

C/C++流星雨

系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C/C…

ModelScope-Agent(1): 基于开源大语言模型的可定制Agent系统

目录 简介快速入门 简介 github地址 快速入门 看前两篇&#xff0c;调用千问API和天气API # 选用RolePlay 配置agent from modelscope_agent.agents.role_play import RolePlay # NOQArole_template 你扮演一个天气预报助手&#xff0c;你需要查询相应地区的天气&#x…

【模型对比】ChatGPT vs Kimi vs 文心一言那个更好用?数据详细解析,找出最适合你的AI辅助工具!

在这个人工智能迅猛发展的时代&#xff0c;AI聊天助手已经深入我们的工作与生活。你是否曾在选择使用ChatGPT、Kimi或是百度的文心一言时感到一头雾水&#xff1f;每款AI都有其独特的魅力与优势&#xff0c;那么&#xff0c;究竟哪一款AI聊天助手最适合你呢&#xff1f;本文将带…

微信小程序uni-app+vue3实现局部上下拉刷新和scroll-view动态高度计算

微信小程序uni-appvue3实现局部上下拉刷新和scroll-view动态高度计算 前言 在uni-appvue3项目开发中,经常需要实现列表的局部上下拉刷新功能。由于网上相关教程较少且比较零散,本文将详细介绍如何使用scroll-view组件实现这一功能,包括动态高度计算、下拉刷新、上拉加载等完整…

SQL——DQL分组聚合

分组聚合&#xff1a; 格式&#xff1a; select 聚合函数1(聚合的列),聚合函数2(聚合的列) from 表名 group by 标识列; ###若想方便分辨聚合后数据可在聚合函数前加上标识列&#xff08;以标识列进行分组&#xff09; 常见的聚合函数: sum(列名):求和函数 avg(列名)…

maven打包时出现找不到符号的错误如何解决

在maven打包的时候有时会出现找不到符号的情况&#xff0c;具体原因是由于引用的BaseEntity是framework模块下的实体类&#xff0c;所以需要将framework重新clean再install&#xff0c;成功后再将我们的模块打包就成功了

openGauss开源数据库实战二十一

文章目录 任务二十一 使用JDBC访问openGauss数据库任务目标实施步骤一、准备工作 二、下载并安装JavaSE81 下载JavaSE8安装Java8SE并配置环境变量 三、下载并安装eclipse四、下载并安装openGauss的JDBC驱动包五、使用IDEA编写JDBC测试程序1 使用IDEA的SSH连接虚拟机2 创建项目并…

Git:常用命令

一、查看当前分支 git branch 二、查看所有分支 git branch -a 三、切换到远程分支 git checkout origin/分支名 示例&#xff1a;git checkout origin/dev 四、拉取远程分支代码 git pull origin 分支名 示例&#xff1a;git pull origin dev 五、常用指令 查看暂存区…

运维实战:K8s 上的 Doris 高可用集群最佳实践

今天我们将深入探讨&#xff1a;&#xff1a;如何在 K8s 集群上部署 Compute storage coupled&#xff08;存算耦合&#xff09; 模式的 Doris 高可用集群&#xff1f; 本文&#xff0c;我将为您提供一份全面的实战指南&#xff0c;逐步引导您完成以下关键任务&#xff1a; 配…

在GITHUB上传本地文件指南(详细图文版)

这份笔记简述了如何在GITHUB上上传文件夹的详细策略。 既是对自己未来的一个参考&#xff0c;又希望能给各位读者带来帮助。 详细步骤 打开目标文件夹&#xff08;想要上传的文件夹&#xff09; 右击点击git bash打开 GitHub创立新的仓库后&#xff0c;点击右上方CODE绿色按…

Vue框架入门

Author&#xff1a;Dawn_T17?? 目录 什么是框架 一.Vue 的使用方向 二.Vue 框架的使用场景 &#xff08;TIP&#xff09;MVVM思想 三.Vue入门案例 TIP&#xff1a;插值表达式 四.Vue-指令? &#xff08;1&#xff09;v-bind 和 v-model? ? &#xff08;2&#x…

FPGA 遍历读 LMK04803 寄存器

主要思路&#xff1a; 1.使用 VIO 输出信号控制什么时候开始读LMK04803寄存器 2.遍历LMK04803所有寄存器&#xff0c;将读到的每个寄存器的值显示在VIO上。 3.遍历指的是 从 R0 开始读&#xff0c;R0读完接着读 R1&#xff0c;一直到R31 结束 4.注意的是写寄存器是 32bit &…

【uni-app 微信小程序】新版本发布提示用户进行更新

知识准备 uni.getUpdateManager文档介绍 不支持APP与H5&#xff0c;所以在使用的时候要做好平台类型的判断&#xff0c;如何判断&#xff0c;参考条件编译处理多端差异 代码参考 export const updateApp () > {const updateManager uni.getUpdateManager()updateManag…

vue实现点击左右按钮横向滚动

html部分 <div ref"tabHeaderRef" class"flex items-center tabs_header"><div class"tab-pre" v-if"hidePre" click"leftPre"><i class"el-icon-arrow-left"></i></div><div r…

数据结构(3)单链表的模拟实现

上一节我们进行了数据结构中的顺序表的模拟式现&#xff0c;今天我们来实现一下另外一个数据结构&#xff1a;单链表。 我们在实现顺序表之后一定会引发一些问题和思考&#xff1a; 1.顺序表在头部和中间插入数据会用到循环&#xff0c;时间复杂O&#xff08;N&#xff09; …