关联模型

数据库中表经常会跟其他数据表有关联,数据操作时需要连同关联的表一起操作。如:一个博客文章会有分类、标签、评论,以及属于哪个用户。

ThinkJS 中支持关联模型,让处理这类操作非常简单。

支持的类型

关联模型中支持常见的 4 类关联关系。如:

  • think.model.HAS_ONE 一对一模型
  • think.model.BELONG_TO 一对一属于
  • think.model.HAS_MANY 一对多
  • think.model.MANY_TO_MANY 多对多

    创建关联模型

可以通过命令 thinkjs model [name] —relation 来创建关联模型。如:

  1. thinkjs model home/post --relation

会创建模型文件 src/home/model/post.js

指定关联关系

可以通过 relation 属性来指定关联关系。如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. //通过 relation 属性指定关联关系,可以指定多个关联关系
  5. this.relation = {
  6. cate: {},
  7. comment: {}
  8. };
  9. }
  10. }

也可以直接使用 ES7 里的语法直接定义 relation 属性。如:

  1. export default class extends think.model.relation {
  2. //直接定义 relation 属性
  3. relation = {
  4. cate: {},
  5. comment: {}
  6. };
  7. init(...args){
  8. super.init(...args);
  9. }
  10. }

单个关系模型的数据格式

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. cate: {
  6. type: think.model.MANY_TO_MANY, //relation type
  7. model: '', //model name
  8. name: 'profile', //data name
  9. key: 'id',
  10. fKey: 'user_id', //forign key
  11. field: 'id,name',
  12. where: 'name=xx',
  13. order: '',
  14. limit: '',
  15. rModel: '',
  16. rfKey: ''
  17. },
  18. };
  19. }
  20. }

各个字段含义如下:

  • type 关联关系类型
  • model 关联表的模型名,默认为配置的 key,这里为 cate
  • name 对应的数据字段名,默认为配置的 key,这里为 cate
  • key 当前模型的关联 key
  • fKey 关联表与只对应的 key
  • field 关联表查询时设置的 field,如果需要设置,必须包含 fKey 对应的值
  • where 关联表查询时设置的 where 条件
  • order 关联表查询时设置的 order
  • limit 关联表查询时设置的 limit
  • page 关联表查询时设置的 page
  • rModel 多对多下,对应的关联关系模型名
  • rfKey 多对多下,对应里的关系关系表对应的 key
  • relation 配置深层级的关联关系显示,比如关闭深层级的关联查询
    如果只用设置关联类型,不用设置其他字段信息,可以通过下面简单的方式:
  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. cate: think.model.MANY_TO_MANY
  6. };
  7. }
  8. }

HAS_ONE

一对一关联,表示当前表含有一个附属表。

假设当前表的模型名为 user,关联表的模型名为 info,那么配置中字段 key 的默认值为 id,字段 fKey 的默认值为 user_id

  1. export default class extends think.model.relation {
  2. init(..args){
  3. super.init(...args);
  4. this.relation = {
  5. info: think.model.HAS_ONE
  6. };
  7. }
  8. }

执行查询操作时,可以得到类似如下的数据:

  1. [
  2. {
  3. id: 1,
  4. name: '111',
  5. info: { //关联表里的数据信息
  6. user_id: 1,
  7. desc: 'info'
  8. }
  9. }, ...]

BELONG_TO

一对一关联,属于某个关联表,和 HAS_ONE 是相反的关系。

假设当前模型名为 info,关联表的模型名为 user,那么配置字段 key 的默认值为 user_id,配置字段 fKey 的默认值为 id

  1. export default class extends think.model.relation {
  2. init(..args){
  3. super.init(...args);
  4. this.relation = {
  5. user: think.model.BELONG_TO
  6. };
  7. }
  8. }

执行查询操作时,可以得到类似下面的数据:

  1. [
  2. {
  3. id: 1,
  4. user_id: 1,
  5. desc: 'info',
  6. user: {
  7. name: 'thinkjs'
  8. }
  9. }, ...
  10. ]

HAS_MANY

一对多的关系。

