【简写Mybatis】02-注册机的实现以及SqlSession处理

前言

注意:

学习源码一定一定不要太关注代码的编写,而是注意代码实现思想:

通过设问方式来体现代码中的思想;方法:5W+1H

源代码:https://gitee.com/xbhog/mybatis-xbhoghttps://github.com/xbhog/mybatis-xbhog;交个朋友,有价值欢迎star。

回顾&分析

上一局实现Mapper接口和映射器通过代理类的方式进行链接。

上一局测试类:【简写MyBatis】01-简单映射器;虽然基本功能实现了,但是还不智能,可以发现该测试类中的映射器代理工厂只能实现单一的接口代理,SqlSession也需要规范化处理;将映射器代理和方法的调用类似服务进行包装。

@Test
public void test_MapperProxyFactory() {
    MapperProxyFactory<IUserDao> factory = new MapperProxyFactory<>(IUserDao.class);

    Map<String, String> sqlSession = new HashMap<>();
    sqlSession .put("com.xbhog.IUserDao.getUserName","模拟执行 Mapper.xml 中 SQL 语句的操作:查询用户姓名");
    IUserDao userDao = factory.newInstance(sqlSession);

    String res = userDao.queryUserName("10001");
    logger.info("测试结果:{}", res);
}

目的

  1. 根据包路径实现接口的扫描和注册
  2. SqlSession规范化处理

实现

项目结构:

└─src 
  ├─main 
  │ └─java 
  │   └─com 
  │     └─xbhog 
  │       ├─binding 
  │       │ ├─MapperProxy.java 
  │       │ ├─MapperProxyFactory.java 
  │       │ └─MapperRegistry.java 
  │       └─session 
  │         ├─DefaultSqlSession.java 
  │         ├─DefaultSqlSessionFactory.java 
  │         ├─SqlSession.java 
  │         └─SqlSessionFactory.java 
  └─test 
    └─java 
      └─com 
        └─xbhog 
          ├─AppTest.java 
          └─IUserDao.java 

项目类图

img

根据包路径实现接口的扫描和注册

可以通过自定义MapperRegistry这个类实现该功能,当然你也可以叫其他的名字(zhangsan、lisi😅);我们只需要将上一局的MapperProxyFactory封装到MapperRegistry,把需要扫描和注册的接口保存到Map中进行预处理,最后代理进行随时使用就可以了;铺垫结束,开始上代码。

先扫描包下所有的Class类,然后保存到Map中。

package com.xbhog;

import cn.hutool.core.lang.ClassScanner;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author xbhog
 * @describe: 接口注册器
 * @date 2024/2/25
 */
public class MapperRegistry {

    private final Map<Class<?>,MapperProxyFactory<?>> interfaceMaps = new HashMap<>();
    public void addMapper(String packageName){
        Set<Class<?>> scanPackage = ClassScanner.scanPackage(packageName);
        scanPackage.forEach(clazz -> {
            addMappers(clazz);
        });
    }

    private void addMappers(Class<?> clazz) {
        if(clazz.isInterface()){
            //判断是否重复添加
            if(haveInterface(clazz)){
                throw new RuntimeException("Type " + clazz + " is already known to the MapperRegistry.");
            }
        }
        // 注册映射器代理工厂
        interfaceMaps.put(clazz, new MapperProxyFactory<>(clazz));
    }

    private boolean haveInterface(Class<?> clazz) {
        return interfaceMaps.containsKey(clazz);
    }

}

然后将上一局的接口和代理工厂操作封装进方法中。

public <T> T getMapper(Class<T> type, SqlSession sqlSession){
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) interfaceMaps.get(type);
    if(Objects.isNull(mapperProxyFactory)){
        throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");
    }
    return (T)mapperProxyFactory.newInstance(sqlSession);
}

SqlSession规范化处理

先定义一个执行Sql、获取映射器的标准接口:

/**
 * @author xbhog
 * @describe: 定义简单的Mapper操作方法
 * @date 2024/2/25
 */
public interface SqlSession {

    <T> T selectOne(String statement,Object parameter);

    /**
     *得到接口映射器
     * @param type 接口类型
     * @return
     */
    <T> T getMapper(Class<T> type);
}

接口实现方式:

package com.xbhog.session;

import com.xbhog.binding.MapperRegistry;

