静态关系 
下面以模块test-vona为例,讲解静态关系的用法
4种关系 
Vona ORM 提供了 4 种关系:
| 名称 | 说明 | 
|---|---|
| hasOne | 1:1 | 
| belongsTo | 1:1/n:1 | 
| hasMany | 1:n。可以实现主表-明细表,以及主表-多级明细表的功能 | 
| belongsToMany | n:n | 
hasOne 
1. 定义关系 
import { ModelPostContent } from './postContent.ts';
@Model({
  entity: EntityPost,
  relations: {
    postContent: $relation.hasOne(ModelPostContent, 'postId', { columns: ['id', 'content'] }),
  },
})
class ModelPost {}| 名称 | 说明 | 
|---|---|
| relations.postContent | 关系名 | 
| $relation.hasOne | 定义1:1关系 | 
| ModelPostContent | 目标Model | 
| 'postId' | 外键 | 
| columns | 要查询的字段列表 | 
WARNING
relations节点有任何变更,都需要执行菜单:Vona Tools: Generate .metadata,从而同步生成对应的类型定义
2. 使用关系 
在 Model 中定义的 hasOne 关系可以用于所有 CRUD 操作。通过include指定需要操作的关系,比如postContent: true,那么,系统在操作 Model Post 的同时,也会操作 Model PostContent
class ServicePost {
  async relationHasOne() {
    // insert
    const postCreate = await this.scope.model.post.insert(
      {
        title: 'Post001',
        postContent: {
          content: 'This is a post',
        },
      },
      {
        include: {
          postContent: true,
        },
      },
    );
    // get
    const post = await this.scope.model.post.get(
      {
        id: postCreate.id,
      },
      {
        include: {
          postContent: true,
        },
      },
    );
    // update
    await this.scope.model.post.update(
      {
        id: postCreate.id,
        title: 'Post001-Update',
        postContent: {
          content: 'This is a post-changed',
        },
      },
      {
        include: {
          postContent: true,
        },
      },
    );
    // delete
    await this.scope.model.post.delete(
      {
        id: postCreate.id,
      },
      {
        include: {
          postContent: true,
        },
      },
    );
  }
}belongsTo 
1. 定义关系 
@Model({
  entity: EntityPostContent,
  relations: {
    post: $relation.belongsTo(() => ModelPostContent, () => ModelPost, 'postId', { columns: '*' }),
  },
})
class ModelPostContent {}| 名称 | 说明 | 
|---|---|
| relations.post | 关系名 | 
| $relation.belongsTo | 定义1:1/n:1关系 | 
| ModelPostContent | 源Model | 
| ModelPost | 目标Model | 
| 'postId' | 外键 | 
| columns | 要查询的字段列表 | 
2. 使用关系 
在 Model 中定义的 belongsTo 关系只用于查询操作。通过include指定需要查询的关系,比如post: true,那么,系统在查询 Model PostContent 的同时,也会查询 Model Post
class ServicePost {
  async relationBelongsTo() {
    const postContent = await this.scope.model.postContent.select({
      include: {
        post: true,
      },
    });
    console.log(postContent[0]?.post?.title);
  }
}hasMany 
1. 定义关系 
import { ModelProduct } from './product.ts';
@Model({
  entity: EntityOrder,
  relations: {
    products: $relation.hasMany(() => ModelProduct, 'orderId', {
      columns: ['id', 'name', 'price', 'quantity', 'amount'],
    }),
  },
})
class ModelOrder {}| 名称 | 说明 | 
|---|---|
| relations.products | 关系名 | 
| $relation.hasMany | 定义1:n关系 | 
| ModelProduct | 目标Model | 
| 'orderId' | 外键 | 
| columns | 要查询的字段列表 | 
2. 使用关系 
在 Model 中定义的 hasMany 关系可以用于所有 CRUD 操作。通过include指定需要操作的关系,比如products: true,那么,系统在操作 Model Order 的同时,也会操作 Model Product
class ServicePost {
  async relationHasMany() {
    // insert
    const orderCreate = await this.scope.model.order.insert(
      {
        orderNo: 'Order001',
        products: [
          { name: 'Apple' },
          { name: 'Pear' },
        ],
      },
      {
        include: {
          products: true,
        },
      },
    );
    // get
    const _order = await this.scope.model.order.get(
      {
        id: orderCreate.id,
      },
      {
        include: {
          products: true,
        },
      },
    );
    // update
    await this.scope.model.order.update(
      {
        id: orderCreate.id,
        orderNo: 'Order001-Update',
        products: [
          // create product: Peach
          { name: 'Peach' },
          // update product: Apple
          { id: orderCreate.products?.[0].id, name: 'Apple-Update' },
          // delete product: Pear
          { id: orderCreate.products?.[1].id, deleted: true },
        ],
      },
      {
        include: {
          products: true,
        },
      },
    );
    // delete
    await this.scope.model.order.delete(
      {
        id: orderCreate.id,
      },
      {
        include: {
          products: true,
        },
      },
    );
  }
}- 当更新主表数据时,可以同时更新明细表数据(包括 Insert/Update/Delete)
 
