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

    @Override
    public <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);
    }

    @Override
    public <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);
    }

    @Override
    public void close() throws Exception {
        simpleExecutor.close();
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public 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;

    @Override
    public <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);
        }

        // 执行sql
        ResultSet 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
        );
        // 解析出来的sql
        String 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;
    }

    @Override
    public 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 查询的执行过程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Git:常用命令

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

算法1(蓝桥杯18)-删除链表的倒数第 N 个节点

问题&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个节点&#xff0c;并且返回链表的头节点。 输入&#xff1a;head 1 -> 2 -> 3 -> 4 -> 5 -> null, n 2 输出&#xff1a;1 -> 2 -> 3 -> 5 -> null输入&#xff1a;head 1 ->…

H5接入Steam 获取用户数据案例 使用 OpenID 登录绑定公司APP账户 steam公开用户信息获取 steam webapi文档使用

官方文档地址 1.注册 Steam API Key&#xff1a; 你需要一个 Steam Web API Key&#xff0c;可以在 Steam API Key 页面 获取。https://steamcommunity.com/dev/apikey 这里开发做demo用自己steam账户的就好&#xff0c;后续上线要用公司的账户 2.使用 OpenID 登录&#xff…

TCP的“可靠性”(上)

目录 TCP的“可靠性”&#xff08;上&#xff09;确认应答&#xff08;可靠性传输的基础&#xff09;超时重传连接管理&#xff08;三次握手&#xff0c;四次挥手&#xff09; TCP的“可靠性”&#xff08;上&#xff09; 想必大家都或多或少的听说过TCP的特性&#xff1a;有连…

九、页面级变量的状态管理

状态管理概述 在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。 自定义组件拥有变量,变…

vs打开unity项目 新建文件后无法自动补全

问题 第一次双击c#文件自动打开vs编辑器的时候能自动补全&#xff0c;再一次在unity中新建c#文件后双击打开发现vs不能自动补全了。每次都要重新打开vs编辑器才能自动补全&#xff0c;导致效率很低&#xff0c;后面发现是没有安装扩展&#xff0c;注意扩展和工具的区别。 解决…

责任链模式的理解和实践

责任链模式&#xff08;Chain of Responsibility&#xff09;是行为型设计模式之一&#xff0c;它通过将多个对象连成一条链&#xff0c;并沿着这条链传递请求&#xff0c;直到有对象处理它为止。这个模式的主要目的是将请求的发送者和接收者解耦&#xff0c;使请求沿着处理链传…

软件工程——期末复习(3)

一、题目类(老师重点提到过的题目) 1、高可靠性是否意味着高可用性&#xff1f;试举例证明自己的观点&#xff1f; 答&#xff1a;高可靠性不意味着高可用性 可靠性说明系统已经准备好&#xff0c;马上可以使用&#xff1b;可用性是系统可以无故障的持续运行&#xff0c;是一…

SList(单链表)

文章目录 一&#xff1a;线性表二&#xff1a;数组2.1数组在内存中的存储 三&#xff1a;链式结构四&#xff1a;单链表4.1概念与结构4.1.1概念4.1.2 结构&#xff08;节点&#xff09;4.1.3链表的性质4.1.4链表的打印 4.2实现单链表 结语 欢迎大家来到我的博客&#xff0c;给生…

VTK知识学习(21)- 数据的读写

1、前言 对于应用程序而言&#xff0c;都需要处理特定的数据&#xff0c;VTK应用程序也不例外。 VTK应用程序所需的数据可以通过两种途径获取: 第一种是生成模型&#xff0c;然后处理这些模型数据(如由类 vtkCylinderSource 生成的多边形数据); 第二种是从外部存储介质里导…

Nignx部署Java服务测试使用的Spring Boot项目Demo

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

虚幻引擎生存建造系统

先做一个建造预览模式&#xff0c;按下按键B后进入建造预览模式 首先创建自定义事件Preview Loop 用射线追踪摆放物体预览位置&#xff0c;并做一个预览材质 增强输入设置按键 每帧判断是否进入建造模式 预览模式制作成功&#xff01; 接着做点击左键放置物品&#xff0…

Blender中使用BlenderGIS插件快速生成城市建筑模型

导入下载 BlenderGIS 插件 去github上下载其压缩包&#xff0c;地址如下&#xff1a; https://github.com/domlysz/BlenderGIS 在BlenderGIS中导入这个插件压缩包&#xff1a; 点击上方菜单栏的编辑&#xff0c;点击偏好设置 在插件>从磁盘安装中导入刚刚下载的压缩包 可…

C语言期末复习

1、任意输入一个半径给r&#xff0c;求圆的面积。 #include <stdio.h> #include <windows.h> void main() { double r,s; printf("输入一个半径给r"); scanf("%lf",&r); sr*r*3.1415926; printf("%lf",s); system(&qu…

深圳大学《2024年904自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《深圳大学904自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

transformers生成式对话机器人

简介 生成式对话机器人是一种先进的人工智能系统&#xff0c;它能够通过学习大量的自然语言数据来模拟人类进行开放、连贯且创造性的对话。与基于规则或检索式的聊天机器人不同&#xff0c;生成式对话机器人并不局限于预定义的回答集&#xff0c;而是可以根据对话上下文动态地…

NanoLog起步笔记-4-Server端的两个线程

nonolog起步笔记-4-Server端的两个线程 Server端的两个线程两个线程的角色与各自的职责RuntimeLogger::compressionThreadMain线程 详细学习一下相关的代码第三个线程第一次出现原位置swip buffer Server端的两个线程 如前所述&#xff0c;nanolog的server端&#xff0c;相对而…

Freertos任务切换

一、操作系统进行任务切换的时机&#xff1a; 采用信号量实现任务的互斥&#xff1a; 二、FreeRTOS 任务切换场合 PendSV 中断的时候提到了上下文(任务)切换被触发的场合&#xff1a; ● 可以执行一个系统调用 ● 系统滴答定时器(SysTick)中断。 1、执行系统调用 执行系统…

【硬件测试】基于FPGA的4FSK调制解调通信系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 3.Verilog核心程序 4.开发板使用说明和如何移植不同的开发板 5.完整算法代码文件获得 1.算法仿真效果 本文是之前写的文章: 《基于FPGA的4FSK调制解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR》 的…

【Vue2+Element-ui】el-dialog宽度适配

1、不适配问题 分辨率100%-页面 分辨率150%-页面 在项目中&#xff0c;我开发分辨率一直是100%&#xff0c;但是客户使用的分辨率不相同&#xff0c;所以宽度要适配 2、解决-封装mixins.js 1)、封装的mixins 我将宽度设置成动态的&#xff0c;因为我的项目中需求不同。 expor…