/**
 * @author xbhog
 * @describe:
 * @date 2024/2/25
 */
public class DefaultSqlSession implements SqlSession{

    private MapperRegistry mapperRegistry;

    public DefaultSqlSession(MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
    }

    @Override
    public <T> T  selectOne(String statement,Object parameter) {
        return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter);
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return mapperRegistry.getMapper(type,this);
    }
}

测试一下:

package com.xbhog;

import com.xbhog.binding.MapperProxyFactory;
import com.xbhog.binding.MapperRegistry;
import com.xbhog.session.DefaultSqlSession;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import java.util.HashMap;
import java.util.Map;

/**
 * Unit test for simple App.
 */
public class AppTest extends TestCase {
    /**
     * Rigourous Test :-)
     */
    public void testApp() {
        MapperRegistry mapperRegistry = new MapperRegistry();
        mapperRegistry.addMapper("com.xbhog");
        DefaultSqlSession sqlSession = new DefaultSqlSession(mapperRegistry);
        IUserDao user = sqlSession.getMapper(IUserDao.class);
        String userName = user.getUserName("xbhog");
        System.out.println("输出的信息:"+userName);
    }
}

img

到这里其实已经可以满足需求了,但是看了下源码发现还是不行,它最外层又封装了一层代理工厂;应该是为了后续的代码扩展,简单工厂模式有助于代码的模块性和可维护性,功能上后续会有配置管理、资源管理、执行器选择和插件等需求;走一步看三步的老狐狸(┬┬﹏┬┬)。先抄作业。

package com.xbhog.session;

import com.xbhog.binding.MapperRegistry;

/**
 * @author xbhog
 * @describe:
 * @date 2024/2/25
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory{

    private final MapperRegistry mapperRegistry;

    public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {
        this.mapperRegistry = mapperRegistry;
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(mapperRegistry);
    }
}

测试

package com.xbhog;

import com.xbhog.binding.MapperProxyFactory;
import com.xbhog.binding.MapperRegistry;
import com.xbhog.session.DefaultSqlSession;
import com.xbhog.session.DefaultSqlSessionFactory;
import com.xbhog.session.SqlSession;
import com.xbhog.session.SqlSessionFactory;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

import java.util.HashMap;
import java.util.Map;

/**
 * Unit test for simple App.
 */
public class AppTest extends TestCase {
    /**
     * Rigourous Test :-)
     */
    public void testApp() {
        MapperRegistry mapperRegistry = new MapperRegistry();
        mapperRegistry.addMapper("com.xbhog");
        DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(mapperRegistry);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        IUserDao user = sqlSession.getMapper(IUserDao.class);
        String userName = user.getUserName("xbhog");
        System.out.println("输出的信息:"+userName);
    }
}

img

总结

  1. What(什么)
  • MapperRegistry是一个注册表,用于存储映射器接口(Mapper Interface)和对应的MapperProxyFactory。它负责管理映射器接口的生命周期。
  • DefaultSqlSessionFactory是MyBatis框架中用于创建SqlSession的工厂类。SqlSession是MyBatis的核心接口,用于执行SQL命令和获取映射结果。
  1. Why(为什么)
  • MapperRegistry的存在是为了确保映射器接口能够被MyBatis框架识别和管理,以便在运行时为这些接口创建代理对象,实现数据库操作的动态绑定。
  • DefaultSqlSessionFactory的目的是为了提供一个统一的入口点,用于创建和管理SqlSession实例。这样可以保证SqlSession的创建和关闭遵循统一的规范,同时提供了会话管理的能力。
  1. Who(谁)
  • MapperRegistry的使用者是MyBatis框架自身,它内部使用MapperRegistry来处理映射器接口的注册和代理对象的创建。
  • DefaultSqlSessionFactory的使用者是应用程序的开发者,他们通过SqlSessionFactory来获取SqlSession实例,进而执行数据库操作。
  1. Where(在哪里)
  • MapperRegistry是MyBatis框架的一部分,通常在MyBatis配置初始化时创建,并在整个应用程序的生命周期中存在。
  • DefaultSqlSessionFactory通常在应用程序启动时创建,并保存在一个全局的变量中,以便在需要时获取SqlSession实例。
  1. When(何时)
  • MapperRegistry的注册发生在MyBatis应用程序启动时,特别是在构建SqlSessionFactory的过程中。
  • DefaultSqlSessionFactory的创建也是在应用程序启动时,通常是在初始化阶段,用于后续的数据库操作。
  1. How(如何)
  • MapperRegistry通过扫描指定包下的映射器接口,并将它们与对应的MapperProxyFactory关联起来。当需要执行映射器接口中的方法时,MapperRegistry会使用MapperProxyFactory来创建一个MapperProxy代理对象。
  • DefaultSqlSessionFactory通过解析MyBatis的配置文件(如mybatis-config.xml,下一节的操作)来创建。它提供了openSession()方法,用于创建SqlSession实例。开发者可以通过SqlSession实例来执行映射器接口中定义的数据库操作。

