前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Node】sequelize 使用对象的方式操作数据库

【Node】sequelize 使用对象的方式操作数据库

作者头像
神仙朱
发布2022-04-12 13:53:29
7.9K0
发布2022-04-12 13:53:29
举报

本文分享 sequelize 的项目实践经验,如果你还不了解 sequelize,可以先看文档

https://www.sequelize.com.cn/

sequelize 是一个可以使用对象的方式操作数据库的 第三方工具库,是一个基于 promise 的 Nodejs ORM

ORM全称是:Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。其实我觉得就是用 mongo 的方式操作 mysql 等关系型数据库

用ORM的好处就是你不用操作表,不用写sql语句,在程序中用面向对象的思路,直接操作对象即可。比如要插入一条语句,直接user.save()即可。对应的 ORM 会生成一条 sql 语句

优点是简化了curd,缺点是牺牲了速度,orm需要解析表结构

下面就主要介绍 sequelize 的具体用法,分为

1、数据库初始化

2、定义表结构 model

3、查询

4、创建

5、更新

6、删除

7、事务

重头在增删改查这几个部分,重点记录下使用的场景和踩得坑,怎么用还是得看文章

1 数据库初始化

首要工作就是使用 sequelize 连接上数据库,如下

代码语言:javascript
复制
const Sequelize = require('sequelize’);
const MysqlConnect = new Sequelize(‘数据库名', ‘账号', '密码', {
  define: {
    timestamps: false, // 全局默认不要 createAt 和 updateAt,自动管理时间
  },
  dialect: 'mysql', // 数据库类型,| 'mariadb' | 'postgres' | 'mssql'
  host: ’10.10.10.10', // ip
  port: 8800, // 端口
  logging: (msg) => { // 日志信息,打印出每个操作生成的具体的 sql语句
    console.log('msg', msg);
  },
  timezone: '+08:00', // 时区,在中国就是 +8
});

其中 timezone 需要设置 +8,因为中国是 东八区, 如果没有设置,默认是 UTC 时间,那么你 2022-1-2 22:00 插入一条数据,但是具体显示的时间变成 2022-1-2 14:00

以前我还踩过一个时区的坑,然后才去学习总结了一下

【踩坑】服务器和本地相差8小时

2 定义数据表结构 model

连接上数据库之后,需要对里面的表做一个映射,表里面有什么字段,字段有什么属性,统统都要列出来

因为 sequelize 不知道表都有什么字段,需要你告诉他,如果你没有列出来某个字段,但是实际操作了这个字段的话,会报错

简单看下模型的定义

代码语言:javascript
复制
function PersonModel(sequelize, DataTypes) {
  return sequelize.define(
    "person", // 给模型自定义个名字,通常是表名的驼峰写法
    {
      id: {
        type: DataTypes.BIGINT, // 数据类型,有 String、Date 等等
        allowNull: true, // 是否允许为空
        primaryKey: true, // 是否主键
        autoIncrement: true, // 是否自增
        comment: "id", // 备注
      },
    },
    {
      sequelize,
      tableName: "person", // 表名称
    }
  );
};

更多 DateTypes 类型请看

https://sequelize.org/master/variable/index.html#static-variable-DataTypes

定义完模型之后,需要把 模型和 之前 初始化连接的数据库 给 关联起来,才可以使用

代码语言:javascript
复制
const Person = MysqlConnect.import(MyModel);

之后我们就是用这个 关联之后的对象,直接进行CRUD的操作

Person.create() 等等

下面记录一些 定义model 时 多会用到的配置

1、自动生成model

复杂的数据表如果要一个个人工去定义,那可太麻烦了,可有使用

https://github.com/sequelize/sequelize-auto

这个库帮我们自动生成model,它直接拉取数据库表的信息,然后生成对应的model

2、定义设置器(get,set)

有的数据出库入库的时候,需要做一些额外的处理,这时就适合放到 设置器去做,比如

2.1 时间格式化

代码语言:javascript
复制
function MyModel(sequelize, DataTypes) {
  return sequelize.define(
    "person",
    {
      create_time: {
        type: DataTypes.DATE,
        get() {
          const v = this.getDataValue('create_time');
          if(v instanceof Date && v.getTime()){
            return moment(v).format('YYYY-MM-DD HH:mm:ss') 
          }
          return null
        },
      },
    }
  );
};

2.2 对象or 数组序列化

代码语言:javascript
复制
function MyModel(sequelize, DataTypes) {
  return sequelize.define(
    "person",
    {
      list: {
        type: DataTypes.STRING(1024),
        get() {
          const v = this.getDataValue('list');
          return typeof v === 'string' && v ? JSON.parse(v) : [];
        },
      },
    }
  );
}

3 查询

查询是最基础的操作,也是我们比较常用的,也没什么特殊要讲的

列举几个查询常用的查询操作

1、分页

2、排序

3、分组

4、限定查询字段

5、查出不重复数量

1、分页

代码语言:javascript
复制
const Person = MysqlConnect.import(MyModel);
const page = 1;
const pageSize = 10;
const result = await Person.findAll({
  limit: pageSize,
  offset: page * pageSize,
});

2、排序

代码语言:javascript
复制
const Person = MysqlConnect.import(MyModel);
const result = await Person.findAll({
  order: [
    ['type', 'DESC'],
    ['id', 'ASC'],
  ],
});

优先满足按 type 降序,如果 type 一样, 那么按 id 升序。

3、分组

以 type 对数据进行分组,相同值的放到一组,反正和 mysql 是一样的

代码语言:javascript
复制
const Person = MysqlConnect.import(MyModel);
const result = await Person.findAll({
  group: 'type',
});

只使用 group ,只会查出每个分组的第一条数据

通常都是用来做统计,换成调用 findAndCountAll 来获得各个分组数量

代码语言:javascript
复制
const Person = MysqlConnect.import(MyModel);
const result = await Person.findAndCountAll({
  group: 'type',
});

4、查询字段

代码语言:javascript
复制
const Person = MysqlConnect.import(MyModel);
const result = await Person.findAll({
  attribute: ['name', 'id’], // 只查出某些字段
  attributes: { exclude: ['id'] }, // 不需要某些字段
  attributes: ['id', ['name', 'label_name']], // 重写字段名称,name 改成 label_name
});

** 查出所有属性

通常我们不设置 attribute,默认查出所有属性

但是 sequelize 只会查出 model 中定义有的字段

比如 model 定义只有 id 和 name 两个字段,那么 sequelize 生成的 sql 语句 只查出这两个字段

代码语言:javascript
复制
SELECT `id`, `name` FROM `person` 

就算表里面还有其他字段,你也无法查出来,除非你更新model

后期维护的时候,表新增字段,model 不一定会及时同步更新,要想还能查出所有字段

代码语言:javascript
复制
const result = await Person.findAll({
  attribute: [‘*’], // 查出所有字段
});

5、查出某字段不重复的数量

代码语言:javascript
复制
const result = await Person.findAndCountAll({
  //  查询某字段不重复的【数量】,而不是记录,默认是id,如果需要其他字段就写 定义 col
  distinct: true,
  // 查出 name 不重复的数量
  col: 'name',
});

注意这里是查出数量,不是数据,比如下面以name 为重复字段的,虽然有 6条数据,但是 counts 是 2

3.1 查询条件

查询条件用 sequelize 之后可以简化很多,使用各种逻辑操作符组合的方式,轻轻松松就能写出复杂的查询语句

详细的逻辑操作符可以看具体文档

https://www.sequelize.com.cn/core-concepts/model-querying-basics#%E6%93%8D%E4%BD%9C%E7%AC%A6

难度不大,根据业务逻辑组合即可,比如这样

代码语言:javascript
复制
const result = await Person.findAll({
  where: {
    // 一个搜索框搜索多个字段
    [Op.or]: {
      phone: {
        [op.like]: `%${search}%`,
      },
      name: {
        [op.like]: `%${search}%`,
      },
      address: {
        [op.like]: `%${search}%`,
      },
    },
    // 搜索在有效期内的
    [Op.And]: {
      bg_time: { [Op.lt]: sequelize.fn('NOW') },
      end_time: { [Op.gt]: sequelize.fn('NOW') },
    },
  },
});

3.2 查询 - 关联表

简单描述下

通常不同类型的数据存放不同的表,但是不同表的数据之间是有关联的,比如 用户表和 评论表,所以需要表与表之间建立联系

常用的三种关联类型就 一对一,一对多,多对多

建立这种关联通常是通过外键的形式,比如在 a 表中 存放 b 表中有关联的数据的id,这样a表就和b 表的数据产生了关联

1一对一

A表的每一条数据 只能和 B 表的一条数据产生关联,B 表也是,一般很少用到一对一的情况,因为通常这样都可以直接放在一张表里,所以这里的应用就是 当表结构特别庞大的时候可能,会进行拆分表,此时一对一就有作用了

在 sequelize 中需要把两张表的 model 手动关联起来,这样他才知道这两张表的关系,从而可以一次性把两张表的数据都查出来

比如一个人只有一个身份证

代码语言:javascript
复制
function IdCardModel(sequelize, DataTypes) {
  return sequelize.define(
    “idCard",{
      cardId: { type: DataTypes.INT },
    }
  );
};
const IdCard = MysqlConnect.import(IdCardModel);
Person.hasOne(IdCard);
Person.findAll({
  include: IdCard, // 直接传入model
});

这样在查人的时候,把另一张表的身份证信息一起查出来

代码语言:javascript
复制
[{
   id:1212,
   name: "xxxxxx",
   idCard:{ cardId:22222 }
}]

因为表关联需要外键,但是上面明显没有写明外键,所以 sequelize 会推断外键在 IdCard 中为 personId,如果没有就会报错 [找不到 idCard.personId 这个字段]

自定义外键,在 IdCard 中的外键为 user_id 和 Person 表产生关联

代码语言:javascript
复制
Person.hasOne(IdCard, {
  foreignKey: 'user_id',
});

重命名 IdCard 表数据 在 Person 信息中的字段名,即 下面数据中 idCard 这个字段名,通常 sequelize 默认会以 idCard 定义的model 名作为字段名

代码语言:javascript
复制
[{
   id:1212,
   name: "xxxxxx",
   idCard:{ cardId:22222 }
}]
代码语言:javascript
复制
Person.hasOne(IdCard, {
  foreignKey: 'user_id',
  as: 'card',
});
Person.findAll({
  include: [
    {
      model: IdCard,
      as: 'card', // 如果在关联的时候使用了as 重命名,那么include 的时候也需要as
    },
  ],
});

[{
   id:1212,
   name: "xxxxxx",
   Card:{ cardId:22222 }
}]

上面是通过 Person 表找到 IdCard 表,如果反之,则需要反过来关联一次

代码语言:javascript
复制
IdCard.belongsTo(Person, {
  foreignKey: 'user_id',
});
IdCard.findAll({
  include:Person
});

这样数据就变成

代码语言:javascript
复制
[{
   cardId:1212,
   person:{ 
     id:1212,
     name: "xxxxxx”, 
   }
}]

因为当进行关联的时候,只有源模型知道 两个模型的关系(前面的是源模型,后面的是目标模型)

另一个模型不知道之间的关系的,所以另一个模型在查询的时候就无法查出关联的表的数据,所以需要反过来关联一次

2一对多

一对多的场景就有很多,比如一个视频有多条评论,一个商品有多个类型,一个用户有多个收货地址

同样需要把表进行关联

代码语言:javascript
复制
person.hasMany(personComment, {
  foreignKey: "user_id",
  as: "comment",
});

查出用户所有的评论

代码语言:javascript
复制
person.findAll({
  include:personComment
});

如果想通过评论查出用户信息,同样使用 belongsTo 关联即可

3多对多

多对多比较特殊一点,因为此时外键放在哪张表都无法满足,所以需要一张专门的外键表

比如 一个活动有多个标签,一个标签属于多个活动,此时就需要一张表额外存放 活动 和标签的对应关系

详细内容了解 sequelize 文档

https://www.sequelize.com.cn/core-concepts/assocs#%E5%A4%9A%E5%AF%B9%E5%A4%9A%E5%85%B3%E7%B3%BB

首先定义 表的model,那么这里就要定义三个表的model 了,包括一张 act_label 的外键表

代码语言:javascript
复制
function ActModel(sequelize, DataTypes) {
  return sequelize.define(
    'act',
    {
      name: { type: DataTypes.STRING(40) }
    },
    { sequelize, tableName: 'act'},
  );
};

function LabelModel(sequelize, DataTypes) {
  return sequelize.define(
    "label",
    {
      name: { type: DataTypes.STRING(40), }
    },
    { sequelize, tableName: 'label'},
  );
};

function ActLabelModel(sequelize, DataTypes) {
  return sequelize.define(
    "act_label",
    {
      id: { type: DataTypes.BIGINT },
      act_id: { type: DataTypes.BIGINT },
      label_id: { type: DataTypes.BIGINT },
    },
    { sequelize, tableName: ‘act_label'},
  );
};

然后进行关联即可

代码语言:javascript
复制
const act = MysqlConnect.import(ActModel);
const label = MysqlConnect.import(LabelModel);
const act_label = MysqlConnect.import(ActLabelModel);

act.belongsToMany(label, {
  through: {
    model: act_label,
    // 当模型中不存在主键时,Belongs-to-Many 将创建一个唯一键. 可以使用 uniqueKey 参数覆盖此唯一键名.
    // 若不希望产生唯一键, 可以使用 unique: false 参数.
    unique: false,
  },
  foreignKey: 'act_id',
  otherKey: 'label_id',
  as: 'labels',
});

act.findAll({
  include: [
    {
      model: label,
      as: 'labels',
      through: {
        attributes: [], // 不要关系表 act_label 产生的数据
      },
    },
  ],
});

使用场景

1、给include 关联的表的数据排序

代码语言:javascript
复制
await person.findAll({
  //  以关联表 comments 的 id 排序
  order: [['comments', 'id', 'DESC']],
  include: [
    {
      model: personComment,
      as:"comments"
    },
  ],
});

2、查询有子表数据的数据

也是比较常见的场景,比如返回有评论的用户数据,感知活跃的用户之类的

代码语言:javascript
复制
await person.findAll({
  include: [
    {
      model: personComment,
      as:"comments",
      required: true //  表示该 comment 数据是否必须,如果为空那么整个person对象都不返回
    },
  ],
});

这里涉及的是数据库的一些概念,left-join、inner-join、right-join 等

加了 required 的设置,就是 inner-join,只查出 两表的有交集的数据

去掉 required ,就是 left-join,查出所有 A 表数据并包含B表的部分数据

具体可以看这篇文章

https://segmentfault.com/a/1190000017369618

3、限制子表数据的总数量

这里不是说限制单个person 的 comments 返回数量,比如这样

代码语言:javascript
复制
await person.findAll({
  include: [
    {
      model: personComment,
      limit:10
    },
  ],
});

而是限制返回的数据 所有 person 的 comment 的总数量,开整

代码语言:javascript
复制
await person.findAll({
  limit: 5,
  // 加了这个false之后,limit 就变成限制 comments 的数量
  subQuery: false,
  include: personComment,
});

返回的数据是,总共有5条comments, person 有多少个无所谓

这里碰到的一个场景是,有一个管理页面翻页是以 子表数据为 准的,但是还是以父表作为主体存在

4 创建

1单个创建

代码语言:javascript
复制
await person.create({
  name: `神仙朱--`,
},);

2批量创建

代码语言:javascript
复制
await person.bulkCreate([
  {
    name: `hoho1`,
  },
  {
    name: `hoho2`,
  },
]);

批量创建的一个使用场景是 可以避免创建重复的数据,直接插入数据

如果数据不存在,就插入,如果存在,就更新

怎么判定这个数据是否存在?

通过主键或者 唯一索引

比如表中已经存在 id 为 1 的数据,此时你再插入 id 为1 的数据,那么就只会进行更新,不会再插入

下面介绍几个添加的场景

1、限定插入的字段

2、限定更新的字段

3、关联表创建

4、自动管理时间

限定插入的字段

有的字段是不需要手动插入的,避免被误操,我们可以限定字段,比如通常 updateTime 添加的时候是不用增加的

代码语言:javascript
复制
await person.bulkCreate(
  [
    {
      name: `hoho1`,
      sex: 1,
      salary: 100,
    },
    {
      name: `hoho2`,
      sex: 2,
      salary: 100,
    },
  ],
  {
    // 插入数据时,只插入 name 和 sex
    fields: ['name', 'sex'],
  },
);

限定更新的字段

如果插入重复的数据 触发更新操作的时候,我们可以限定更新的字段,比如通过 createUser 和 createTime 是不用更新的

代码语言:javascript
复制
await person.bulkCreate(
  [
    {
      name: `hoho1`,
      sex: 1,
      salary: 100,
    },
    {
      name: `hoho2`,
      sex: 2,
      salary: 100,
    },
  ],
  {
    // 更新数据时,只更新 name 和 sex,就算插入 salary 也不更新
    updateOnDuplicate: ['name', 'sex'],
  },
);

除个别字段外, fields 和 updateOnDuplicate 通常要保持一致,需要更新的字段,不仅要放在 updateOnDuplicate 中,更要放在 fields 中

如果数据表字段很多,那岂不是要一个个写完?因为 fields 和 updateOnDuplicate 都是白名单,所以需要明确把字段写上去

但是我们可以通过 lodash 排除个别字段即可

代码语言:javascript
复制
const _ = require('lodash’);
const fields = Object.keys(person.tableAttributes);
const fieldsCreate = _.without(fields, 'updateTime');
const fieldsUpdate = _.without(fields, 'createUser', 'createTime');
await person.bulkCreate(
  [
    {
      name: `hoho1`,
      sex: 1,
      salary: 100,
    },
  ],
  {
    fields: fieldsCreate,
    updateOnDuplicate: fieldsUpdate,
  },
);

关联创建

我们可以通过关联表的形式创建,一次性插入两张表的数据,不用分开插入

代码语言:javascript
复制
await person.create(
  {
    name: `hoho`,
    comment: [
      { content: ‘hoho-content’ },
    ],
  },
  {
    include: [
      {
        model: person_comment,
        as: 'comment',
      },
    ],
  },
);

自动管理时间

通常像 create_time,update_time 这种时间,自己维护的话会比较麻烦,我们可以把这部分数据交给 sequelize 去做,它可以自动帮我们管理时间

主要在 model 定义的时候开启这个配置

代码语言:javascript
复制
function PersonModel(sequelize, DataTypes) {
  return sequelize.define(
    'person',
    {
      list: {
        type: DataTypes.STRING(1024),
      },
    },
    {
      sequelize,
      tableName: 'person', // 表名称
      timestamps: true,
    },
  );
}

注意,它只是帮我们管理这个字段,我们需要自己创建 createAt 和 updateAt 这个两个字段,它并不会帮我们创建

如果你不想每张表都写这个定义,直接整个数据都统一配置,那么在初始化的时候配置即可

代码语言:javascript
复制
const Sequelize = require('sequelize’);
const MysqlConnect = new Sequelize(‘数据库名', ‘账号', '密码', {
  define: {
    timestamps: true, // 整个库默认自动管理时间,自动更新 createAt 和 updateAt
  },
  host: ’10.10.10.10', // ip
  port: 8800, // 端口
  timezone: '+08:00', // 时区,在中国就是 +8
});

访问文档了解更多

https://www.sequelize.com.cn/core-concepts/model-basics#%E6%97%B6%E9%97%B4%E6%88%B3

5 更新

更新就比较简单,没有什么特殊的点

批量更新

加上查询条件即可

代码语言:javascript
复制
await person.update(
  {
    name: '修改过',
  },
  {
    where: {
      status: 3,
    },
  },
);

单个更新

查询出来的每个数据对象,都包含了update 方法,通常利用这个把数据查出来之后,进行批量差别更新

代码语言:javascript
复制
const result = await person.findAll();
const list = result.map((item, index) => {
  return item.update({
    sex: index,
  });
});
const resultList = await Promise.all(list);

或者需要额外做一些业务逻辑来做一些过滤, 在查询条件无法满足的时候

比如说,视频可以打标签,每次编辑视频的时候,都可以新增标签,也可以修改原有标签,也可以删除标签

此时我们就可以先把 该视频的所有 标签查出来,然后加上各种逻辑分支进行处理

代码语言:javascript
复制
const { labels, videoId } = request.body;
const videoList = await LabelsModel.findAll({
  video_id: videoId,
});
labels.forEach((la) => {
  const [item] = _.remove(videoList, (x) => x.name === la.name);
  // 更新
  if (item) {
    item.update(la);
  }
  // 不存在就添加
  else {
    LabelsModel.create(la);
  }
});
// 多余的删除
if (labels.length) {
  labels.forEach((item) => {
    LabelsModel.destory({
      name: item.name,
    });
  });
}

6 删除

删除主要有下面3个使用场景

1、直接删除

2、软删除

3、关联表删除

直接删除

支持范围删除

代码语言:javascript
复制
await person.destroy({
  where: {
    id: 11111
  },
});

软删除

数据很重要,一般我们执行删除,不会直接把数据从数据库中抹掉,而是设置删除标志位,我们一般是用 delFlag 这个字段作为是否删除的依据

sequelize 可以帮我们完成软删除

不需要我们设立标志位,也不需要我们在查询的时候过滤掉删除的数据

代码语言:javascript
复制
function PersonModel(sequelize, DataTypes) {
  return sequelize.define(
    'person',
    {
      id: {  type: DataTypes.STRING(1024),},
    },
    {
      sequelize,
      tableName: 'person', // 表名称
      paranoid: true,
    },
  );
}

但是我们需要在表中加上 deleteAt 这个字段,当我们删除的时候,sequelize 便会帮我们记录删除的时间

更多请了解

https://www.sequelize.com.cn/core-concepts/paranoid

关联表删除

我们希望有关联的表,在我们对一方进行删除的时候,另一方也会自动跟着删除,不需要我们删除两遍

1、使用 sequelize 的方式

因为 sequelize 不支持关联表删除,如果想完成这个操作,只能通过钩子函数的方式

钩子需要在model 中定义

代码语言:javascript
复制
function PersonModel(sequelize, DataTypes) {
  return sequelize.define(
    'person',
    {
      id: { type: DataTypes.STRING(1024) },
    },
    {
      sequelize,
      tableName: 'person', // 表名称
      paranoid: true,

      // beforeDestroy 和 afterDestroy hook 只会在具有 onDelete: 'CASCADE' 和 hooks: true 的关联上被调用
      afterDestroy: async function (instance, options) {
        return instance.getComment().then((c) => {
          return Promise.all(c.map((i) => i.destroy()));
        });
      },
    },
  );
}

然后在进行model 关联的配置 onDelete = CASCADE,如下

代码语言:javascript
复制
person.hasMany(person_comment, {
  foreignKey: 'user_id',
  as: 'comment',
  onDelete: 'CASCADE',
  hooks: true,
});

然后只需要通过如下的 person 操作 , 该 person 下 的comment 也会被一并删除

代码语言:javascript
复制
await person.destroy({
  where: {
    id: 10,
  },
});

其中 onDelete 的值和 sql 一样的,释义如下

1. CASCADE: 从父表中删除或更新对应的行,同时自动删除或更新子表中匹配的行。ON DELETE CANSCADE和ON UPDATE CANSCADE都被InnoDB所支持。

2. SET NULL: 从父表中删除或更新对应的行,同时将子表中的外键列设为空。注意,这些在外键列没有被设为NOT NULL时才有效。ON DELETE SET NULL和ON UPDATE SET SET NULL都被InnoDB所支持。

3. NO ACTION: InnoDB拒绝删除或者更新父表。

4. RESTRICT: 拒绝删除或者更新父表。指定RESTRICT(或者NO ACTION)和忽略ON DELETE或者ON UPDATE选项的效果是一样的。

5. SET DEFAULT: InnoDB目前不支持。

2、数据库自带外键约束

只要在数据库表中定义了两表关联的外键,那么当删除父表数据时,子表关联的数据也会被自动删除。这个操作不需要经过 sequelize,完全从 数据库层面配置

下面就是添加外键的 sql 语句,给 comment 加上外键 user_id ,关联 person 表的id

代码语言:javascript
复制
alter table `comment` add CONSTRAINT `useid_ref` 
FOREIGN KEY (`user_id`) 
REFERENCES `person` (`id`) 
ON DELETE NO ACTION ON UPDATE NO ACTION

当 person 被删除的时候,它关联的comment 也会被自动删除

这也是比较推荐的方式

7 事务

数据库中我觉得是比较重要的一个功能了,凡是涉及到多个sql 操作的都必须开启事务

数据库事务是各种数据项进行各种操作时,这些操作要么都执行,要么都不执行。

比如说一起执行4个数据库操作,其中三个成功了,一个失败了,那么那三个就会回退成未操作的情况

比如下面这样,只要有一个 create 或者 destory 失败了,那么本次事务就全部失败,没有发生任何数据变动

代码语言:javascript
复制
const updateVideo = (transaction) => {
  const promiseArr = [];
  if (notExit) {
    // 每个操作都要传入 本次事务的 transaction
    promiseArr.push(video.create({ name: 1 }, { transaction }));
  } else {
    promiseArr.push(video.destory({ name: 1 }, { transaction }));
  }
  return Promise.all(promiseArr);
};

await sequelize.transaction((t) => {
  return Promise.all([updateVideo(t)]);
});

看下开启一个事务失败后的日志是怎么样的

代码语言:javascript
复制
Executing (fe92f7fa-be8e-419b-a848-7b31e54d957b): START TRANSACTION;
Executing (fe92f7fa-be8e-419b-a848-7b31e54d957b): INSERT INTO `person` (`id`,`name`) VALUES (NULL,'new--2');
Executing (fe92f7fa-be8e-419b-a848-7b31e54d957b): INSERT INTO `person` (`id`,`name`) VALUES (NULL,'new—2');
// 因为我设置了name 为唯一索引,所以当我插入第二条同名数据时,就会报错,然后就开始回退,连同前一条数据被回滚
Executing (fe92f7fa-be8e-419b-a848-7b31e54d957b): ROLLBACK;

最后

鉴于本人能力有限,难免会有疏漏错误的地方,请大家多多包涵, 如果有任何描述不当的地方,欢迎后台联系本人

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 神仙朱 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档