belongsToMany 
1. 定义关系 
定义n:n关系需要中间 Model。比如,Model User 和 Model Role 是n:n,需要提供中间 Model RoleUser
@Model({
  entity: EntityUser,
  relations: {
    roles: $relation.belongsToMany('test-vona:roleUser', 'test-vona:role', 'userId', 'roleId', { columns: ['id', 'name'] }),
  },
})
class ModelUser {}| 名称 | 说明 | 
|---|---|
| relations.roles | 关系名 | 
| $relation.belongsToMany | 定义n:n关系 | 
| 'test-vona:roleUser' | 中间Model | 
| 'test-vona:role' | 目标Model | 
| 'userId' | 外键 | 
| 'roleId' | 外键 | 
| columns | 要查询的字段列表 | 
2. 使用关系 
在 Model 中定义的 belongsToMany 关系可以用于所有 CRUD 操作。需要强调的是,这里的 CRUD 操作是针对中间 Model,而不是目标 Model。通过include指定需要操作的关系,比如roles: true,那么,系统在操作 Model User 的同时,也会操作中间 Model RoleUser
class ServiceUser {
  async relationBelongsToMany() {
    // insert: roles
    const roles = await this.scope.model.role.insertBulk([
      { name: 'role-family' },
      { name: 'role-friend' },
    ]);
    const roleIdFamily = roles[0].id;
    const roleIdFriend = roles[1].id;
    // insert: user
    const userCreate = await this.scope.model.user.insert(
      {
        name: 'Tom',
        roles: [{
          id: roleIdFamily,
        }],
      },
      {
        include: {
          roles: true,
        },
      },
    );
    // get: user
    await this.scope.model.user.get(
      {
        id: userCreate.id,
      },
      {
        include: {
          roles: true,
        },
      },
    );
    // update: user
    await this.scope.model.user.update(
      {
        id: userCreate.id,
        roles: [
          // delete
          { id: roleIdFamily, deleted: true },
          // insert
          { id: roleIdFriend },
        ],
      },
      {
        include: {
          roles: true,
        },
      },
    );
    // delete: user
    await this.scope.model.user.delete(
      {
        id: userCreate.id,
      },
      {
        include: {
          roles: true,
        },
      },
    );
  }
}autoload 
通过前面的演示可以看到,如果要对关系进行操作,需要通过include指定对应的关系选项
如果需要关联的关系属于频繁操作,那么可以设置关系autoload: true,从而省去include选项
树形结构 
接下来我们实现一棵目录树,来演示如何利用autoload实现一个树形结构
1. 定义关系 
@Model({
  entity: EntityCategory,
  relations: {
    children: $relation.hasMany(() => ModelCategory, 'categoryIdParent', {
      autoload: true,
      columns: ['id', 'name'],
    }),
  },
})
class ModelCategory {}| 名称 | 说明 | 
|---|---|
| relations.children | 关系名 | 
| $relation.hasMany | 定义1:n关系 | 
| ModelCategory | 目标Model | 
| 'categoryIdParent' | 外键 | 
| autoload | 自动加载 | 
| columns | 要查询的字段列表 | 
2. 使用关系 
- 由于定义了与自身的 hasMany 关系,从而形成树形结构。此树形结构可以用于所有 CRUD 操作
 - 由于定义了
