Node.js+MySQL不使用Sequelize实现MVC时自动获取模型表名方案
自定义MVC框架模型层通用CRUD实现方案
问题根因
你当前使用全局变量db.model存储表名的写法存在并发安全问题:多请求同时触发不同模型查询时,全局表名会被覆盖导致串表,同时将CRUD逻辑重复写在每个模型中会产生大量冗余代码。
核心实现思路是封装基础模型基类:所有通用增删改查逻辑统一在基类实现,业务模型仅需继承基类、配置对应表名即可直接复用所有通用方法,无需重复编码,也不存在全局变量污染问题。
具体实现步骤
1. 重构db配置层
db层仅保留数据库连接池和基础SQL执行能力,移除全局表名相关逻辑,同时修复原代码SQL拼接缺少空格的语法问题,统一使用参数化查询避免SQL注入:
// src/config/db.config.js const mysql = require("mysql2"); const creds = { host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_SCHEMA, connectionLimit: 10, multipleStatements: false }; const pool = mysql.createPool(creds); // 暴露基础查询方法,仅负责SQL执行,不耦合业务表逻辑 exports.query = (sql, params = []) => { return new Promise((resolve, reject) => { pool.query(sql, params, (err, results) => { if (err) return reject(err); resolve(results); }); }); }; exports.escape = str => pool.escape(str);
2. 封装通用基础模型类
新建基础模型类,所有通用CRUD方法统一在此实现,方法会自动读取调用方(业务模型)配置的表名,无需手动传参:
// src/models/BaseModel.js const db = require("../config/db.config"); class BaseModel { // 业务模型继承后重写该属性,填入自身对应的表名即可 static tableName = ""; /** * 通用查询方法 * @param {string} fields 查询字段,默认* * @param {string} condition 查询条件,默认1=1查全表 * @param {Array} params 条件参数,用于参数化查询 */ static async get(fields = "*", condition = "1=1", params = []) { // 此处this指向调用方法的业务模型类,自动读取对应表名 const sql = `SELECT ${fields} FROM ${this.tableName} WHERE ${condition}`; return db.query(sql, params); } // 通用新增方法 static async create(data) { const keys = Object.keys(data).join(","); const placeholders = Object.keys(data).map(() => "?").join(","); const values = Object.values(data); const sql = `INSERT INTO ${this.tableName} (${keys}) VALUES (${placeholders})`; const res = await db.query(sql, values); return res.insertId; } // 通用更新方法 static async update(data, condition, conditionParams = []) { const setClause = Object.keys(data).map(key => `${key} = ?`).join(","); const values = [...Object.values(data), ...conditionParams]; const sql = `UPDATE ${this.tableName} SET ${setClause} WHERE ${condition}`; const res = await db.query(sql, values); return res.affectedRows; } // 通用删除方法 static async delete(condition, conditionParams = []) { const sql = `DELETE FROM ${this.tableName} WHERE ${condition}`; const res = await db.query(sql, conditionParams); return res.affectedRows; } } module.exports = BaseModel;
3. 简化业务模型代码
所有业务模型不需要重复写任何CRUD逻辑,仅需继承BaseModel、配置自身表名即可完成模型定义:
// src/modules/tutorials/tutorial.model.js const BaseModel = require("../../models/BaseModel"); class Tutorial extends BaseModel { // 仅需配置表名,其余通用方法自动继承 static tableName = "tutorials"; // 如果需要自定义实例方法、业务关联逻辑,直接在类中编写即可,不影响通用CRUD } module.exports = Tutorial;
调用说明
原有控制器、路由层代码不需要做任何修改,Tutorial.get()等调用可以直接正常运行。后续新增其他业务模型(比如User、Article)时,只需要继承BaseModel配置对应表名,就能直接使用所有通用CRUD方法,不需要重复编写数据操作逻辑。
注意:不要继续使用全局变量存储表名,类继承的实现方式天然做了模型隔离,多并发场景下不会出现串表的问题;所有查询条件尽量用参数化传参,不要直接拼接字符串到SQL中,避免SQL注入风险。
内容的提问来源于stack exchange,提问作者sabin maharjan
相关产品推荐
相关产品推荐