需要注意的是:通过这两节可以看到mybatis中运用了大量的工厂模式;对外提供统一的方法,屏蔽细节以及上下文的关联关系,最终目的服务于用户,简化使用。

参考

https://mp.weixin.qq.com/s/o6lnWJqU_6FNO8HpxAs9gA

ChatGPT问答

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

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

相关文章

51单片机学习(5)-----蜂鸣器的介绍与使用

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 蜂鸣器的介绍 1.蜂鸣器介绍 2.压电式蜂鸣器 &#xff08;无源…

生成式 AI - Diffusion 模型的数学原理(5)

来自 论文《 Denoising Diffusion Probabilistic Model》&#xff08;DDPM&#xff09; 论文链接&#xff1a; https://arxiv.org/abs/2006.11239 Hung-yi Lee 课件整理 讲到这里还没有解决的问题是&#xff0c;为什么这里还要多加一个噪声。Denoise模型算出来的是高斯分布的均…

NeurIPS 2023 Spotlight | VoxDet:基于3D体素表征学习的新颖实例检测器

本文提出基于3D体素表征学习的新颖实例检测器VoxDet。给定目标实例的多视图&#xff0c;VoxDet建立该实例的三维体素表征。在更加杂乱的测试图片上&#xff0c;VoxDet使用体素匹配算法检测目标实例。实验表明&#xff0c;VoxDet中的三维体素表征与匹配比多种二维特征与匹配要更…

【深入理解设计模式】适配器设计模式

适配器设计模式 适配器设计模式是一种结构型设计模式&#xff0c;用于将一个类的接口转换成客户端所期望的另一个接口&#xff0c;从而使得原本由于接口不兼容而不能一起工作的类能够一起工作。适配器模式通常用于以下场景&#xff1a; 现有接口与需求不匹配&#xff1a;当需要…

IP对讲终端SV-6002(防水)

SV-6002&#xff08;防水&#xff09;是一款IP对讲终端&#xff0c;具有10/100M以太网接口&#xff0c;其接收网络的音频数据&#xff0c;解码后播放&#xff0c;外部DC12~24V电源供电端子&#xff0c;提供单路2W的音频输出。基于TCP/IP网络通信协议和数字音频技术&#xff0c;…

【Java EE初阶二十三】servlet的简单理解

1. 初识servlet Servlet 是一个比较古老的编写网站的方式&#xff0c;早起Java 编写网站,主要使用 Servlet 的方式&#xff0c;后来 Java 中产生了一个Spring(一套框架)&#xff0c;Spring 又是针对 Servlet 进行了进一步封装,从而让我们编写网站变的更简单了&#xff1b;Sprin…

都有金蝶了,也能开发报表,为什么要用BI?

很多企业在一开始时都会有这样的困惑&#xff1a;我都有金蝶ERP了&#xff0c;也能自己开发报表&#xff0c;为什么还要买BI&#xff1f; 答案是显而易见的&#xff0c;金蝶ERP毕竟不是专业的数据分析系统&#xff0c;它的主要任务是在企业管理流程上&#xff0c;虽然很多企业…

Linux内核网络

文章目录 前言网络协议栈图解功能 发送Linux内核网络数据包图解流程 接收Linux内核网络数据包图解流程 最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;因为Linux内核涉及的内容极多&#xff0c;我们初学者如果一上来就开始深挖细节&#xff0c;很有可能会在Linux内核代码…

MySQL - 事务日志

