Sequelize

官网文档地址open in new window

Datatypes 文档open in new window

概念

模型

模型是 Sequelize 的本质. 模型是代表数据库中表的抽象。

模型也是 ES6 类. 类的实例表示该模型中的一个对象(该对象映射到数据库中表的一行)

几乎每个 Sequelize 方法都是异步的。

模型查询

使用嵌套数组来重命名属性:

Model.findAll({
  attributes: ['foo', ['bar', 'baz'], 'qux'],
})
1
2
3

等同于:

SELECT foo, bar AS baz, qux FROM ...
1

使用 sequelize.fn 进行聚合:

Model.findAll({
  attributes: [
    'foo',
    [sequelize.fn('COUNT', sequelize.col('hats')), 'n_hats'],
    'bar'
  ]
});
1
2
3
4
5
6
7

等同于:

SELECT foo, COUNT(hats) AS n_hats, bar FROM ...
1

使用聚合函数时,必须为它提供一个别名,以便能够从模型中访问它。

也可以在attributes中搭配includeexclude使用。

数据类型

列参数

WHERE 子句

使用

ORM 工具,映射到数据库,像操作对象一样去操作数据库。团队中一般不会直接写 sql 去操作数据库,而是使用 ORM 工具。

  • 建模(外键) & 同步到数据库
  • 增删改查 & 连表查询
  • 数据表,用 JS 中的模型(class)代替
  • 一条或多条记录,用 JS 中一个对象或数组代替
  • sql 语句,用对象方法代替

安装使用

安装 sequelize:

yarn add sequelize
1

安装 mysql:

yarn add mysql2
1

连接数据库和建模

const Sequelize = require('sequelize')

const seq = new Sequelize('case2-microblog', 'root', '123456', {
  host: 'localhost',
  dialect: 'mysql',
})

seq
  .sync({ force: true })
  .then(() => {
    process.exit()
  })
  .catch((error) => {
    console.log('sync', error)
  })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

建模:

// 创建 User 模型,创建后的表名是users
const User = seq.define('user', {
  // id 会自动创建,并设为主键、自增
  userName: {
    type: Sequelize.STRING, // varchar(255)
    allowNull: false, // 不为空
    unique: true, // 唯一性
  },
  password: {
    type: Sequelize.STRING,
    allowNull: false,
  },
  nickName: {
    type: Sequelize.STRING,
    comment: '昵称', // 注释
  },
  // 会自动创建`createdAt`和`updatedAt`
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

外键关联:

Blog.belongsTo(User, {
  // 创建外键 Blog.userId -> User.id
  foreignKey: 'userId',
})

// 另一种写法
User.hasMany(Blog, {
  // 创建外键 Blog.userId -> User.id
  foreignKey: 'userId',
})
1
2
3
4
5
6
7
8
9
10

创建记录

使用create创建一条记录:

const userInfo = await User.create({
  userName: 'licong',
  password: '123456',
  nickName: '阿聪',
})
console.log(userInfo.dataValues)
1
2
3
4
5
6

使用bulkCreate创建多条记录:

const userInfo = await User.bulkCreate(
  [
    {
      userName: 'licong',
      password: '123456',
      nickName: '阿聪',
    },
    {
      userName: 'zhangsan',
      password: '123456',
      nickName: '张三',
    },
  ],
  { returning: true }
)
// returning: true 返回结果信息
console.log(userInfo.dataValues)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

查询记录

  • findOne
  • findAll
  • findAndCountAll

查询一条记录使用findOne

const licong = await User.findOne({
  // attributes: ['userName', 'nickName'],	// 查询特定的列
  where: {
    userName: 'licong',
  },
})
console.log(licong.dataValues)
1
2
3
4
5
6
7

查询多条记录使用findAll

const blogs = await Blog.findAll({
  where: {
    userId: 1,
  },
  limit: 2, // 限制本次查询 2 条
  offset: 1, // 跳过 1 条
  order: [
    ['id', 'desc'], // 排序
  ],
})
console.log(blogs.map((blog) => blog.dataValues))
1
2
3
4
5
6
7
8
9
10
11

查询总数使用findAndCountAll

const blogListAndCount = await Blog.findAndCountAll({
  where: {
    userId: 1,
  },
  limit: 2,
  offset: 1,
  order: [
    ['id', 'desc'], // 排序
  ],
})
console.log(blogListAndCount.count) // 返回所有总条数
console.log(blogListAndCount.rows.map((blog) => blog.dataValues))
1
2
3
4
5
6
7
8
9
10
11
12

setDataValue 设置值

instance.setDataValue(key, value)
1

getDataValue 获取值

instance.getDataValue(key)
1

连表查询

查 Blog 表包含上 User 表:

const blogListWithUser = await Blog.findAndCountAll({
  order: [['id', 'desc']],
  include: [
    {
      model: User,
      attributes: ['userName'],
      where: {
        userName: 'licong',
      },
    },
  ],
})
console.log(blogListWithUser.count)
console.log(
  blogListWithUser.rows.map((blog) => {
    const blogVal = blog.dataValues
    blogVal.user = blogVal.user.dataValues
    return blogVal
  })
)

// 要使用这种方式外键关联
Blog.belongsTo(User, {
  // 创建外键 Blog.userId -> User.id
  foreignKey: 'userId',
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

查 User 表包含上 Blog 表:

const userListWithBlog = await User.findAndCountAll({
  attributes: ['userName', 'nickName'],
  include: [
    {
      model: Blog,
    },
  ],
})
console.log(
  userListWithBlog.rows.map((user) => {
    const userVal = user.dataValues
    // 每个用户有多条数据
    userVal.blogs = JSON.stringify(userVal.blogs.map((blog) => blog.dataValues))
    return userVal
  })
)

// 要使用这种方式外键关联
User.hasMany(Blog, {
  // 创建外键 Blog.userId -> User.id
  foreignKey: 'userId',
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

模型(表)之间的关联关系

数据库中的表之间存在一定的关联关系,表之间的关系基于主/外键进行关联、创建约束等。关系表中的数据分为1对1(1:1)1对多(1:M)多对多(N:M)三种关联关系。

  • hasOne: 添加外键到目标模,与目标模型建立1:1关联关系,关联关系(外键)存在于目标模型中。
  • belongsTo: 为当前模型添加外键,与目标模型建立1:1关联关系,关联关系(外键)存在于源模型中。
  • hasMany: 添加外键到目标模型,与目标模型建立1:N关联关系,关联关系(外键)存在于目标模型中。
  • belongsToMany: 与目标模型建立N:M关联关系,会创建交叉表。

修改记录

修改一条记录使用update

const updateRes = await Blog.update(
  {
    title: '修改标题',
  },
  {
    where: {
      id: 1,
    },
  }
)
console.log(updateRes[0] > 0)
1
2
3
4
5
6
7
8
9
10
11

删除记录

删除一条记录使用destroy

const destroyRes = await Blog.destroy({
  where: {
    id: 1,
  },
})
console.log(destroyRes > 0)
1
2
3
4
5
6

关联

关联文档open in new window

Sequelize 提供了 四种 关联类型,并将它们组合起来以创建关联:

  • HasOne 关联类型
  • BelongsTo 关联类型
  • HasMany 关联类型
  • BelongsToMany 关联类型

关联的定义顺序是有关系的. 换句话说,对于这四种情况,定义顺序很重要. 在上述所有示例中,A 称为 源 模型,而 B 称为 目标 模型. 此术语很重要.

将 fooId 列添加到 Bar 中

Foo.hasOne(Bar)
Bar.belongsTo(Foo)
1
2

等同于:

CREATE TABLE IF NOT EXISTS "foos" (
  /* ... */
);
CREATE TABLE IF NOT EXISTS "bars" (
  /* ... */
  "fooId" INTEGER REFERENCES "foos" ("id") ON DELETE SET NULL ON UPDATE CASCADE
  /* ... */
);
1
2
3
4
5
6
7
8

自定义外键

// 方法 1
Foo.hasOne(Bar, {
  foreignKey: 'myFooId',
})
Bar.belongsTo(Foo)

// 方法 2
Foo.hasOne(Bar, {
  foreignKey: {
    name: 'myFooId',
  },
})
Bar.belongsTo(Foo)
1
2
3
4
5
6
7
8
9
10
11
12
13

其它用法

attributes

返回指定字段:

{
  attributes: ['id', 'name', 'code']
}
1
2
3

排除指定字段:

{
  attributes: {
    exclude: ['createdAt', 'updatedAt', 'deletedAt']
  }
}
1
2
3
4
5

有这两种用法就不需要再改写全局Model.prototype.toJSON,但是两者不可同时使用。

Last Updated: 2023/3/13 09:53:21
Contributors: licong96