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

2022-04-12 13:53:29 浏览数 (1)

本文分享 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#操作符

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

代码语言: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#多对多关系

首先定义 表的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#时间戳

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;

最后

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

0 人点赞