手写mybatis之数据源的解析、创建和使用

前言


在上一章节我们解析 XML 中的 SQL 配置信息,并在代理对象调用 DefaultSqlSession 中进行获取和打印操作,从整个框架结构来看我们解决了对象的代理、Mapper的映射、SQL的初步解析,那么接下来就应该是连库和执行SQL语句并返回结果了。
那么这部分内容就会涉及到解析 XML 中关于 dataSource 数据源信息配置,并简历事务管理和连接池的启动和使用。并将这部分能力在 DefaultSqlSession 执行 SQL 语句时进行调用。
核心类图如下
在这里插入图片描述

事务管理


一次数据库的操作应该具有事务管理能力,而不是通过 JDBC 获取链接后直接执行即可。还应该把控链接、提交、回滚和关闭的操作处理。所以这里我们结合 JDBC 的能力封装事务管理。
事务接口定义 Transaction
定义标准的事务接口,链接、提交、回滚、关闭,具体可以由不同的事务方式进行实现,包括:JDBC和托管事务,托管事务是交给 Spring 这样的容器来管理

Connection getConnection() throws SQLException;void commit() throws SQLException;void rollback() throws SQLException;void close() throws SQLException;
public class JdbcTransaction implements Transaction {protected Connection connection;protected DataSource dataSource;protected TransactionIsolationLevel level = TransactionIsolationLevel.NONE;protected boolean autoCommit;public JdbcTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {this.dataSource = dataSource;this.level = level;this.autoCommit = autoCommit;}@Overridepublic Connection getConnection() throws SQLException {connection = dataSource.getConnection();connection.setTransactionIsolation(level.getLevel());connection.setAutoCommit(autoCommit);return connection;}@Overridepublic void commit() throws SQLException {if (connection != null && !connection.getAutoCommit()) {connection.commit();}}
}

事务工厂

public interface TransactionFactory {/*** 根据 Connection 创建 Transaction* @param conn Existing database connection* @return Transaction*/Transaction newTransaction(Connection conn);/*** 根据数据源和事务隔离级别创建 Transaction* @param dataSource DataSource to take the connection from* @param level Desired isolation level* @param autoCommit Desired autocommit* @return Transaction*/Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);}

类型别名注册器
在 Mybatis 框架中我们所需要的基本类型、数组类型以及自己定义的事务实现和事务工厂都需要注册到类型别名的注册器中进行管理,在我们需要使用的时候可以从注册器中获取到具体的对象类型,之后在进行实例化的方式进行使用。

