Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sequelize 文档学习(基础篇) #56

Open
18888628835 opened this issue Jul 15, 2021 · 0 comments
Open

Sequelize 文档学习(基础篇) #56

18888628835 opened this issue Jul 15, 2021 · 0 comments

Comments

@18888628835
Copy link
Owner

18888628835 commented Jul 15, 2021

Sequelize 文档学习(基础篇)

源码地址

基本介绍

Sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。

什么是 ORM 呢,简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。

面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。很早就有人提出,关系也可以用对象表达,这样的话,就能使用面向对象编程,来操作关系型数据库。

ORM 把数据库中映射成对象:

  • 表(Tables)=> 类 (class)

  • 行数据(record)=>对象

  • 字段(field)=>属性

下图表示在代码逻辑中,Seq 与 DB 的关系。首先 Seq 会将表抽象成一个类,它生成很多 Objects实例,这些实例都通过 Seq 内部的逻辑,跟数据库 DB 做映射关联。

image-20210716094528777

下面通过案例快速了解Sequelize的运行过程(以下简称 Seq)

连接数据库

首先需要安装 Sequelize 和数据库驱动程序

npm install --save sequelize

# 选择其中之一就行
$ npm install --save pg pg-hstore # Postgres
$ npm install --save mysql2
$ npm install --save mariadb
$ npm install --save sqlite3
$ npm install --save tedious # Microsoft SQL Server

首先需要连接数据库,连接数据库需要先创建 Sequelize 实例,Sequelize 连接mysql 使用以下方法:

首先我们创建一个 config 配置文件

//config.ts
export const config = {
  database: "qiu", // 使用哪个数据库
  username: "root", // 用户名
  password: "123456", // 口令
  host: "localhost", // 主机名
  port: 3306, // 端口号,MySQL默认3306
};

其次将配置的参数传递给 Sequelize 构造函数,以连接数据库

//app.ts
import { config } from "./config";
const { Sequelize, DataTypes } = require("sequelize");

const sequelize = new Sequelize(
  config.database,
  config.username,
  config.password,
  {
    host: config.host, //主机
    dialect: "mysql", //别名可以写'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一
  }
);

这时候可以测试一下,采用官方定义的.authenticate函数测试连接是否正常,我用的是 ts,所以这里用 ts-node 运行

$ ts-node app

可以看到成功的字样了

Executing (default): SELECT 1+1 AS result
 连接成功.

创建Model(表)

Model,是模型的意思。ORM 框架会将数据库中的表抽象成模型,然后变成一个类。

这个类会告诉 sequalize 表名、列和列的数据类型。下面我们创建一个模型,也就是生成一个数据库中的表

const User = sequelize.define(
  "User",
  {
    // 在这里定义模型属性
    firstName: {
      type: DataTypes.STRING,
      allowNull: false,
      defaultValue: "qiu",
    },
    lastName: {
      type: DataTypes.STRING,
      defaultValue: "yanxi",
      // allowNull 默认为 true
    },
  },
  {
    // 这是其他模型参数
  }
);

然后我们需要同步一下,由于有可能我们定义的表会在数据库中已存在,所以我们需要使用同步来告诉 sequelize 同步的方式,这里有三种

  • User.sync() - 如果表不存在,则创建该表(如果已经存在,则不执行任何操作)
  • User.sync({ force: true }) - 将创建表,如果表已经存在,则将其首先删除
  • User.sync({ alter: true }) - 这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),然后在表中进行必要的更改以使其与模型匹配.

这里我们使用第二种进行学习

User.sync({ force: true });
console.log("同步成功");

运行程序

$ ts-node app

我们可以看到以下信息

