Node.js自研ORM框架深度解析与实践

Node.js自研ORM框架深度解析与实践

前言

在现代Web开发中,对象关系映射(ORM)框架扮演着至关重要的角色。它们为开发者提供了一层抽象,使得数据库操作变得更加简单和直观。本文将深入解析一个基于Node.js和MySQL的自研ORM框架,该框架虽然轻量级,但功能完善,包含了连接池管理、查询构建器、事务处理、字段验证等核心功能。

项目概述

技术栈

  • 运行环境: Node.js
  • 数据库: MySQL
  • 核心依赖: mysql@2.18.1

项目结构

Node/
├── orm/                    # ORM核心模块
│   ├── db.js              # 数据库连接与事务处理
│   ├── dbSet.js           # 数据集操作(CRUD)
│   └── QueryBuilder.js    # SQL查询构建器
├── entitys/               # 实体层
│   └── dbContext.js       # 数据库上下文
├── dbtest.js              # 测试文件
└── package.json           # 项目配置

核心模块深度解析

1. 数据库连接层 (db.js)

数据库连接层是整个ORM框架的基础,负责管理MySQL连接池和事务处理。

核心特性
  • 连接池管理: 使用mysql.createPool()创建连接池,提高性能
  • 异步操作: 基于Promise封装,支持async/await语法
  • 事务支持: 完整的事务生命周期管理(开启、提交、回滚)
  • 错误处理: 完善的异常捕获和处理机制