假如当前模型名为 post,关联表的模型名为 comment,那么配置字段 key 默认值为 id,配置字段 fKey 默认值为 post_id

  1. 'use strict';
  2. /**
  3. * relation model
  4. */
  5. export default class extends think.model.relation {
  6. init(...args){
  7. super.init(...args);
  8. this.relation = {
  9. comment: {
  10. type: think.model.HAS_MANY
  11. }
  12. };
  13. }
  14. }

执行查询数据时,可以得到类似下面的数据:

  1. [{
  2. id: 1,
  3. title: 'first post',
  4. content: 'content',
  5. comment: [{
  6. id: 1,
  7. post_id: 1,
  8. name: 'welefen',
  9. content: 'first comment'
  10. }, ...]
  11. }, ...]

如果关联表的数据需要分页查询,可以通过 page 参数进行,如:

  1. 'use strict';
  2. /**
  3. * relation model
  4. */
  5. export default class extends think.model.relation {
  6. init(...args){
  7. super.init(...args);
  8. this.relation = {
  9. comment: {
  10. type: think.model.HAS_MANY
  11. }
  12. };
  13. }
  14. getList(page){
  15. return this.setRelation('comment', {page: page}).select();
  16. }
  17. }

除了用 setRelation 来合并参数外,可以将参数设置为函数,合并参数时会自动执行该函数。

MANY_TO_MANY

多对多关系。

假设当前模型名为 post,关联模型名为 cate,那么需要一个对应的关联关系表。配置字段 rModel 默认值为 post_cate,配置字段 rfKey 默认值为 cate_id

  1. 'use strict';
  2. /**
  3. * relation model
  4. */
  5. export default class extends think.model.relation {
  6. init(...args){
  7. super.init(...args);
  8. this.relation = {
  9. cate: {
  10. type: think.model.MANY_TO_MANY,
  11. rModel: 'post_cate',
  12. rfKey: 'cate_id'
  13. }
  14. };
  15. }
  16. }

查询出来的数据结构为:

  1. [{
  2. id: 1,
  3. title: 'first post',
  4. cate: [{
  5. id: 1,
  6. name: 'cate1',
  7. post_id: 1
  8. }, ...]
  9. }, ...]

关联死循环

如果 2 个关联表,一个设置对方为 HAS_ONE,另一个设置对方为 BELONG_TO,这样在查询关联表的数据时会将当前表又查询了一遍,并且会再次查询关联表,最终导致死循环。

可以在配置里设置 relation 字段关闭关联表的关联查询功能,从而避免死循环。如:

  1. export default class extends think.model.relation {
  2. init(..args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. relation: false //关联表 user 查询时关闭关联查询
  8. }
  9. };
  10. }
  11. }

也可以设置只关闭当前模型的关联关系,如:

  1. export default class extends think.model.relation {
  2. init(..args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. relation: 'info' //关联表 user 查询时关闭对 info 模型的关联关系
  8. }
  9. };
  10. }
  11. }

临时关闭关联关系

设置关联关系后,查询等操作都会自动查询关联表的数据。如果某些情况下不需要查询关联表的数据,可以通过 setRelation 方法临时关闭关联关系查询。

全部关闭

通过 setRelation(false) 关闭所有的关联关系查询。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. comment: think.model.HAS_MANY,
  6. cate: think.model.MANY_TO_MANY
  7. };
  8. }
  9. getList(){
  10. return this.setRelation(false).select();
  11. }
  12. }

部分启用

通过 setRelation('comment') 只查询 comment 的关联数据,不查询其他的关联关系数据。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. comment: think.model.HAS_MANY,
  6. cate: think.model.MANY_TO_MANY
  7. };
  8. }
  9. getList2(){
  10. return this.setRelation('comment').select();
  11. }
  12. }

部分关闭

通过 setRelation('comment', false) 关闭 comment 的关联关系数据查询。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. comment: think.model.HAS_MANY,
  6. cate: think.model.MANY_TO_MANY
  7. };
  8. }
  9. getList2(){
  10. return this.setRelation('comment', false).select();
  11. }
  12. }

重新全部启用

通过 setRelation(true) 重新启用所有的关联关系数据查询。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. comment: think.model.HAS_MANY,
  6. cate: think.model.MANY_TO_MANY
  7. };
  8. }
  9. getList2(){
  10. return this.setRelation(true).select();
  11. }
  12. }