目录 1. redo日志 1.1 为什么需要REDO日志 1.2 REDO日志的好处、特点 1. 好处 2. 特点 1.3 redo的组成 1.4 redo的整体流程 1.5 redo log的刷盘策略 1.6 不同刷盘策略演示 1. 流程图 ​编辑2. 举例 1.7 写入redo log buffer 过程 1.8 redo log file 1. 相关参数…

[云原生] 二进制安装K8S(中)部署网络插件和DNS

书接上文&#xff0c;我们继续部署剩余的插件 一、K8s的CNI网络插件模式 2.1 k8s的三种网络模式 K8S 中 Pod 网络通信&#xff1a; &#xff08;1&#xff09;Pod 内容器与容器之间的通信 在同一个 Pod 内的容器&#xff08;Pod 内的容器是不会跨宿主机的&#xff09;共享…

批量解决opencv cv2.imread读取32位抠图png图像后,出现隐藏背景无法去除的问题

一、问题展示 1.原始png含蒙版抠图信息&#xff1a;位深度为32位&#xff0c;4通道图像信息&#xff0c;含蒙版背景信息 2.使用opencv读取保存后图像信息&#xff1a;位深度为24位&#xff0c;3通道图像信息&#xff0c;显示了扣除的背景 二、问题分析 1.用cv模块无法识别深度…

Stable Diffusion 绘画入门教程(webui)-ControlNet(线稿约束)

上篇文章介绍了openpose&#xff0c;本篇文章介绍下线稿约束&#xff0c;关于线稿约束有好几个处理器都属于此类型&#xff0c;但是有一些区别。 包含&#xff1a; 1、Canny(硬边缘&#xff09;&#xff1a;识别线条比较多比较细&#xff0c;一般用于更大程度得还原照片 2、ML…

C++ : string类

目录 介绍&#xff1a; string类的七种构造函数&#xff1a; string类的第三个构造函数 string类的第四个构造函数 string类的第五个构造函数 string类的第六个构造函数 string类常用的构造 string类对象的访问遍历操作 第一种遍历方式&#xff1a; 第二种遍历方…

使用Node.js开发RESTful API

在当今信息时代&#xff0c;不管是企业级应用还是个人项目&#xff0c;都离不开RESTful API的使用。RESTful API 是现代 web 应用的基石&#xff0c;通过它&#xff0c;我们可以实现前后端的高效沟通和数据传输。而在开发 RESTful API 的过程中&#xff0c;Node.js 的强大功能和…

AR应用的开发流程

增强现实&#xff08;Augmented Reality&#xff0c;AR&#xff09;是一种技术&#xff0c;它将虚拟信息叠加在真实世界中&#xff0c;通过计算机生成的视觉、听觉、触觉等感官反馈&#xff0c;将虚拟元素与现实世界进行交互。这种技术使得用户可以与现实世界中的虚拟对象进行互…

vue插件——vue-print-nb 实现打印功能

之前写过好多关于打印的功能&#xff0c;通过windowprint组合键来实现打印。 今天遇到一个需求&#xff0c;就是使用vue插件来实现打印功能。 最终效果图如下&#xff1a; 下面介绍解决步骤&#xff1a; 解决步骤1&#xff1a;安装vue-print-nb插件——npm install vue-p…

flutter开发实战-手势Gesture与ListView滚动竞技场的可滑动关闭组件

flutter开发实战-手势Gesture与ListView滚动竞技场的可滑动关闭组件 最近看到了一个插件&#xff0c;实现一个可滑动关闭组件。滑动关闭组件即手指向下滑动&#xff0c;组件随手指移动&#xff0c;当移动一定位置时候&#xff0c;手指抬起后组件滑出屏幕。 一、GestureDetect…

配置多个后端 API 代理

在开发 React 应用时&#xff0c;通常会涉及到与后端 API 的交互。而在开发过程中&#xff0c;我们经常需要在开发环境中使用代理来解决跨域请求的问题。Create React App 提供了一种简单的方式来配置代理&#xff0c;即通过创建一个名为 setupProxy.js 的文件来配置代理规则。…

力扣链表篇

以下刷题思路来自代码随想录以及官方题解 文章目录 203.移除链表元素707.设计链表206.反转链表24.两两交换链表中的节点19.删除链表的倒数第N个节点面试题 02.07. 链表相交142.环形链表II 203.移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链…
最新文章