数据库应用

1.数据库与ORM的关系

  • 数据库:是存储数据的地方,如MySQL、PostgreSQL等。它们使用SQL(结构化查询语言)来操作数据
  • ORM(对象关系映射):是一种技术,它允许你通过面向对象的方式来操作数据库,而不是直接编写SQL语句。ORM工具(如Sequelize、TypeORM)将数据库表映射为编程语言中的类(或模型),使得你可以用更自然的方式处理数据。
  • 学习基本的SQL:了解如何创建表、插入数据、查询数据等,这有助于更好地理解ORM的工作原理
  • 选择合适的ORM工具:根据你的项目需求和技术栈选择一个合适的ORM工具,并深入学习其文档

2.外键与关联

  • 外键含义:在设计数据库时,经常会遇到需要表示实体之间关系的情况,这时就需要使用外键

外键需要 InnoDB 引擎的支持,MySQL5.5之后的版本默认引擎为InnoDB,操作数据表时使用哪个引擎是数据库本身的设置,例如小皮面板里面可以看到具体的设置(所以 ORM 使用时不需要关注引擎设置),但是可以使用sql语句进行临时指定,但是这种情况少

  • 创建外键:外键是数据表自带的功能,可以通过图形化界面创建,也可以通过SQL语句创建,也可以通过ORM进行定义(内部还是SQL)

在数据库中创建外键(Foreign Key)时,被引用的表(父表 / 主表)必须已经存在

  • 定义模型之间的关系:在Sequelize中,可以使用hasOne, belongsTo, hasMany, belongsToMany等方法来描述这些关系,MySql本身没有这种关联关系,它通过SQL语句例如LEFT JOIN进行关联,不依赖于外键

3.外键约束

  • 含义:当你定义一个外键时,可以指定四种不同的行为来控制 DELETE 和 UPDATE 操作
  • CASCADE:级联操作。如果父表中的记录被更新或删除,则相应的子表中的记录也会自动更新或删除。
  • SET NULL:将子表中的外键字段设置为 NULL。仅当外键字段允许 NULL 值时有效。
  • NO ACTION 或 RESTRICT:拒绝执行导致违反外键约束的操作。在 MySQL 中,这两个选项的行为是相同的。
  • SET DEFAULT:将子表中的外键字段设置为其默认值。注意,并不是所有的存储引擎都支持这个选项

4.ORM 框架

  • 含义:ORM 是 "Object-Relational Mapping" 的缩写,即对象关系映射,通俗的说,就是通过 ORM 让开发中可以使用熟悉的编程语言语法来执行常见的数据库操作,代替原生的SQL
  • sequelize 是一个基于 promise 的 Node.js ORM,他通过定义模型的形式(一个数据表对应一个模型),来操作数据表
  • 安装
  • 创建实例
  • 通过实例定义模型(数据表结构)
  • 同步模型
  • 调用模型,进行常用查询的操作

5.一些注意点

  • 树形结构的数据,存在子节点时,不得删除,防止数据不一致,也不允许修改节点位置
  • 父级角色取消勾选某些权限节点时,子级角色相关的节点权限需要一并删除

6.升级路线

阶段 可以做的事 目标
初级版 最简 CRUD + 基础权限 能跑起来
中级版 封装 service、拦截器、日志 更规范
高级版 多租户支持、软删除恢复、数据归档 更健壮
企业级 权限系统、审计日志、接口文档自动生成 可交付

7.ID 的选择

  • 如果系统是单体架构、无分布式数据库需求、对 ID 可读性要求高,自增 ID 是更优解,性能更优,可读性强,实现简单
  • 如果系统涉及分布式部署(多数据库节点)、数据迁移 / 合并、需隐藏数据量,可考虑 UUID,全局唯一,隐藏数据规模,便于数据迁移(无需担心 ID 重复,避免繁琐的 ID 重映射)
  • 关于id命名:主键推荐直接用 id,因为 users 表里的 id 当然就是 “user 的 id”,外键用 xx_id关联即可

8.字段命名

  • 推荐:数据库列名用下划线,代码用驼峰法,通过ORM自动完成转换(包括读取与写入)
  • Sequelize的配置方法:初始化实例时,通过define.underscored进行配置