public class TypeAliasRegistry {private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<>();public TypeAliasRegistry() {// 构造函数里注册系统内置的类型别名registerAlias("string", String.class);// 基本包装类型registerAlias("byte", Byte.class);registerAlias("long", Long.class);registerAlias("short", Short.class);registerAlias("int", Integer.class);registerAlias("integer", Integer.class);registerAlias("double", Double.class);registerAlias("float", Float.class);registerAlias("boolean", Boolean.class);}public void registerAlias(String alias, Class<?> value) {String key = alias.toLowerCase(Locale.ENGLISH);TYPE_ALIASES.put(key, value);}public <T> Class<T> resolveAlias(String string) {String key = string.toLowerCase(Locale.ENGLISH);return (Class<T>) TYPE_ALIASES.get(key);}}

注册事务

public class Configuration {//环境protected Environment environment;// 映射注册机protected MapperRegistry mapperRegistry = new MapperRegistry(this);// 映射的语句,存在Map里protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();// 类型别名注册机protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();public Configuration() {typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);}
}

在 Configuration 配置选项类中,添加类型别名注册机,通过构造函数添加 JDBC、DRUID 注册操作。
整个 Mybatis 的操作都是使用 Configuration 配置项进行串联流程,所以所有内容都会在 Configuration 中进行链接。
解析数据源配置
通过在 XML 解析器 XMLConfigBuilder 中,扩展对环境信息的解析,我们这里把数据源、事务类内容称为操作 SQL 的环境。解析后把配置信息写入到 Configuration 配置项中,便于后续使用。

public class XMLConfigBuilder extends BaseBuilder {public Configuration parse() {try {// 环境environmentsElement(root.element("environments"));// 解析映射器mapperElement(root.element("mappers"));} catch (Exception e) {throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}return configuration;}private void environmentsElement(Element context) throws Exception {String environment = context.attributeValue("default");List<Element> environmentList = context.elements("environment");for (Element e : environmentList) {String id = e.attributeValue("id");if (environment.equals(id)) {// 事务管理器TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();// 数据源Element dataSourceElement = e.element("dataSource");DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(dataSourceElement.attributeValue("type")).newInstance();List<Element> propertyList = dataSourceElement.elements("property");Properties props = new Properties();for (Element property : propertyList) {props.setProperty(property.attributeValue("name"), property.attributeValue("value"));}dataSourceFactory.setProperties(props);DataSource dataSource = dataSourceFactory.getDataSource();// 构建环境Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);configuration.setEnvironment(environmentBuilder.build());}}}}

SQL执行和结果封装
在上一章节中在 DefaultSqlSession#selectOne 只是打印了 XML 中配置的 SQL 语句,现在把数据源的配置加载进来以后,就可以把 SQL 语句放到数据源中进行执行以及结果封装。

public class DefaultSqlSession implements SqlSession {private Configuration configuration;public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;}@Overridepublic <T> T selectOne(String statement, Object parameter) {try {MappedStatement mappedStatement = configuration.getMappedStatement(statement);Environment environment = configuration.getEnvironment();Connection connection = environment.getDataSource().getConnection();BoundSql boundSql = mappedStatement.getBoundSql();PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));ResultSet resultSet = preparedStatement.executeQuery();List<T> objList = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));return objList.get(0);} catch (Exception e) {e.printStackTrace();return null;}}
}

在 selectOne 方法中获取 Connection 数据源链接,并简单的执行 SQL 语句,并对执行的结果进行封装处理。
因为目前这部分主要是为了大家串联出整个功能结构,所以关于 SQL 的执行、参数传递和结果封装都是写死的,后续我们进行扩展。
测试
数据库表创建

