带码农《手写Mybatis》进度3:实现映射器的注册和使用

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
作者:小傅哥
博客:https://bugstack.cn

沉淀、分享、成长,让自己和他人都能有所收获!😄

一、前言

如何面对复杂系统的设计?

我们可以把 Spring、Mybatis、Dubbo 这样的大型框架或者一些公司内部的较核心项目,都可以称为复杂的系统。这样的工程也不在是初学编程手里的玩具项目,没有所谓的CRUD,更多时候要面对的都是对系统分层的结构设计和聚合逻辑功能的实现,再通过层层转换进行实现和调用。

这对于很多刚上道的小码农来说,会感觉非常难受,不知道要从哪下手,但又想着可以一口吃个胖子。其实这是不现实的,因为这些复杂系统中的框架中有太多的内容你还没用了解和熟悉,越是硬搞越难受,信心越受打击。

其实对于解决这类复杂的项目问题,核心在于要将主干问题点缩小,具体的手段包括:分治、抽象和知识。运用设计模式和设计原则等相关知识,把问题空间合理切割为若干子问题,问题越小也就越容易理解和处理。就像你可以把很多内容做成单个独立的案例一样,最终在进行聚合使用。

二、目标

在上一章节我们初步的了解了怎么给一个接口类生成对应的映射器代理,并在代理中完成一些用户对接口方法的调用处理。虽然我们已经看到了一个核心逻辑的处理方式,但在使用上还是有些刀耕火种的,包括:需要编码告知 MapperProxyFactory 要对哪个接口进行代理,以及自己编写一个假的 SqlSession 处理实际调用接口时的返回结果。

那么结合这两块问题点,我们本章节要对映射器的注册提供注册机处理,满足用户可以在使用的时候提供一个包的路径即可完成扫描和注册。与此同时需要对 SqlSession 进行规范化处理,让它可以把我们的映射器代理和方法调用进行包装,建立一个生命周期模型结构,便于后续的内容的添加。

三、设计

鉴于我们希望把整个工程包下关于数据库操作的 DAO 接口与 Mapper 映射器关联起来,那么就需要包装一个可以扫描包路径的完成映射的注册器类。

当然我们还要把上一章节中简化的 SqlSession 进行完善,由 SqlSession 定义数据库处理接口和获取 Mapper 对象的操作,并把它交给映射器代理类进行使用。这一部分是对上一章节内容的完善

有了 SqlSession 以后,你可以把它理解成一种功能服务,有了功能服务以后还需要给这个功能服务提供一个工厂,来对外统一提供这类服务。比如我们在 Mybatis 中非常常见的操作,开启一个 SqlSession。整个设计可以如图 3-1

图 3-1 映射器的注册和使用

  • 以包装接口提供映射器代理类为目标,补全映射器注册机 MapperRegistry,自动扫描包下接口并把每个接口类映射的代理类全部存入映射器代理的 HashMap 缓存中。
  • 而 SqlSession、SqlSessionFactory 是在此注册映射器代理的上次层使用标准定义和对外服务提供的封装,便于用户使用。我们把使用方当成用户 经过这样的封装就就可以更加方便我们后续在框架上功能的继续扩展了,也希望大家可以在学习的过程中对这样的设计结构有一些思考,它可以帮助你解决一些业务功能开发过程中的领域服务包装。

四、实现

1. 工程结构

mybatis-step-02
└── src├── main│   └── java│       └── cn.bugstack.mybatis│           ├── binding│           │   ├── MapperProxy.java│           │   ├── MapperProxyFactory.java│           │   └── MapperRegistry.java│           └── session│               ├── defaults│               │   ├── DefaultSqlSession.java│               │   └── DefaultSqlSessionFactory.java│               ├── SqlSession.java│               └── SqlSessionFactory.java└── test└── java└── cn.bugstack.mybatis.test.dao├── dao│   ├── ISchoolDao.java│   └── IUserDao.java└── ApiTest.java

映射器标准定义实现关系,如图 3-2

图 3-2 映射器标准定义实现关系

  • MapperRegistry 提供包路径的扫描和映射器代理类注册机服务,完成接口对象的代理类注册处理。
  • SqlSession、DefaultSqlSession 用于定义执行 SQL 标准、获取映射器以及将来管理事务等方面的操作。基本我们平常使用 Mybatis 的 API 接口也都是从这个接口类定义的方法进行使用的。
  • SqlSessionFactory 是一个简单工厂模式,用于提供 SqlSession 服务,屏蔽创建细节,延迟创建过程。