关闭深层级的关联关系

有时候有多级关联关系,比如:a 关联了 b,b 又关联了 c,这时候查询会把 a、b、c 的数据都查询出来了。在有些情况下,我们不需要 c 的数据,不想在查询 c 了,这时候可以配置 relation 来解决。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. comment: {
  6. type: think.model.HAS_MANY,
  7. relation: false //关闭更深层级的关联查询
  8. }
  9. };
  10. }
  11. }

除了完全关闭外,也可以设置部分关闭。如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. comment: {
  6. type: think.model.HAS_MANY,
  7. relation: 'xxx' //关闭名为 xxx 的关联查询,保留其他的关联查询
  8. }
  9. };
  10. }
  11. }

设置查询条件

field

设置 field 可以控制查询关联表时数据字段,这样可以减少查询的数据量,提高查询查询效率。默认情况会查询所有数据。

如果设置了查询的字段,那么必须包含关联字段,否则查询出来的数据无法和之前的数据关联。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. field: 'id,name,email' //必须要包含关联字段 id
  8. }
  9. };
  10. }
  11. }

如果某些情况下必须动态的设置的话,可以将 field 设置为一个函数,执行函数时返回对应的字段。如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. field: model => {
  8. return model._relationField;
  9. }
  10. }
  11. };
  12. }
  13. selectData(relationfield){
  14. //将要查询的关联字段设置到一个私有属性中,便于动态设置 field 里获取
  15. this._relationField = relationfield;
  16. return this.select();
  17. }
  18. }

形参 model 指向当前模型类。

where

设置 where 可以控制查询关联表时的查询条件,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. where: {
  8. grade: 1 //只查询关联表里 grade = 1 的数据
  9. }
  10. }
  11. };
  12. }
  13. }

也可以动态的设置 where 条件,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. where: model => {
  8. return model._relationWhere;
  9. }
  10. }
  11. };
  12. }
  13. selectData(relationWhere){
  14. this._relationWhere = relationWhere;
  15. return this.select();
  16. }
  17. }

形参 model 指向当前模型类。

page

可以通过设置 page 进行分页查询,page 参数会被解析为 limit 数据。

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. page: [1, 15] //第一页,每页 15 条
  8. }
  9. };
  10. }
  11. }

也可以动态设置分页,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. page: model => {
  8. return model._relationPage;
  9. }
  10. }
  11. };
  12. }
  13. selectData(page){
  14. this._relationPage = [page, 15];
  15. return this.select();
  16. }
  17. }

形参 model 指向当前模型类。

limit

可以通过 limit 设置查询的条数,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. limit: [10] //限制 10 条
  8. }
  9. };
  10. }
  11. }

也可以动态设置 limit,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. limit: model => {
  8. return model._relationLimit;
  9. }
  10. }
  11. };
  12. }
  13. selectData(){
  14. this._relationLimit = [1, 15];
  15. return this.select();
  16. }
  17. }

形参 model 指向当前模型类。

注: 如果设置 page,那么 limit 会被忽略,因为 page 会转为 limit

order

通过 order 可以设置关联表的查询排序方式,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. order: 'level DESC'
  8. }
  9. };
  10. }
  11. }

也可以动态的设置 order,如:

  1. export default class extends think.model.relation {
  2. init(...args){
  3. super.init(...args);
  4. this.relation = {
  5. user: {
  6. type: think.model.BELONG_TO,
  7. order: model => {
  8. return model._relationOrder;
  9. }
  10. }
  11. };
  12. }
  13. selectData(){
  14. this._relationOrder= 'level DESC';
  15. return this.select();
  16. }
  17. }

形参 model 指向当前模型类。

:动态配置值从 2.2.3 版本开始支持。

注意事项

关联字段的数据类型要一致

比如:数据表里的字段 id 的类型为 int,那么关联表里的关联字段 user_id 也必须为 int 相关的类型,否则无法匹配数据。这是因为匹配的时候使用绝对等于进行判断的。

mongo 关联模型

该关联模型的操作不适合 mongo 模型,mongo 的关联模型请见 https://docs.mongodb.org/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/

原文: https://thinkjs.org/zh-cn/doc/2.2/model_relation.html