CREATE TABLEUSER(id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID',userId VARCHAR(9) COMMENT '用户ID',userHead VARCHAR(16) COMMENT '头像',createTime TIMESTAMP NULL COMMENT '创建时间',updateTime TIMESTAMP NULL COMMENT '更新时间',userName VARCHAR(64),PRIMARY KEY (id))ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into user (id, userId, userHead, createTime, updateTime, userName) values (1, '10001', '1_04', '2022-04-13 00:00:00', '2022-04-13 00:00:00', '码农明哥'); 
配置数据源```xml
<environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="DRUID"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment>
</environments>

通过 mybatis-config-datasource.xml 配置数据源信息,包括:driver、url、username、password。
配置Mapper

<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.lm.mybatis.test.po.User">SELECT id, userId, userName, userHeadFROM userwhere id = #{id}
</select>

流程验证

@Test
public void test01() throws IOException {// 1. 从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();// 2. 获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 3. 测试验证User user = userDao.queryUserInfoById(1L);logger.info("测试结果:{}", JSON.toJSONString(user));
}

功能验证

@Test
public void test_selectOne() throws IOException {// 解析 XMLReader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);Configuration configuration = xmlConfigBuilder.parse();// 获取 DefaultSqlSessionSqlSession sqlSession = new DefaultSqlSession(configuration);// 执行查询:默认是一个集合参数Object[] req = {1L};Object res = sqlSession.selectOne("com.lm.mybatis.test.dao.IUserDao.queryUserInfoById", req);logger.info("测试结果:{}", JSON.toJSONString(res));
}

除了整体的功能流程测试外,我们还可以只对本章节新增的内容进行单元测试。由于本章节的主要操作都是在解析内容的添加,处理 XML 配置中的数据源信息,以及解析后可以在 DefaultSqlSession 中调用数据源执行 SQL 语句并返回结果。
所以我们这里单独把这部分提取出来进行测试验证,通过基于这样的测试,可以更好的在 Sequence Diagram 中生成对应的 UML 方便读者更加容易理解本章节新增的内容和流
在这里插入图片描述
总结
1:以解析 XML 配置解析为入口,添加数据源的整合和包装,引出事务工厂对 JDBC 事务的处理,并加载到环境配置中进行使用。
2:那么通过数据源的引入就可以在 DefaultSqlSession 中从 Configuration 配置引入环境信息,把对应的 SQL 语句提交给 JDBC 进行处理并简单封装结果数据。
最后:结合本章节建立起来的框架结构,数据源、事务、简单的SQL调用,下个章节将继续这部分内容的扩展处理,让整个功能模块逐渐完善。

好了到这里就结束了手写mybatis之数据源的解析、创建和使用的学习,大家一定要跟着动手操作起来。需要的源码的 可si我获取;

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

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

相关文章

需求10——通过改一个小bug来学习如何定位问题

在浏览我之前完成的一些小需求时&#xff0c;我发现了一个非常有价值的需求。这个需求可以让我深入了解系统中关于故障上报的功能。通过完善这个需求&#xff0c;我能够全面掌握整个故障上报的流程。 这个需求主要是关于故障上报流程中出现的问题。当前的流程如下&#xff1a;…

地面沉降数值模拟方法

目前&#xff0c;地面沉降问题是我国较为常见的环境地质问题&#xff0c;其巨大的破坏力严重影响城市建筑安全和交通轨道运行。围绕地面沉降的防控与治理&#xff0c;是工程地质、环境地质、轨道交通设计等相关技术人员十分关注的领域&#xff0c;而数值模拟技术是评估防控效果…

云栖实录 | Hologres3.0全新升级:一体化实时湖仓平台

本文根据2024云栖大会实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a; 姜伟华 | 阿里云智能集团资深技术专家、Hologres 负责人 丁 烨 | 阿里云智能集团产品专家、Hologres 产品负责人 活动&#xff1a; 2024 云栖大会 - 商用大数据计算与分析平台论…

RS485为什么用隔离?

RS-485是工业与仪器仪表中的物理层总线设计 标准&#xff0c;目前已成为业界应用最为广泛的标准通信接 口之一。这种通信接口允许在简单的一对双绞线上 进行多点双向通信&#xff0c;它所具有的噪声抑制能力、数 据传输速率、电缆长度及可靠性是其他标准无法比 拟的。 当需要在…

探索未来:picows,Python的AI新宠

文章目录 **探索未来&#xff1a;picows&#xff0c;Python的AI新宠**背景&#xff1a;为何选择picows&#xff1f;什么是picows&#xff1f;如何安装picows&#xff1f;简单的库函数使用方法场景应用常见Bug及解决方案总结 探索未来&#xff1a;picows&#xff0c;Python的AI新…

国资委推动中央企业人工智能发展

2023年以来&#xff0c;国资委多次对中央企业发展人工智能提出要求。2024年2月&#xff0c;国资委召开中央企业人工智能专题推进会&#xff0c;提出加快推动人工智能发展&#xff0c;是国资央企发挥功能使命&#xff0c;抢抓战略机遇&#xff0c;培育新质生产力&#xff0c;推进…

Axios 网络请求

文章目录 Axios 网络请求1.Axios 使用1.Axios 简介2.Axios 安装安装命令 3.Axios 引入方式全局引入局部引入 2.整合 vue1.在组件中使用 axios 发送请求发送结果这里就出现了跨域问题 3.跨域后端解决办法全局配置类 加入注解 CrossOrigin请求结果 全局配置 baseUrl Axios 网络请…

Linux内核启动过程1

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

阿里云+frp内网穿透工作站远程开机

frp 是一个可用于内网穿透的高性能的反向代理应用&#xff0c;支持 tcp, udp, http, https 等协议。利用 frp 和一个带有公网 IP 的云服务器作为中间跳板&#xff0c;可以实现内网穿透&#xff0c;不在家的时候也可以访问到家里面的工作站。有了这个缺口之后&#xff0c;在外面…

基于STM32单片机的恒温焊台的设计

本设计以STM32F401CBU6为主控控制芯片&#xff0c;包含电压采集模块&#xff0c;温度控制模块&#xff0c;输入模块和OLED显示模块等。使用EC11编码器设置温度&#xff0c;选择开关机时间&#xff0c;当焊台开机后&#xff0c;就可以设置所需要的温度&#xff0c;这时的手柄开始…

BricsCAD 24:智能绘图与自动化,加速设计流程

BricsCAD是一款功能丰富、易于使用且具有良好兼容性的CAD软件。bricscad 24 mac一款集2D绘图和3D建模于一体的CAD软件&#xff0c;它由比利时Bricsys NV公司研发&#xff0c;界面与AutoCAD相近&#xff0c;易于上手。 BricsCAD 24 for mac v24.1.05 注册密钥下载 BricsCAD 21 …

MES系统中的正向追溯与反向追溯管理

随着制造业的日益发展&#xff0c;生产过程中的质量控制和管理变得尤为关键。MES系统作为一种实现车间生产管理和控制的重要工具&#xff0c;其追溯功能在生产过程中起着至关重要的作用。 一、MES系统概述 MES系统是一套面向制造企业车间执行层的生产信息化管理系统。它通过对…

1-laravel 搭建与路由基础

文章目录 laravel 环境搭建安装工程的命令 基于laravel 开发访问默认欢迎页面第一路由 laravel 环境搭建 借助 phpstudy 搭建环境 安装工程的命令 C:\phpstudy_pro\WWW>composer create-project --prefer-dist laravel/laravel la-3 安装位置 安装…

使用 Go 和 Gin 框架构建简单的用户和物品管理 Web 服务

使用 Go 和 Gin 框架构建简单的用户和物品管理 Web 服务 在本项目中&#xff0c;我们使用 Go 语言和 Gin 框架构建了一个简单的 Web 服务&#xff0c;能够管理用户和物品的信息。该服务实现了两个主要接口&#xff1a;根据用户 ID 获取用户名称&#xff0c;以及根据物品 ID 获…

大模型驱动机器狗——从UMI on Legs到Helpful DoggyBot:分别把机械臂装到机器狗背上、夹爪装到机器狗嘴里

前言 今年十一7天假期期间&#xff0c;一半的时间都在改本博客内的上一篇文章《从Fast-UMI到Diff-Control&#xff1a;分别改进UMI的硬件及其所用的Diffusion policy(含ControlNet详解)》&#xff0c;改完之后&#xff0c;接下来计划要写的博客包括且不限于 第1-2篇&#xff…

CDA数据分析师证书含金量到底如何?

为什么学习数据分析&#xff1f; 2024年&#xff0c;是一个被数据影响的时代。数据&#xff0c;如同无形的燃料&#xff0c;驱动着现代社会的运转。从全球互联网的用户每天产生的2.5亿TB数据&#xff0c;到制造业的传感器、金融交易、医疗病历等领域的海量信息&#xff0c;数据…

小红书爆款首图生成prompt v0.1

由于平时需要在小红书&#xff0c;抖音&#xff0c;公众号等自媒体平台发布一些内容&#xff0c;其中一个需求就是需要一个亮眼的首图&#xff0c;特别是小红书&#xff0c;首图效果好坏会直接决定推流的效果。 受到李继刚老师一系列 Prompt 的启发&#xff0c;创作了下面这个小…

牛客:xay loves count与1LL的用法

xay loves count 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行代码 #include <bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);int n;cin >> n;int a[1000005] {0};int cnt[1000005] …

Linux内核启动过程2

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

en造数据结构与算法C# 之 动态规划

动态规划 动态规划和分治法很像&#xff0c;都是拆解问题解决 分治法常用递归算法来写&#xff0c;但是动态规划和分治法的最大不同就是存入值 &#xff0c;AI真方便 钢条切割问题 其实该问题最平常&#xff0c;也是最直接的思想就是先把前项最赚米的方案总结出来&…