同步成功
Executing (default): DROP TABLE IF EXISTS `Users`;
Executing (default): CREATE TABLE IF NOT EXISTS `Users` (`id` INTEGER NOT NULL auto_increment , `firstName` VARCHAR(255) NOT NULL DEFAULT 'qiu', `lastName` VARCHAR(255) DEFAULT 'yanxi', `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `Users`

英文的意思四号删除了原来的 Users 表,并且创建了 Users 表,里面有 id、firstName、lastName、createAt、updatedAt等属性。主键是 id,引擎使用了 InnoDB。

可以看到我们虽然只用了两个字段,但是 seq 帮助我们多做了一些工作:自动生成 id、createAt、updatedAt 等数据,还帮着我们把表名由我们定义的 User 改成了 Users。

那我们现在使用Sequel Pro可视化工具来查看一下新生成的表。

image-20210715171654500

模型实例-增删改查

模型是一个类,它所生成的实例是一个数据访问对象,通过这个实例,我们可以做一些操作。

增加数据

(async () => {
  await User.sync({ force: true });
  //create 是 build 方法+save 方法的集合
  const Jim = await User.create({ firstName: "Jim", lastName: "green" });
  const kitty = await User.create({ firstName: "hello", lastName: "kitty" });
})();

上面的代码我已经创建了两行数据,打开可视化工具看一下

image-20210715173622384

解释一下,seq 框架有 build 和 save 方法,建立一行数据用 build,它会创建一个对象(record),这个对象可以映射到数据库的数据。但是要保存到数据库中,还需要调用 save 方法。上面的 create 方法是语法糖,直接帮助我们完成了创建-保存的工作。

创建好实例后,如果我们希望查看实例的属性,最好调用实例的 toJSON 方法,而不要直接打印它

  const kitty = await User.create({ firstName: "hello", lastName: "kitty" });
  // console.log(kitty); 不要这样打印
  console.log(kitty.toJSON());

修改数据

返回的实例对象,我们直接修改,然后调用 save 方法保存一下就完成更新操作。

 //修改数据
  Jim.lastName = "red";
  //保存
  await Jim.save();
  kitty.firstName = "halo";
  //保存
  await kitty.save();

image-20210715184835859

删除数据

删除数据则调用 destroy 方法删除

  await Jim.destroy();

下面是删除操作时,seq 给我们返回的删除操作指令,说明删除成功了

Executing (default): DELETE FROM `Users` WHERE `id` = 1

重载数据

重载数据是什么意思呢?比如下面这行代码

  const Jim2 = await User.create({ firstName: "Jim", lastName: "green" });
  Jim2.firstName = "Jim2";
  console.log(Jim2.firstName); //Jim2 但是数据库中的数据没变,依然是Jim
  await Jim2.reload(); //但是我依然希望能得到数据库的数据而不是 Jim2,可以调用 reload方法
  console.log(Jim2.firstName); //Jim

调用reload 方法实际上会再去数据库 select 查询最新的数据,然后返回结果。

上面的代码,在打印台会这样生成查询语句

Executing (default): INSERT INTO `Users` (`id`,`firstName`,`lastName`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?);
Jim2
Executing (default): SELECT `id`, `firstName`, `lastName`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`id` = 3;
Jim

保存部分字段

现在我们知道,除了 create 的创建数据和destroy 方法的删除数据以外,我们都需要调用 save 方法来将数据保存到数据库当中。

有没有可能我在修改实例对象的多个数据后,仅仅保存一个字段的数据到数据库中呢,seq 是同样支持的。以下是实现

  const qiu = await User.create({ firstName: "qiu" });
  qiu.firstName = "Qiu";
  console.log(qiu.lastName); //按照默认值生成的 yanxi
  qiu.lastName = "YanXi";
  qiu.save({ fields: ["lastName"] });

save 方法当中传入一个对象,属性为 fields,值为一个数组,数组中放希望保存的字段就可以实现部分保存。

image-20210715193142665

此时实例对象的 firstName 已经变成了 Qiu,但是你可以看到数据库中还是 qiu,所以我们需要 reload 一下

  qiu.reload();
  console.log(qiu.firstName); //qiu

数据递增和递减

seq 中递增和递减使用两个方法,分别是incrementdecrement

现在我们返回define 方法,再新增一个 age 字段,它的 DataTypes 是 INTEGER

    //...省略原来定义的字段
    age: {
      type: DataTypes.INTEGER,
      defaultValue: 0,
    },

下面我们再创建一个 record 对象,并让其 age 一次递增1

  const yang = await User.create({ firstName: "yang" });
  console.log("yang.age", yang.age); //0
  const incrementResult = await yang.increment("age");
  console.log("yang.age", yang.age); //0
  console.log("incrementResult.age", incrementResult.age); //0

此时数据库中的数据已经递增1了,不过需要注意一下,这时候的incrementResultyang实例对象的数据都没有变化,需要我们 reload 一下。

  await yang.reload();
  console.log("yang.age", yang.age); //1
  console.log("incrementResult.age", incrementResult.age); //1

对此官方的解释是这样的

const jane = await User.create({ name: "Jane", age: 100 });
const incrementResult = await jane.increment('age', { by: 2 });
// 注意: 如只增加 1, 你可以省略 'by' 参数, 只需执行 `user.increment('age')`

// 在 PostgreSQL 中, 除非设置了 `{returning:false}` 参数(不然它将是 undefined),
// 否则 `incrementResult` 将是更新后的 user.

// 在其它数据库方言中, `incrementResult` 将会是 undefined. 如果你需要更新的实例, 你需要调用 `user.reload()`.

不过经过实际测试,在 mysql 环境下,incrementResult其实是原来的实例对象而不是 undefined。(其他数据库不详)

一次性递增多个字段分两种:

  • 递增不同数量

  • 递增相同数量

可以这样写

const jane = await User.create({ name: "Jane", age: 100, cash: 5000 });
await jane.increment({
  'age': 2,
  'cash': 100
});

// 如果值增加相同的数量,则也可以使用以下其他语法:
await jane.increment(['age', 'cash'], { by: 2 });

递减在写法上是一样的,就不赘述了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant