• 作者:老汪软件技巧
  • 发表时间:2024-09-15 15:01
  • 浏览量:

前言

哈喽大家好!我是嘟老板,有经验的小伙伴应该知道,不同的业务实体间,通常会存在一些相同的字段,比如创建人、创建时间、更新人、更新时间等等,既然每个实体都有,能不能把他们统一维护起来,供每个实体复用呢?今天我们就来看看,基于 TypeORM 如何实现实体复用。

阅读本文您将收获:

了解什么是 TypeORM 实体,如何定义。了解如何使用嵌入和集成实现 TypeORM 实体复用。等等...实体简介(选读)

若您已对 TypeORM 有所了解,可略过。

实体是一个与数据库表或者 MongoDB 集合对应的类(class),是数据库表或集合在应用程序中的表示。

若要创建一个 TypeORM 实体,需先定义一个类,然后使用 注解来标记它。

例如:

@Entity()
class User {
    name: string
    tel: string
    email: string
    // ...
}

嵌入列

嵌入列是一种特殊的实体列,它可以接受一个定义了实体列的类,并将这些列合并到当前实体的数据库表中。

实现

嵌入列通过定义一个特殊的列,即可完成嵌入。例如:

@Column(() => Base) 
base: Base

创建基础类

通常业务实体都会包含以下字段:

我们可以创建一个基础类 BaseEntity,专门维护公共的实体列。

import {
  Column,
  CreateDateColumn,
  DeleteDateColumn,
  UpdateDateColumn,
  VersionColumn
} from 'typeorm'
import { BaseEntity as Base } from './base/base.entity'
export class BaseEntity {
  // 数据版本
  @VersionColumn()
  version!: number
  // 创建人
  @Column()
  createdBy!: string
  // 创建时间
  @CreateDateColumn()
  createdAt!: Date
  // 更新人
  @Column()
  updatedBy!: string
  // 更新时间
  @UpdateDateColumn()
  updatedAt!: Date
  // 删除时间
  @DeleteDateColumn()
  deletedAt!: Date
}

创建业务实体

假设现有一个菜单实体 Menu,我们将 BaseEntity 定义的列嵌入到 Menu 类中。

import { ACTIVATION_STATUS, MENU_TYPE, MENU_TYPE_DESC } from '@constants/enums'
import {
  Column,
  CreateDateColumn,
  DeleteDateColumn,
  Entity,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
  VersionColumn
} from 'typeorm'
@Entity({ name: 'menu' })
export class Menu {
  @PrimaryGeneratedColumn()
  id!: number
  // 菜单编码
  @Column({ unique: true })
  code!: string
  // 菜单名称
  @Column()
  name!: string
  // 其他列 ...
  @Column(() => Base) 
  base: Base
}

这样 Menu 实体除去本身定义的业务列外,还会额外嵌入以下列:

细心的小伙伴会发现,嵌入到 Menu 类中的列名称并没有保持 BaseEntity 中定义的名称,而是按照命名规则: 目标实体类定义的列名称 + BaseEntity 定义的列名称,并转换为小驼峰形式 重新命名,例如 Menu 实体类定义的 base 拼接 BaseEntity 定义的 version、createdBy...

若您的程序中不想使用嵌入列的列命名规则,可以使用 实体继承,请继续往下看...

实体继承

实体继承分为以下两种方式:

我们分别来看一下...

具体表继承(推荐)

_重复劳动的意义_重复劳动英语怎么说

具体表继承是最简单且最有效的继承方法,属于一种数据库集成策略,会为每个业务类创建单独的数据表。

实现

上文中的 BaseEntity 基础类不变,调整下 Menu 实体类,干掉 Menu 类中嵌入的 base 字段,改为使用 extends 继承 BaseEntity 类。

import { ACTIVATION_STATUS, MENU_TYPE, MENU_TYPE_DESC } from '@constants/enums'
import {
  Column,
  CreateDateColumn,
  DeleteDateColumn,
  Entity,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
  VersionColumn
} from 'typeorm'
@Entity({ name: 'menu' })
export class Menu extends BaseEntity {
  @PrimaryGeneratedColumn()
  id!: number
  // 菜单编码
  @Column({ unique: true })
  code!: string
  // 菜单名称
  @Column()
  name!: string
  // 其他列 ...
}

这样就完成了具体表继承,仅仅需要通过 extends 关键字继承基础类即可,确实十分的简单。

个人认为,这是最符合业务实体定义习惯的复用方式,基础类中定义的列会直接继承到业务实体类中,不会更改列名,而且会为目标实体创建包含继承列的数据表。

单表继承

同样是实体继承,单表继承有何不同呢?

当存在多个类,各自拥有不同的属性,但在数据库中,这些类的数据存储在同一个表中。这种情况可以用到单表继承。

实现

TypeORM 提供 @TableInheritance 注解来方便实现单表继承。

创建继承类

假设现有两个业务实体类:菜单实体 Mneu 和按钮实体 Button,它们存在公共属性:id、code、name。

新增一个 Resource 类,定义 Menu 类和 Button 类的公共属性:

import {
  Entity
  Column,
  PrimaryGeneratedColumn,
  TableInheritance
} from 'typeorm'
@Entity()
@TableInheritance({ column: { type: "varchar", name: "type" } })
export class Resource {
    @PrimaryGeneratedColumn()
    id: number
    @Column()
    code: string
    @Column()
    name: string
}

定义业务实体类

Menu 类和 Button 类定义如下,需要注意以下几点:

Menu 类:

import {
  ChildEntity,
  Column,
} from 'typeorm'
@ChildEntity()
export class Menu extends Resource {
    @Column()
    type: string
}

Button 类:

import {
  ChildEntity,
  Column,
} from 'typeorm'
@ChildEntity()
export class Button extends Resource {
    @Column()
    size: string
}

这样单表继承就搞定了,数据库中会创建一个名为 resource 的数据表,Menu 和 Button 的实体数据都会存在该表中。

结语

本文重点介绍了如何基于 TypeORM 实现实体复用,主要有三种方式,分别是嵌入列、具体表继承和单表继承,其中:

希望对您有所帮助!ZimuAdmin 对 TypeORM 有深入应用,欢迎 star。

如您对文章内容有任何疑问或想深入讨论,欢迎评论区留下您的问题和见解。

技术简而不凡,创新生生不息。我是 嘟老板,咱们下期再会。

往期干货