sequelize学习笔记二(关联)

Sequelize关联(就是联表操作)存在三种关系:

  • 一对一
  • 一对多
  • 多对多
    同时提供了四种关联类型,我们可以将其组合创建关联:
  • HasOne
  • BelongsTo
  • HasMany
  • BelongsToMany
A.hasOne(B, { /* 参数 */ }); // A 有一个 B
A.belongsTo(B, { /* 参数 */ }); // A 属于B
A.hasMany(B, { /* 参数 */ }); // A 有多个 B
A.belongsToMany(B, { through: 'C', /* 参数 */ }); // A 属于多个 B , 通过联结表 C

解析: 具体怎么用后面解析,先弄清一个概念:A(前者)称为源模型,B(后者)称为目标模型

我们先定义一个简单的例子,后续的操作都是基于该例子:
我们分别定义了一个船长(Captain)和一艘船(Ship)的model。

const sequelize = require('./db').OAWebSite
const { DataTypes } = require('sequelize');

// 这是我们用于以下示例的模型的设置
const Ship = sequelize.define('ship', {
  name: { type: DataTypes.STRING, unique: true },
  crewCapacity: DataTypes.INTEGER,
  amountOfSails: DataTypes.INTEGER
}, { timestamps: false });
const Captain = sequelize.define('captain', {
  name: { type: DataTypes.STRING, unique: true },
  skillLevel: {
    type: DataTypes.INTEGER,
    validate: { min: 1, max: 10 }
  }
}, { timestamps: false });

一对一

hasOnebelongsTo联用:
例如:我们定义一艘船只有一个船长,一个船长只开一艘船。这里我们需要定义外键的位置:我们需要考虑的是是否能为空,例如我们这将外键设在Ship上。(船可以在没有船长的情况下存在)

Captain.hasOne(Ship);
Ship.belongsTo(Captain);
自定义外键

这时我们再调用Ship.sync()就可以发现Ship表里面多了一个外键(captainId)。当然我们可以通过指定外键的名字:

Captain.hasOne(Ship, {
   foreignKey: 'myShip'
});
// =>
Captain.hasOne(Ship, {
   foreignKey: {
    name: 'myShip',
    as: 'gg'  // 别名
  }
});

还有很多方法指定外键的名字,甚至指定其type,allowNull,defaultValue等就不一一举例。如果需要强关联型,例如船长必须有船,则可以指定其外键不允许为null即可。

Captain.hasOne(Ship, {
   foreignKey: {
    name: 'myShip',
    allowNull: false
  }
});

一对多

一对多关联将一个源与多个目标连接,而所有这些目标仅与此单个源连接.
简单来说就是一个船长他可以开多艘船(just as wang校长有多辆车一个道理)。用法和配置和一对一几乎一样。

Captain.hasMany(Ship);
Ship.belongsTo(Captain);

多对多

多对多关联将一个源与多个目标相连,而所有这些目标又可以与第一个目标之外的其他源相连.
简单来说就像共享汽车吧,一个人可以开过多辆车,一辆车被多个人开过。

Captain.belongsToMany(Ship, { through: 'ShipCaptains' });
Ship.belongsToMany(Captain, { through: 'ShipCaptains' });

解析,这里将生产一个新表ShipCaptains并且外键默认为(‘shipId’, ‘captainId’)

特殊方法

查询实例
这里可以通过include来查找关联的数据表信息,默认查询是不包括联表信息

Captain.hasOne(Ship);
Ship.belongsTo(Captain);

// 查询所有联表信息
console.log(await Captain.findAll({ include: Ship }).toJSON())
// 获取其对应关系表信息
const awesomeCaptain = await Captain.findOne({
  where: {
    name: "Jack Sparrow"
  }
});
awesomeCaptain.getShip()

解析:上面的getShip()方法是联表后,sequelize会自动根据联表对象的实例名天津一些方法到Captain对象上。如果定义了别名,则需要根据别名来获取。

...
Ship.belongsTo(Captain, { as: 'leader' });
...
const ship = Ship.findOne();
console.log((await ship.getLeader()).toJSON());
预加载和延迟加载

