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

2024-02-26 16:36

本文主要是介绍【简写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;}@Overridepublic <T> T  selectOne(String statement,Object parameter) {return (T) ("你被代理了!" + "方法:" + statement + " 入参:" + parameter);}@Overridepublic <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;}@Overridepublic 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问答

这篇关于【简写Mybatis】02-注册机的实现以及SqlSession处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/749484

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import