You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 13:21:30