预先加载: 是在findAll的时候直接include关联对象,这时候一开始就获取了所有对象和其关联的对象。
**延迟加载:**是先不获取其关联对象,后续再使用类似getShip的一些内部方法获取到其关联的随性。

Foo.hasOne(Bar) & Foo.belongsTo(Bar)
  • foo.getBar() 获取关联对象信息
  • foo.setBar() 设置关联对象信息
  • foo.createBar() 创建关联对象信息
    用法理解起来很简单
const foo = await Foo.create({ name: 'the-foo' });
const bar1 = await Bar.create({ name: 'some-bar' });
const bar2 = await Bar.create({ name: 'another-bar' });
console.log(await foo.getBar()); // null
await foo.setBar(bar1);
console.log((await foo.getBar()).name); // 'some-bar'
await foo.createBar({ name: 'yet-another-bar' });
const newlyAssociatedBar = await foo.getBar();
console.log(newlyAssociatedBar.name); // 'yet-another-bar'
await foo.setBar(null); // Un-associate
console.log(await foo.getBar()); // null
Foo.hasMany(Bar) & Foo.belongsToMany(Bar, { through: Baz })

fooInstance.getBars() 获取关联对象信息
fooInstance.countBars() 获取关联对象数
fooInstance.hasBar() 是否还有该关联对象
fooInstance.hasBars() 是否含有对象数组[]
fooInstance.setBars() 设置多个关联对象的对象
fooInstance.addBar() 添加关联对象
fooInstance.addBars() 添加多个关联对象
fooInstance.removeBar() 移除关联对象
fooInstance.removeBars() 移除多个关联对象
fooInstance.createBar() 创建关联对象
具体用法和上面基本一致

const foo = await Foo.create({ name: 'the-foo' });
const bar1 = await Bar.create({ name: 'some-bar' });
const bar2 = await Bar.create({ name: 'another-bar' });
console.log(await foo.getBars()); // []
console.log(await foo.countBars()); // 0
console.log(await foo.hasBar(bar1)); // false
await foo.addBars([bar1, bar2]);
console.log(await foo.countBars()); // 2
await foo.addBar(bar1);
console.log(await foo.countBars()); // 2
console.log(await foo.hasBar(bar1)); // true
await foo.removeBar(bar2);
console.log(await foo.countBars()); // 1
await foo.createBar({ name: 'yet-another-bar' });
console.log(await foo.countBars()); // 2
await foo.setBars([]); // 取消关联所有先前关联的 Bars
console.log(await foo.countBars()); // 0

为什么关联是成对定义的?

当在两个模型之间定义了 Sequelize 关联时,只有 源 模型 知晓关系. 因此,例如,当使用 Foo.hasOne(Bar)(当前,Foo 是源模型,而 Bar 是目标模型)时,只有 Foo 知道该关联的存在. 这就是为什么在这种情况下,如上所示,Foo 实例获得方法 getBar(), setBar() 和 createBar() 而另一方面,Bar 实例却没有获得任何方法.

指定关联主键

默认情况下都是以关联对象表的id主键作为外键,我们可以定义sourceKey(源模型key)或targetKey(目标模型key)。具体看外键设在哪,例如A.hasOne(B)和A.hasMany(B) 外键都是设置在B(目标模型)上,所以我们要指定的是源模型key.

A.hasOne(B, { sourceKey: 'name', foreignKey: 'fooName' });
A.hasMany(B, { sourceKey: 'title', foreignKey: 'barTitle' });

如果是belongsTo关系的话,则需要设置在目标模型上,

A.belongsTo(B, { targetKey: 'name', foreignKey: 'captainName' });

这里再次强调,无论哪种都是前者是源模型,后者是目标模型

但如果是belongsToMany则其和前面的不同,前面只有一个外键,这里可以配置两个或只配一个或都不配。不配置的默认以关联对象表的id主键作为外键。

A.belongsToMany(B, { through: 'foo_bar', sourceKey: 'name', targetKey: 'title' });
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值