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

news2025/2/27 22:56:48

前言

注意:

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

通过设问方式来体现代码中的思想;方法: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.coloradmin.cn/o/1473354.html

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

相关文章

Qt程序设计-钟表自定义控件实例

本文讲解Qt钟表自定义控件实例。 效果如下: 创建钟表类 #ifndef TIMEPIECE_H #define TIMEPIECE_H#include <QWidget> #include <QPropertyAnimation> #include <QDebug> #include <QPainter> #include <QtMath>#include <QTimer>#incl…

Collectors.toMap的value为空报NullPointerException

1、现象 import lombok.Data; import org.apache.commons.lang3.StringUtils;import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collect…

Ps:索引颜色模式

Ps菜单&#xff1a;图像/模式/索引颜色 Image/Mode/Indexed Color 索引颜色 Indexed Color模式可生成最多 256 种颜色的 8 位图像文件。 这种颜色的限制使得索引颜色模式的图像文件相比于全彩图像&#xff08;如 RGB 颜色模式下的图像&#xff09;具有更小的文件大小&#xff0…

学习磁盘管理

文章目录 一、磁盘接口类型二、磁盘设备的命名三、fdisk分区四、自动挂载五、扩容swap六、GPT分区七、逻辑卷管理八、磁盘配额九、RAID十、软硬链接 一、磁盘接口类型 IDE、SATA、SCSI、SAS、FC&#xff08;光纤通道&#xff09; IDE, 该接口是并口。SATA, 该接口是串口。SCS…

Neoverse S3 系统 IP:机密计算和多芯片基础设施 SoC 的基础

第三代Neoverse系统IP Neoverse S3 产品推出了我们的第三代基础设施特定系统 IP&#xff0c;这是下一代基础设施 SOC 的理想基础&#xff0c;适用于从 HPC 和机器学习到 Edge 和 DPU 的各种应用。S3 机箱专注于为我们的合作伙伴提供 Chiplet、机密计算等关键创新以及 UCIe、DD…

使用R语言进行多元线性回归分析-多重共线的诊断

一、数据集 序号X1x2x3x4Y序号X1x2x3X4Y12666078.57831224472.51229155274.31954182293.12356850104.3111047426115.92143184787.6111140233483.8155263395.971266912113.311655922109.2111368812109.410771176102.73       1、从中选取主要变量&#xff0c;建立与因变…

NVM存储设备MTBF介绍

1. 概念 1.1. MTBF MTBF(Mean Time Between Failure)&#xff0c;平均故障间隔时间&#xff0c;也被称为平均无故障时间&#xff0c;是衡量一个产品的可靠性指标&#xff0c;其单位为小时。其定义为&#xff1a;产品在总的使用阶段累计工作时间与故障次数的比值&#xff1a; …

【软件测试】--功能测试2--常用设计测试用例方法

一、解决穷举场景 重点&#xff1a;使用等价类划分法 1.1 等价类划分法 重点&#xff1a;有效等价和单个无效等价各取1个即可。 步骤&#xff1a;1、明确需求2、确定有效和无效等价3、根据有效和无效造数据编写用例 1.2 案例&#xff08;qq合法验证&#xff09; 需求&#xff…

展厅设计在零售领域发挥哪些关键作用

1、 陈列和布局 零售展示设计的成功始于合适的陈列和布局。展厅设计公司考虑产品的类型、大小和特点&#xff0c;以创建有吸引力的展示。产品陈列应使顾客能够轻松浏览和访问。 2、色彩和照明 色彩和照明是零售展示设计的关键元素。展示区域的色彩和照明方案应与品牌形象一致&a…

GEE入门篇|遥感专业术语(实践操作4):光谱分辨率(Spectral Resolution)

目录 光谱分辨率&#xff08;Spectral Resolution&#xff09; 1.MODIS 2.EO-1 光谱分辨率&#xff08;Spectral Resolution&#xff09; 光谱分辨率是指传感器进行测量的光谱带的数量和宽度。 您可以将光谱带的宽度视为每个波段的波长间隔&#xff0c;在多个波段测量辐射亮…