const sequelize = new Sequelize('数据库名', '用户名', '密码', {
  host: 'localhost',
  dialect: 'mysql',

  // 🔥 在这里统一配置,所有模型都生效
  define: {
    underscored: true,        // 自动把 camelCase 转成 snake_case
  }
});

9.数据库配置

  • 代码要可复用,配置要可变

数据库的配置文件是代码的一部分,必须要提交,但是配置具体值(如密码、密钥)属于环境数据,不应进代码库,因为他不仅会泄露开发者隐私,也不符合开发(dev)、测试(test)、生产(prod)环境隔离的原则,而且如果配置写死在代码里,部署时容易覆盖导致出错,所以要结构留着,数据剥离,一般在项目根目录用 .env 存值,然后借助插件进行读取并填入配置文件中

  • 安装 dotenv,用于加载 .env 文件
npm install dotenv
  • 编写 .env 文件 和 .env.example 文件
# .env
DB_USER=root
DB_PASS=root
DB_NAME=wms
DB_HOST=localhost

# secrets 相关
JWT_SECRET=12345678
JWT_EXPIRES_IN_DAYS=3
SALT_LENGTH=8
  • 务必把 .env 加入 .gitignore
# .gitignore
.env
  • 在主入口读取配置
// app.js (或你的主入口文件)
require('dotenv').config(); // 👈 必须放在最顶部!
  • 在配置文件中进行读取
// config/database.js
module.exports = {
    development: {
      username: process.env.DB_USER || 'root',
      password: process.env.DB_PASS || 'root',
      database: process.env.DB_NAME || 'wms',
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT, 10) || 3306,
      dialect: 'mysql',
      logging: false,//关闭日志提示

      define: {
        underscored: true,        // 自动把 camelCase 转成 snake_case,模型里面继续用驼峰法(数据库用下划线,代码用驼峰)
      }
    },
}
// config/secrets.js
module.exports = {
    development: {
        jwtSecret: process.env.JWT_SECRET || '12345678',
        expiresIn: parseInt(process.env.JWT_EXPIRES_IN_DAYS, 10) || 3,
        saltLength: parseInt(process.env.SALT_LENGTH, 10) || 8
    }
}

10.Sequelize 常见用法

  • 基本查询
// 查找所有用户
const allUsers = await User.findAll()

// 根据条件查找(单条)
const user = await User.findOne({ where: { userName: 'Alice' } })

// 根据主键查找
const userById = await User.findByPk(1)

// 带条件的多条查询 + 分页:第 1 页,每页 10 条
const page = 1
const pageSize = 10

const activeUsers = await User.findAll({
  where: {
    status: 'active'
  },
  offset: (page - 1) * pageSize, // 跳过前 (page-1)*pageSize 条
  limit: pageSize                // 最多返回 pageSize 条
})
  • 关联声明:用于指导 ORM 在内部生成 JOIN 查询时的关联逻辑
// 关联声明:用于指导 ORM 在内部生成 JOIN 查询时的关联逻辑
sequelize.models.UserRole.belongsTo(sequelize.models.User, { foreignKey: 'userId' })
sequelize.models.UserRole.belongsTo(sequelize.models.Role, { foreignKey: 'roleId' })

// 等价于显式指定目标主键(Sequelize 默认 targetKey 为模型的主键 'id')
sequelize.models.UserRole.belongsTo(sequelize.models.User, { foreignKey: 'userId', targetKey: 'id' })
sequelize.models.UserRole.belongsTo(sequelize.models.Role, { foreignKey: 'roleId', targetKey: 'id' })
  • 关联查询:最终结果 = 条件1 ∩ 条件2 (交集)
UserRole.findAll({
  include: [
    { model: User, where: { userName: 'Alice' } },   // 条件1:Alice → User.userName → User.id → UserRole.userId
    { model: Role, where: { roleName: 'Admin' } }    // 条件2:Admin → Role.roleName → Role.id → UserRole.roleId
  ]
})

// 1. 每个 include 的模型都会以「模型名」为 key,嵌套返回一个对象
// 2. 想控制关联对象里包含哪些字段?用 attributes
// 3. 自定义嵌套 key 名(使用 as)
// 4. 若仅需附带关联数据而不影响主记录的返回,应设置 required: false(即使用 LEFT JOIN)
posted @ 2025-03-17 11:33  ---空白---  阅读(31)  评论(0)    收藏  举报