数据库中表经常会跟其他数据表有关联,数据操作时需要连同关联的表一起操作。如:一个博客文章会有分类、标签、评论,以及属于哪个用户。
ThinkJS 中支持关联模型,让处理这类操作非常简单。
关联模型中支持常见的 4 类关联关系。如:
think.model.HAS_ONE
一对一模型think.model.BELONG_TO
一对一属于think.model.HAS_MANY
一对多think.model.MANY_TO_MANY
多对多可以通过命令 thinkjs model [name] --relation
来创建关联模型。如:
thinkjs model home/post --relation
会创建模型文件 src/home/model/post.js
。
可以通过 relation
属性来指定关联关系。如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
//通过 relation 属性指定关联关系,可以指定多个关联关系
this.relation = {
cate: {},
comment: {}
};
}
}
也可以直接使用 ES7 里的语法直接定义 relation
属性。如:
export default class extends think.model.relation {
//直接定义 relation 属性
relation = {
cate: {},
comment: {}
};
init(...args){
super.init(...args);
}
}
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
cate: {
type: think.model.MANY_TO_MANY, //relation type
model: '', //model name
name: 'profile', //data name
key: 'id',
fKey: 'user_id', //forign key
field: 'id,name',
where: 'name=xx',
order: '',
limit: '',
rModel: '',
rfKey: ''
},
};
}
}
各个字段含义如下:
type
关联关系类型model
关联表的模型名,默认为配置的 key,这里为 cate
name
对应的数据字段名,默认为配置的 key,这里为 cate
key
当前模型的关联 keyfKey
关联表与只对应的 keyfield
关联表查询时设置的 field,如果需要设置,必须包含 fKey
对应的值where
关联表查询时设置的 where 条件order
关联表查询时设置的 orderlimit
关联表查询时设置的 limitpage
关联表查询时设置的 pagerModel
多对多下,对应的关联关系模型名rfKey
多对多下,对应里的关系关系表对应的 keyrelation
配置深层级的关联关系显示,比如关闭深层级的关联查询如果只用设置关联类型,不用设置其他字段信息,可以通过下面简单的方式:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
cate: think.model.MANY_TO_MANY
};
}
}
一对一关联,表示当前表含有一个附属表。
假设当前表的模型名为 user
,关联表的模型名为 info
,那么配置中字段 key
的默认值为 id
,字段 fKey
的默认值为 user_id
。
export default class extends think.model.relation {
init(..args){
super.init(...args);
this.relation = {
info: think.model.HAS_ONE
};
}
}
执行查询操作时,可以得到类似如下的数据:
[
{
id: 1,
name: '111',
info: { //关联表里的数据信息
user_id: 1,
desc: 'info'
}
}, ...]
一对一关联,属于某个关联表,和 HAS_ONE 是相反的关系。
假设当前模型名为 info
,关联表的模型名为 user
,那么配置字段 key
的默认值为 user_id
,配置字段 fKey
的默认值为 id
。
export default class extends think.model.relation {
init(..args){
super.init(...args);
this.relation = {
user: think.model.BELONG_TO
};
}
}
执行查询操作时,可以得到类似下面的数据:
[
{
id: 1,
user_id: 1,
desc: 'info',
user: {
name: 'thinkjs'
}
}, ...
]
一对多的关系。
假如当前模型名为 post
,关联表的模型名为 comment
,那么配置字段 key
默认值为 id
,配置字段 fKey
默认值为 post_id
。
'use strict';
/**
* relation model
*/
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: {
type: think.model.HAS_MANY
}
};
}
}
执行查询数据时,可以得到类似下面的数据:
[{
id: 1,
title: 'first post',
content: 'content',
comment: [{
id: 1,
post_id: 1,
name: 'welefen',
content: 'first comment'
}, ...]
}, ...]
如果关联表的数据需要分页查询,可以通过 page
参数进行,如:
'use strict';
/**
* relation model
*/
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: {
type: think.model.HAS_MANY
}
};
}
getList(page){
return this.setRelation('comment', {page: page}).select();
}
}
除了用 setRelation
来合并参数外,可以将参数设置为函数,合并参数时会自动执行该函数。
多对多关系。
假设当前模型名为 post
,关联模型名为 cate
,那么需要一个对应的关联关系表。配置字段 rModel
默认值为 post_cate
,配置字段 rfKey
默认值为 cate_id
。
'use strict';
/**
* relation model
*/
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
cate: {
type: think.model.MANY_TO_MANY,
rModel: 'post_cate',
rfKey: 'cate_id'
}
};
}
}
查询出来的数据结构为:
[{
id: 1,
title: 'first post',
cate: [{
id: 1,
name: 'cate1',
post_id: 1
}, ...]
}, ...]
如果 2 个关联表,一个设置对方为 HAS_ONE,另一个设置对方为 BELONG_TO,这样在查询关联表的数据时会将当前表又查询了一遍,并且会再次查询关联表,最终导致死循环。
可以在配置里设置 relation
字段关闭关联表的关联查询功能,从而避免死循环。如:
export default class extends think.model.relation {
init(..args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
relation: false //关联表 user 查询时关闭关联查询
}
};
}
}
也可以设置只关闭当前模型的关联关系,如:
export default class extends think.model.relation {
init(..args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
relation: 'info' //关联表 user 查询时关闭对 info 模型的关联关系
}
};
}
}
设置关联关系后,查询等操作都会自动查询关联表的数据。如果某些情况下不需要查询关联表的数据,可以通过 setRelation
方法临时关闭关联关系查询。
通过 setRelation(false)
关闭所有的关联关系查询。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: think.model.HAS_MANY,
cate: think.model.MANY_TO_MANY
};
}
getList(){
return this.setRelation(false).select();
}
}
通过 setRelation('comment')
只查询 comment
的关联数据,不查询其他的关联关系数据。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: think.model.HAS_MANY,
cate: think.model.MANY_TO_MANY
};
}
getList2(){
return this.setRelation('comment').select();
}
}
通过 setRelation('comment', false)
关闭 comment
的关联关系数据查询。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: think.model.HAS_MANY,
cate: think.model.MANY_TO_MANY
};
}
getList2(){
return this.setRelation('comment', false).select();
}
}
通过 setRelation(true)
重新启用所有的关联关系数据查询。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: think.model.HAS_MANY,
cate: think.model.MANY_TO_MANY
};
}
getList2(){
return this.setRelation(true).select();
}
}
有时候有多级关联关系,比如:a 关联了 b,b 又关联了 c,这时候查询会把 a、b、c 的数据都查询出来了。在有些情况下,我们不需要 c 的数据,不想在查询 c 了,这时候可以配置 relation 来解决。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: {
type: think.model.HAS_MANY,
relation: false //关闭更深层级的关联查询
}
};
}
}
除了完全关闭外,也可以设置部分关闭。如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
comment: {
type: think.model.HAS_MANY,
relation: 'xxx' //关闭名为 xxx 的关联查询,保留其他的关联查询
}
};
}
}
设置 field 可以控制查询关联表时数据字段,这样可以减少查询的数据量,提高查询查询效率。默认情况会查询所有数据。
如果设置了查询的字段,那么必须包含关联字段,否则查询出来的数据无法和之前的数据关联。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
field: 'id,name,email' //必须要包含关联字段 id
}
};
}
}
如果某些情况下必须动态的设置的话,可以将 field 设置为一个函数,执行函数时返回对应的字段。如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
field: model => {
return model._relationField;
}
}
};
}
selectData(relationfield){
//将要查询的关联字段设置到一个私有属性中,便于动态设置 field 里获取
this._relationField = relationfield;
return this.select();
}
}
形参 model
指向当前模型类。
设置 where 可以控制查询关联表时的查询条件,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
where: {
grade: 1 //只查询关联表里 grade = 1 的数据
}
}
};
}
}
也可以动态的设置 where 条件,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
where: model => {
return model._relationWhere;
}
}
};
}
selectData(relationWhere){
this._relationWhere = relationWhere;
return this.select();
}
}
形参 model
指向当前模型类。
可以通过设置 page 进行分页查询,page 参数会被解析为 limit 数据。
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
page: [1, 15] //第一页,每页 15 条
}
};
}
}
也可以动态设置分页,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
page: model => {
return model._relationPage;
}
}
};
}
selectData(page){
this._relationPage = [page, 15];
return this.select();
}
}
形参 model
指向当前模型类。
可以通过 limit 设置查询的条数,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
limit: [10] //限制 10 条
}
};
}
}
也可以动态设置 limit,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
limit: model => {
return model._relationLimit;
}
}
};
}
selectData(){
this._relationLimit = [1, 15];
return this.select();
}
}
形参 model
指向当前模型类。
注: 如果设置 page
,那么 limit
会被忽略,因为 page
会转为 limit
。
通过 order 可以设置关联表的查询排序方式,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
order: 'level DESC'
}
};
}
}
也可以动态的设置 order,如:
export default class extends think.model.relation {
init(...args){
super.init(...args);
this.relation = {
user: {
type: think.model.BELONG_TO,
order: model => {
return model._relationOrder;
}
}
};
}
selectData(){
this._relationOrder= 'level DESC';
return this.select();
}
}
形参 model
指向当前模型类。
注
:动态配置值从 2.2.3
版本开始支持。
比如:数据表里的字段 id
的类型为 int
,那么关联表里的关联字段 user_id
也必须为 int
相关的类型,否则无法匹配数据。这是因为匹配的时候使用绝对等于进行判断的。
该关联模型的操作不适合 mongo 模型,mongo 的关联模型请见 https://docs.mongodb.org/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/。