autoload: true,那么,系统在操作主数据的同时,也会自动操作 children 
class ServiceCategory {
  async categoryTree() {
    // create
    const treeCreate = await this.scope.model.category.insert({
      name: 'Category-1',
      children: [
        {
          name: 'Category-1-1',
          children: [
            { name: 'Category-1-1-1' },
          ],
        },
        {
          name: 'Category-1-2',
        },
      ],
    });
    // get
    const tree = await this.scope.model.category.get({
      id: treeCreate.id,
    });
    assert.equal(tree?.children.length, 2);
    assert.equal(tree?.children[0].children.length, 1);
    // update
    await this.scope.model.category.update({
      id: treeCreate.id,
      name: 'Category-1-Update',
      children: [
        // create
        { name: 'Category-1-3' },
        // update
        { id: treeCreate.children?.[0].id, name: 'Category-1-1-Update' },
        // delete
        { id: treeCreate.children?.[1].id, deleted: true },
      ],
    });
    // delete
    await this.scope.model.category.delete({
      id: treeCreate.id,
    });
  }
}树形结构(反向查询) 
前面演示的是如何从父级向子级查询一棵目录树,接下来演示从子级向父级查询目录树
1. 定义关系 
@Model({
  entity: EntityCategory,
  relations: {
    parent: $relation.belongsTo(() => ModelCategoryChain, () => ModelCategoryChain, 'categoryIdParent', {
      autoload: true,
      columns: ['id', 'name', 'categoryIdParent'],
    }),
  },
})
class ModelCategoryChain {}| 名称 | 说明 | 
|---|---|
| relations.parent | 关系名 | 
| $relation.belongsTo | 定义n:1关系 | 
| ModelCategoryChain | 源Model | 
| ModelCategoryChain | 目标Model | 
| 'categoryIdParent' | 外键 | 
| autoload | 自动加载 | 
| columns | 要查询的字段列表 | 
2. 使用关系 
- 由于定义了与自身的 belongsTo 关系,从而形成反向的树形结构。此树形结构只用于查询操作
 - 由于定义了
autoload: true,那么,系统在查询子目录的同时,也会自动查询 parent 
class ServiceCategory {
  async categoryTreeReverse() {
    // create
    const treeCreate = await this.scope.model.category.insert({
      name: 'Category-1',
      children: [
        {
          name: 'Category-1-1',
          children: [
            { name: 'Category-1-1-1' },
          ],
        },
        {
          name: 'Category-1-2',
        },
      ],
    });
    // 'Category-1-1-1'
    const subCategoryId = treeCreate.children?.[0].children?.[0].id;
    // get: reverse
    const subCategory = await this.scope.model.categoryChain.get({
      id: subCategoryId,
    });
    assert.equal(subCategory?.parent?.parent?.id, treeCreate.id);
  }
}关系选项 
1. $relation.hasOne/$relation.belongsTo 
| 名称 | 说明 | 
|---|---|
| autoload | 自动加载 | 
| columns | 要查询的字段列表 | 
| meta.client | 定义关系所使用的数据源,可以实现跨数据源的关系查询 | 
| meta.table | 定义关系所使用的数据表 | 
2. $relation.hasMany/$relation.belongsToMany 
| 名称 | 说明 | 
|---|---|
| autoload | 自动加载 | 
| columns | 要查询的字段列表 | 
| meta.client | 定义关系所使用的数据源,可以实现跨数据源的关系查询 | 
| meta.table | 定义关系所使用的数据表 | 
| distinct | 是否启用 distinct | 
| where | 条件语句 | 
| joins | 关联表 | 
| orders | 排序 | 
| limit | 可用于分页查询 | 
| offset | 可用于分页查询 | 
| aggrs | 聚合查询 | 
| groups | 分组查询 | 
Model参数 
在定义关系时需要提供参数:源Model/目标Model/中间Model,支持以下类型:
| 名称 | 说明 | 
|---|---|
| ModelPost | Model Class | 
| () => ModelPost | 通过函数延迟加载,从而避免触发循环依赖的错误 | 
| 'test-vona:post' | 当跨模块使用Model时,一般直接使用Model名 |