雾锁王国Enshrouded多人联机专用服务器配置要求

雾锁王国/Enshrouded服务器CPU内存配置如何选择&#xff1f;阿里云服务器网aliyunfuwuqi.com建议选择8核32G配置&#xff0c;支持4人玩家畅玩&#xff0c;自带10M公网带宽&#xff0c;1个月90元&#xff0c;3个月271元&#xff0c;幻兽帕鲁服务器申请页面 https://t.aliyun.com…

GCC如何产生core dump

先决条件 1.安装apport&#xff08;automatically generate crash reports for debugging&#xff09; 2.修改/etc/security/limits.conf文件&#xff0c;使允许core dump&#xff0c;或者用ulimit -c unlimited设置core dump文件的大小为unlimited&#xff08;临时方案&#x…

【分享】WinRAR解压缩软件的3个密码功能

WinRAR是一款功能强大的解压缩软件&#xff0c;除了用来解压缩文件&#xff0c;还可以作为加密软件&#xff0c;给压缩包设置密码&#xff0c;达到保护文件的目的。今天来分享一下WinRAR的3个密码功能&#xff0c;一起来看看吧&#xff01; 功能一&#xff1a;设置“打开密码”…

TP6书写+uni前端,最新版本圈子系统,搭载各种插件,APP小程序H5公众都可以打包拥有,源码交付,支持二开!

部分插件展示 群聊插件 单独频道功能说明&#xff1a; 1、可申请建群。后台审核&#xff0c; 2、群分为自由加入和审核加入&#xff0c;由群主审核。 3、群聊天内容保存到数据库。 4、可查看附近的群&#xff0c;需要用户开启定位。 5、群主可踢人和禁言。 6、支持每个人提示音…

使用Scrapy将数据提取到数据库中,进行处理

我们将数据处理的方式&#xff0c;最开始csv文件 再到与数据库建立联系 代码&#xff1a; Spider: import scrapyclass ShuangseqiuSpider(scrapy.Spider):name "shuangseqiu"allowed_domains ["sina.com.cn"]start_urls ["https://view.lottery…

17.材质和外观

1.图形学中的材质 在图形学中&#xff0c;材质&#xff08;Material&#xff09;是用来描述物体外观和表面特性的属性集合。它包含了控制光的反射、折射、吸收以及其他光学效果的信息&#xff0c;从而决定了物体在渲染过程中的外观。 渲染方程中那一项和材质有关&#xff1f; …

【白话前端】数字孪生(web端)常用技术栈和软件列举

通常友友们看到我发的好看的可是化图片&#xff0c;经常问我是怎么实现的&#xff0c;用的什么软件&#xff0c;其实还真不是一项技术和一个软件就能达成的&#xff0c;这次我分享下web端的技术栈和软件&#xff0c;下次分享桌面端的。 3D建模软件&#xff1a; 数字孪生需要建…

win系统环境变量设置后不马上启用解决方法

目录 1. 刷新系统变量缓存2. 查看环境变量,检查是否真的保存成功了3. 重启系统(最后的玄学)总结 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 1. 刷新系统变量缓存 有时候系统会缓存环境变量&#xff0c;导致…

【计算机网络】1.4 接入网和物理媒体

1.4 接入网和物理媒体 问题&#xff1a;怎样将端系统和边缘路由器连接&#xff1f; 答&#xff1a;有线方式&#xff08;住宅接入网络、单位接入网络等&#xff09;或无线方式&#xff08;无线接入网络&#xff09;。 有线接入方式 光纤同轴混合网是基于已有的有线电视网开发的…

MWC 2024丨美格智能发布全新5G-A模组及FWA解决方案,将5.5G带入现实

2月26日&#xff0c;在MWC 2024世界移动通信大会上&#xff0c;美格智能正式宣布推出5G-A模组SRM817WE以及全新的5G-A FWA解决方案&#xff0c;包含5G-A CPE解决方案SRT858M、5G-A MiFi解决方案SRT878H和5G-A ODU解决方案SRT853MX&#xff0c;旨在进一步提升网络性能&#xff0…