2. 映射器注册机

源码详见cn.bugstack.mybatis.binding.MapperRegistry

public class MapperRegistry {/*** 将已添加的映射器代理加入到 HashMap*/private final Map, MapperProxyFactory> knownMappers = new HashMap<>();public  T getMapper(Class type, SqlSession sqlSession) {final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);if (mapperProxyFactory == null) {throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);}}public  void addMapper(Class type) {/* Mapper 必须是接口才会注册 */if (type.isInterface()) {if (hasMapper(type)) {// 如果重复添加了,报错throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");}// 注册映射器代理工厂knownMappers.put(type, new MapperProxyFactory<>(type));}}public void addMappers(String packageName) {Set> mapperSet = ClassScanner.scanPackage(packageName);for (Class mapperClass : mapperSet) {addMapper(mapperClass);}}}
  • MapperRegistry 映射器注册类的核心主要在于提供了 ClassScanner.scanPackage 扫描包路径,调用 addMapper 方法,给接口类创建 MapperProxyFactory 映射器代理类,并写入到 knownMappers 的 HashMap 缓存中。
  • 另外就是这个类也提供了对应的 getMapper 获取映射器代理类的方法,其实这步就包装了我们上一章节手动操作实例化的过程,更加方便在 DefaultSqlSession 中获取 Mapper 时进行使用。

3. SqlSession 标准定义和实现

源码详见cn.bugstack.mybatis.session.SqlSession

public interface SqlSession {/*** Retrieve a single row mapped from the statement key* 根据指定的SqlID获取一条记录的封装对象** @param  the returned object type 封装之后的对象类型* @param statement sqlID* @return Mapped object 封装之后的对象*/T selectOne(String statement);/*** Retrieve a single row mapped from the statement key and parameter.* 根据指定的SqlID获取一条记录的封装对象,只不过这个方法容许我们可以给sql传递一些参数* 一般在实际使用中,这个参数传递的是pojo,或者Map或者ImmutableMap** @param  the returned object type* @param statement Unique identifier matching the statement to use.* @param parameter A parameter object to pass to the statement.* @return Mapped object*/T selectOne(String statement, Object parameter);/*** Retrieves a mapper.* 得到映射器,这个巧妙的使用了泛型,使得类型安全** @param  the mapper type* @param type Mapper interface class* @return a mapper bound to this SqlSession*/T getMapper(Class type);}
  • 在 SqlSession 中定义用来执行 SQL、获取映射器对象以及后续管理事务操作的标准接口。
  • 目前这个接口中对于数据库的操作仅仅只提供了 selectOne,后续还会有相应其他方法的定义。

源码详见cn.bugstack.mybatis.session.defaults

public class DefaultSqlSession implements SqlSession {/*** 映射器注册机*/private MapperRegistry mapperRegistry;@Overridepublic  T selectOne(String statement, Object parameter) {return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter);}@Overridepublic  T getMapper(Class type) {return mapperRegistry.getMapper(type, this);}}
  • 通过 DefaultSqlSession 实现类对 SqlSession 接口进行实现。
  • getMapper 方法中获取映射器对象是通过 MapperRegistry 类进行获取的,后续这部分会被配置类进行替换。
  • 在 selectOne 中是一段简单的内容返回,目前还没有与数据库进行关联,这部分在我们渐进式的开发过程中逐步实现。

4. SqlSessionFactory 工厂定义和实现

源码详见cn.bugstack.mybatis.session.SqlSessionFactory

public interface SqlSessionFactory {/*** 打开一个 session* @return SqlSession*/SqlSession openSession();}
  • 这其实就是一个简单工厂的定义,在工厂中提供接口实现类的能力,也就是 SqlSessionFactory 工厂中提供的开启 SqlSession 的能力。

源码详见cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {private final MapperRegistry mapperRegistry;public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {this.mapperRegistry = mapperRegistry;}@Overridepublic SqlSession openSession() {return new DefaultSqlSession(mapperRegistry);}}
  • 默认的简单工厂实现,处理开启 SqlSession 时,对 DefaultSqlSession 的创建以及传递 mapperRegistry,这样就可以在使用 SqlSession 时获取每个代理类的映射器对象了。

五、测试

1. 事先准备

在同一个包路径下,提供2个以上的 Dao 接口:

public interface ISchoolDao {String querySchoolName(String uId);}public interface IUserDao {String queryUserName(String uId);Integer queryUserAge(String uId);}

2. 单元测试

@Test
public void test\_MapperProxyFactory() {// 1. 注册 MapperMapperRegistry registry = new MapperRegistry();registry.addMappers("cn.bugstack.mybatis.test.dao");// 2. 从 SqlSession 工厂获取 SessionSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(registry);SqlSession sqlSession = sqlSessionFactory.openSession();// 3. 获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 4. 测试验证String res = userDao.queryUserName("10001");logger.info("测试结果:{}", res);
}
  • 在单元测试中通过注册机扫描包路径注册映射器代理对象,并把注册机传递给 SqlSessionFactory 工厂,这样完成一个链接过程。
  • 之后通过 SqlSession 获取对应 DAO 类型的实现类,并进行方法验证。

测试结果

22:43:23.254 [main] INFO  cn.bugstack.mybatis.test.ApiTest - 测试结果:你被代理了!方法:queryUserName 入参:[Ljava.lang.Object;@50cbc42fProcess finished with exit code 0
  • 通过测试大家可以看到,目前我们已经在一个有 Mybatis 影子的手写 ORM 框架中,完成了代理类的注册和使用过程。

六、总结

  • 首先要从设计结构上了解工厂模式对具体功能结构的封装,屏蔽过程细节,限定上下文关系,把对外的使用减少耦合。
  • 从这个过程上读者伙伴也能发现,使用 SqlSessionFactory 的工厂实现类包装了 SqlSession 的标准定义实现类,并由 SqlSession 完成对映射器对象的注册和使用。
  • 本章学习要注意几个重要的知识点,包括:映射器、代理类、注册机、接口标准、工厂模式、上下文。这些工程开发的技巧都是在手写 Mybatis 的过程中非常重要的部分,了解和熟悉才能更好的在自己的业务中进行使用。

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

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

相关文章

ubuntu16.04安装metasploit+postgresql

ubuntu16.04安装metasploitpostgresql 1、安装msf 1.1、打开终端&#xff0c;进入安装目录&#xff08;你想放在哪就进哪 cd /opt curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb &g…

01背包问题,动态规划求解

01背包问题&#xff1a; 1.递归思想 0- 1 背包问题如果采用递归算法来描述则非常清楚明白, 它的算法根本思想是假设用布尔函数knap( s, n) 表示n 件物品放入可容质量为s 的背包中是否有解( 当knap 函数的值为真时 说明问题有解,其值为假时无解) . 我们可以通过输入s 和n 的值, …

《C++程序设计原理与实践》读书笔记(二)

C语言提供了一些内置的类型&#xff0c;如char、int和double。对于一个类型&#xff0c;如果编译器无须借助程序员在源码中提供的任何声明&#xff0c;就知道如何表示这种类型的对象以及可以对它进行什么样的运算&#xff0c;我们就称这种类型是内置的。非内置的类型称为用户自…

SonarQube代码质量管理平台安装与使用

SonarQube代码质量管理平台安装与使用 注原文地址&#xff1a;http://blog.csdn.net/hunterno4/article/details/11687269Sonar简介 Sonar是一个用于代码质量管理的开源平台&#xff0c;用于管理源代码的质量&#xff0c;可以从七个维度检测代码质量 通过插件形式&#xff0c;可…

docker容器编排原来这么丝滑~

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 前言&#xff1a; 请各大网友尊重本人原创知识分享&#xff0c;谨记本人博客&#xff1a;南国以南i 概念介绍&#xff1a…

Ant in Action读书笔记(三):在Ant中导入环境变量

<property environment"env"/><echo message"${env.ANT_HOME}"/>能把所有的环境变量都导入成Ant的property转载于:https://www.cnblogs.com/buhaiqing/archive/2012/11/03/2752142.html

互联网思维的“独孤九剑”

课前秀&#xff1a;三个段子 第一个段子&#xff1a;有一个毫无餐饮行业经验的人&#xff0c;他开了一家餐馆&#xff0c;菜品只有12道&#xff0c;在北京只有两家分店&#xff1b;仅两个月时间&#xff0c;就实现了所在商场餐厅坪效第一名&#xff1b;绿茶单位坪效大约是100元…

linux系统分析工具续-SystemTap和火焰图(Flame Graph)

本文为网上各位大神文章的综合简单实践篇&#xff0c;参考文章较多&#xff0c;有些总结性东西&#xff0c;自认暂无法详细写出&#xff0c;建议读文中列出的参考文档&#xff0c;相信会受益颇多。下面开始吧&#xff08;本文出自 “cclo的博客” 博客&#xff0c;请务必保留此…

MySQL8.0.x 版本安装步骤傻瓜式教程【官方版】

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 MySQL8.0.x 安装 一、下载 MySQL官网下载链接&#xff1a;https://downloads.mysql.com/archives/community/ 选择版本后…

jar包直接拷贝到WEB-INF/lib下和以userLibrary引入的区别

jar包直接拷贝到WEB-INF/lib下和以userLibrary形式引入的区别&#xff1f; jar包放置在WEB-INF/lib下和通过build path导入的区别是什么&#xff1f; 上一篇文章&#xff1a;【原创】java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener…

Just do it

不管做什么 坚持下去 最后慢慢有自己的优势点一点一滴 用心去做用心去工作 思考并实现自己的想法主动不是说在嘴上 去做 do it做东西会遇到挫折 想办法克服或绕过 寻求帮助 积极沟通do it&#xff0c; enjoy work转载于:https://blog.51cto.com/bluemood/1588962

sqoop1.4.6导出oracle实例

1、导入odbj6.jar到$SQOOP_HOME/lib目录下。 2、sqoop import --append --connect jdbc:oracle:thin:219.216.110.120:1521:orcl --username TEST1 --password test1 --table TEST1 --columns age --hbase-table test1 --hbase-row-key id --column-family personinfo 说明: S…

不用电的计算机(二)

Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/course/detail/35475 转载请注明出处&#xff1a;https://blog.csdn.net/morningli/p/16058594.html 上一篇讲到最早的计算机是什么样的&#xf…

JS 数组 各项操作

构建数组(Array) var a new Array(); //创建Array对象 var a new Array; //创建Array对象 var a new Array(10); //创建Array对象&#xff0c;并指定数组中项的个数 var a new Array("a","b","c"); //数组a,b,c var a ["a","…

CocoaPods did not set the base configuration of your project 问题解决方案

今天在使用pod install的时候&#xff0c;出现了 [!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configuration…

OpenSSH 密钥管理:RSA/DSA 认证(转载)

我们中有许多人把优秀的 OpenSSH用作古老的 telnet 和 rsh 命令的替代品&#xff0c;OpenSSH 不仅是安全的而且是加密的。 OpenSSH 更加吸引人的特性之一是它能够使用基于一对互补的数字式密钥的 RSA 和 DSA 认证协议来认证用户。RSA 和 DSA 认证承诺 不必提供密码就能够同远程…

在UnityUI中绘制线状统计图

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 先来个效果图 觉得不好看可以自己调整 1.绘制数据点 线状图一般由数据点和连线组…

HTTP 错误 404.3 - Not Found 由于扩展配置问题而无法提供您请求的页面

错误原因为 IIS 扩展信息中午此扩展 标签&#xff1a; 今天&#xff0c;在vs2013中新建了一个placard.json文件&#xff0c;当我用jq读取它的时候&#xff0c;去提示404&#xff0c;直接在浏览器访问这个文件&#xff0c;提示&#xff1a; HTTP 错误 404.3 – Not Found 由于扩…

国企开会特点

1、领导讲话只有目标性没有整体逻辑性&#xff1a;想哪说哪 2、有些讨论会什么基本上都是领导一个人在开会&#xff0c;其他人都是听着&#xff1b;领导讲到差不多&#xff0c;如果想起来会问大家还有什么问题吗&#xff1f;如果没想起来就这么地 一个领导讲来讲去也就那么点干…

一行代码,让 VS Code 内置 PDF 阅读器变成深色模式

Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https://blog.csdn.net/m0_56069948/article/details/122285941 许多人会用 VSCode 写 LaTeX&#xff0c;等等&#xff0c;都会用到 PDF 预览。VSCo…