关键代码解析
// 连接池初始化
function DbBase(options) {this.pool = mysql.createPool(options);
}// 核心查询方法
DbBase.prototype.Run = async function (sql, params, conn) {if (conn) {// 使用指定连接(事务场景)return new Promise((resolve, reject) => {conn.query(sql, params, (err, result) => {if (err) {console.log(err);reject(err);} else {resolve(result);}});});} else {// 使用连接池return new Promise((resolve, reject) => {this.pool.query(sql, params, (err, result) => {if (err) {console.log(err);reject(err);} else {resolve(result);}});});}
};
事务处理机制

事务处理是这个ORM框架的亮点之一,实现了完整的事务生命周期:

DbBase.prototype.RunTransaction = async function (fn) {let connection;try {// 1. 获取连接connection = await new Promise((resolve, reject) => {this.pool.getConnection((err, conn) => {if (err) reject(err);else resolve(conn);});});// 2. 开启事务await new Promise((resolve, reject) => {connection.beginTransaction(err => {if (err) reject(err);else resolve();});});// 3. 执行业务逻辑const result = await fn(connection);// 4. 提交事务await new Promise((resolve, reject) => {connection.commit(err => {if (err) {connection.rollback(() => {reject(err);});} else {resolve(result);}});});return result;} catch (error) {// 5. 异常回滚if (connection) {await new Promise((resolve, reject) => {connection.rollback(() => {connection.release();reject(error);});});}} finally {// 6. 释放连接if (connection) {connection.release();}}
};

2. 查询构建器 (QueryBuilder.js)

查询构建器采用链式调用模式,提供了灵活的SQL构建能力。

核心特性
  • 链式调用: 支持method chaining模式
  • 参数化查询: 防SQL注入
  • 分页支持: 内置分页功能
  • 灵活的条件构建: 支持多种WHERE条件
使用示例
// 基础查询
const query = new QueryBuilder().select(['id', 'name', 'email']).from('users').where('status', 'active').where('age', '>', 18).orderBy('created_at', 'DESC').paginate(1, 10);const {sql, parameters} = query.build();
分页功能实现
paginate(page, perPage) {if(page <= 1) page = 1;const offset = (page - 1) * perPage;return this.limit(offset, perPage);
}

3. 数据集操作层 (dbSet.js)

dbSet是ORM框架的核心,实现了完整的CRUD操作和数据验证功能。

核心功能
  • CRUD操作: Create、Read、Update、Delete
  • 字段验证: 类型、长度、必填、默认值验证
  • 自动主键: 支持自增主键
  • 视图支持: 区分表和视图的操作权限
字段定义与验证
// 字段定义
dbSet.prototype.AddField = function (fieldName, fieldType, length = 0, required = false, isNull = true, defaultValue = null) {this.fields[fieldName] = { fieldName, fieldType, length, required, isNull, defaultValue };
}// 字段验证逻辑
dbSet.prototype.FieldVerify = function (data) {Object.keys(this.fields).forEach(key => {var field = this.fields[key];var fieldValue = data[field.fieldName];// 必填验证if (field.required && !(field.fieldName in data)) throw new Error(`必须包含字段'${field.fieldName}'`);// 默认值设置if (field.defaultValue != null && fieldValue == null) data[field.fieldName] = field.defaultValue;});// 类型验证、长度验证等...
};
CRUD操作实现

添加数据

dbSet.prototype.Add = async function (data, conn) {if (this.isview) {throw new Error(`视图${this.tableName}不能添加数据`);}this.FieldVerify(data);var { keys, values, _values } = this.GetDataContent(data);var sql = `insert into \`${this.tableName}\`(${keys}) values(${_values})`;return new Promise((resolve, reject) => {this.db.Run(sql, values, conn).then(res => {resolve(res.insertId);}).catch(err => {reject(err);});});
}

查询数据

dbSet.prototype.GetList = async function (query, conn) {var { sql, parameters } = query.build();return new Promise((resolve, reject) => {this.db.Run(sql, parameters, conn).then(res => {resolve(res);}).catch(err => {reject(err);})})
}
性能优化特性

连接复用查询

dbSet.prototype.GetTable = async function(query) {// 使用同一个连接进行列表和计数查询,节约连接池开销let connection;try {connection = await new Promise((resolve, reject) => {this.db.pool.getConnection((err, conn) => {if (err) reject(err);else resolve(conn);});});var list = await this.GetList(query, connection);var count = await this.GetCount(query, connection);return {list, count};} finally {if (connection) connection.release();}
}

4. 数据库上下文 (dbContext.js)

数据库上下文是整个ORM框架的入口点,负责初始化数据库连接和实体映射。

function dbContext() {// 数据库连接配置this.db = new DbBase({host: '127.0.0.1',port: '3306',user: 'user',password: '123456',database: 'test'});// 实体映射this.test = new dbSet("test", this.db);this.test.AddField("name", "string", 3, true, false, "123456");
}

实战测试案例

基础CRUD操作测试

var dbContext = require('./entitys/dbContext');
var _db = new dbContext();// 添加数据
var res = await _db.test.Add({name: "测试用户"
});
console.log("新增ID:", res);// 查询数据
var query = _db.test.Query();
query.where("id", 5);
query.limit(100);
var result = await _db.test.GetTable(query);
console.log("查询结果:", result);

事务操作测试

// 事务示例
await _db.db.RunTransaction(async (conn) => {// 在事务中执行多个操作var res1 = await _db.test.Add({name: "事务测试1"}, conn);var res2 = await _db.test.Add({name: "事务测试2"}, conn);// 如果任何操作失败,整个事务会自动回滚
});

框架特色与优势

1. 轻量级设计

  • 核心代码不到500行
  • 无重度依赖,仅依赖mysql驱动
  • 启动快速,内存占用低

2. 类型安全

  • 完整的字段类型验证
  • 数据长度校验
  • 必填字段检查

3. 性能优化

  • 连接池管理
  • 参数化查询防止SQL注入
  • 连接复用减少开销

4. 易于扩展

  • 模块化设计
  • 清晰的分层架构
  • 支持自定义字段类型

与主流ORM框架对比

特性自研ORMSequelizeTypeORM
学习成本
性能
功能完整性基础完整完整
包大小极小
定制性

应用场景

适用场景

  • 中小型项目快速开发
  • 对性能要求较高的场景
  • 需要深度定制ORM功能
  • 学习ORM原理的教学项目

不适用场景

  • 复杂的关系映射需求
  • 需要数据库迁移功能
  • 多数据库支持需求

总结

这个自研ORM框架虽然功能相对简单,但展现了ORM框架的核心设计思想:

  1. 分层架构: 清晰的数据库连接层、查询构建层、数据操作层
  2. 事务管理: 完整的事务生命周期处理
  3. 性能优化: 连接池、参数化查询等优化策略
  4. 类型安全: 完善的数据验证机制

对于中小型项目或学习ORM原理来说,这是一个很好的参考实现。通过理解这个框架的设计思路,开发者可以更好地理解ORM的工作原理,并根据实际需求进行扩展和优化。

在实际项目中,建议根据具体需求选择合适的ORM框架。如果项目规模较小且对性能要求较高,可以考虑使用类似的轻量级自研方案;如果项目复杂度较高,则建议选择成熟的开源ORM框架如Sequelize或TypeORM。


本文详细解析了一个基于Node.js的自研ORM框架,涵盖了数据库连接、查询构建、CRUD操作、事务处理等核心功能。希望能够为正在学习或考虑自研ORM框架的开发者提供有价值的参考。

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

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

相关文章

汇总图片拖进ps中 photoshop同时打开几个文件夹

如果你有许多文件夹&#xff0c;你想选中一部分&#xff0c;然后把里面的图片全部拖进photoshop当中&#xff0c;但是文件夹又不能直接拖进去&#xff0c;那么你可以尝试使用一下这个工具&#xff0c;首先测试一下直接拖文件夹。选中你要处理的文件夹&#xff0c;直接拖进photo…

mysql 5.7 查询运行时间较长的sql

开发过程遇到sql 执行时间长&#xff0c;又取消不了的情况 可使用 kill query ID 杀死进程获取正在运行的sqlSELECT ID, -- 进程ID&#xff1a;MySQL服务器分配给每个连接的唯一标识符&#xff0c;用于区分不同的客户端连接USER, …

MongoDB 从入门到实践:全面掌握文档型 NoSQL 数据库核心操作

目录 一、MongoDB 基础准备 1. 官方资源获取 2. 安装步骤解析 二、MongoDB 核心指令详解 1. 数据库操作指令 2. 集合操作指令 3. 文档操作指令 查询文档 插入文档 修改文档 删除文档 三、进阶查询技巧 1. 运算符的灵活运用 比较运算符 逻辑运算符 范围与成员运算…

CVPR2025丨遥感领域,全模态与秒超高清遥感建模重大突破,性能提升创新点

关注gongzhonghao【CVPR顶会精选】刚入门遥感建模时&#xff0c;总好奇别人为什么总能提出新方法&#xff1f;慢慢摸索后才发现&#xff0c;创新点并不是硬憋出来的&#xff0c;而是要从数据特性、传感器差异、地物细节以及环境变化中发现机会。不同波段、不同分辨率、不同时相…

HTML5详篇

前端三剑客 前端三剑客是指HTML、CSS和JavaScript: HTML超文本标记语言(Hyper Text Markup Language):简单理解描述网页结构的;用于网页内容的语言。它通过使用不同的HTML标签来定义页面中的各种元素,例如标题、段落、图像、链接等【无羽毛的小鸟模型】 CSS层叠样式表(…

【Transient-Free 3DGS】delayed densification + coarse to fine增加GS的鲁棒性

25年最新连接去除场景瞬态对象工程与3DGS的pipeline&#xff0c;改进了spotlesssplats&#xff0c;已开源&#xff1a; [2506.02751] RobustSplat: Decoupling Densification and Dynamics for Transient-Free 3DGSAbstract page for arXiv paper 2506.02751: RobustSplat: De…

【MySQL】CRUD基础详解

CRUD基础前言&#xff1a;数据库的层级结构一、新增&#xff08;Create&#xff09;1. 单行数据 全列插入2. 单行数据的简写插入3. 指定列插入4. 多行数据插入二、查询&#xff08;Retrieve&#xff09;1. 全列查询2. 指定列查询3. 查询结果为表达式&#xff08;1&#xff09;…

互联网大厂Java求职面试实录:核心技术栈与业务场景解析

互联网大厂Java求职面试实录&#xff1a;核心技术栈与业务场景解析 面试场景设定 本文通过一个严肃的面试官和搞笑的水货程序员大面条之间的对话&#xff0c;模拟互联网大厂Java岗位的技术面试过程。面试涵盖Java SE、Spring生态、数据库、微服务、缓存、安全、消息队列、AI等多…

response对象的elapsed属性

在Python的requests库中&#xff0c;当我们发送一个请求后&#xff0c;会得到一个Response对象&#xff0c;这个对象有一个elapsed属性&#xff0c;它返回一个timedelta对象&#xff0c;表示从发送请求到收到响应所经过的时间。response.elapsed.total_seconds() 是 Python req…

【ansible】5.在受管主机部署文件和Jinja2模板

1.Ansible 中&#xff0c;如何用模块创建一个文件并设置权限644并设置SELinux类型&#xff0c;如何从受管主机中删除文件&#xff1f;使用ansible.builtin集合中的 file 模块&#xff0c;添加state&#xff1a;touch 创建文件&#xff0c;mode&#xff1a;‘0644’ 设置权限&am…

雪花算法数据库主键

雪花算法&#xff08;Snowflake&#xff09;作为一种分布式 ID 生成方案&#xff0c;在分布式系统中具有显著优势&#xff0c;能够解决多个关键问题。以下是它的核心好处及主要应用场景&#xff1a;雪花算法的核心好处全局唯一性&#xff1a;通过时间戳、机器 ID、数据中心 ID …

C/C++ 头文件命名约定

有的时候&#xff0c;在C的代码中&#xff0c;可以看到有如下的头文件引用的代码: #include <iostream> #include <unistd.h> #include <csignal>其中有一些是引用了.h文件&#xff0c;另外一些是引用了模块式的比如iostream和csignal&#xff0c;那么为什么…

异质结3.0时代的降本提效革命:捷造科技设备技术创新与产业拐点分析

光伏产业经历了从PERC到TOPCon和异质结&#xff08;HJT&#xff09;的技术迭代&#xff0c;而2025年将成为异质结技术规模化应用的关键转折点。捷造科技通过一系列突破性技术创新&#xff0c;将GW级异质结整线设备价格降至2亿元&#xff0c;较行业平均水平降低约40%&#xff0c…

【网络】http 协议中 Vary 标头的作用

在 HTTP 协议中&#xff0c;Vary 标头是一个关键的缓存控制机制&#xff0c;用于告知缓存服务器&#xff08;或代理&#xff09;&#xff1a;响应内容的生成依赖于请求中的哪些特定头部字段。其核心作用是确保缓存服务器能根据这些字段的差异&#xff0c;正确区分和返回不同版本…

CSS 进阶用法

一、选择器进阶复杂选择器组合详解后代选择器后代选择器使用空格分隔两个选择器&#xff0c;例如div p&#xff0c;表示选择div元素内所有的p元素。这种选择方式会匹配所有层级的后代元素&#xff0c;包括子元素、孙元素等任意深度的嵌套元素。应用示例&#xff1a;/* 选中arti…

GitHub 热榜项目 - 日榜(2025-08-23)

GitHub 热榜项目 - 日榜(2025-08-23) 生成于&#xff1a;2025-08-23 统计摘要 共发现热门项目&#xff1a;13 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点&#xff1a;1&#xff09;AI工作流构建成为风口&#xff0c;sim和airi等项目展示…

SHAP分析+KOA-RIME开普勒结合霜冰算法双重优化BP神经网络+9种映射方法+新数据预测!机器学习可解释分析!

代码主要功能 该Matlab代码实现了一个KOA-RIME开普勒结合霜冰算法双重优化的BP神经网络回归模型&#xff0c;结合特征贡献度分析&#xff08;SHAP&#xff09;和新数据预测功能。核心功能包括&#xff1a; 双重参数优化&#xff1a;先用智能算法&#xff08;以chebyshev映射改进…

【数据结构】栈和队列——栈

目录栈和队列栈栈的基本概念栈的顺序存储实现栈的定义与初始化入栈操作出栈操作读取栈顶元素判空和判满操作栈的销毁操作操作集合栈和队列 栈 栈的基本概念 栈的定义&#xff1a; 栈&#xff08;Stack&#xff09; 是一种线性表&#xff0c;它限定了数据元素的插入和删除操…

大数据管理与应用系列丛书《数据挖掘》读书笔记之集成学习(1)

文章目录前言一、集成学习是什么&#xff1f;1.基本思想2.集成学习的类型3. 集成学习的结合策略3.1 为什么结合策略是集成学习的灵魂&#xff1f;3.2 经典策略(1)**投票法&#xff08;Voting&#xff09;****(2)平均法&#xff08;Averaging&#xff09;****(3) 学习法**3.3 关…

嵌入式知识篇---32GUI

要理解 32 位单片机的 GUI&#xff0c;咱们先从 “基础概念” 入手&#xff0c;再拆成 “为什么能跑 GUI”“核心组成”“怎么实现”“常用工具”“实际用途” 这几步讲&#xff0c;全程不用复杂术语&#xff0c;像聊日常用品一样说清楚。一、先搞懂 2 个基础概念在讲 “32 位单…