前言
哈喽大家好!我是 嘟老板,有经验的小伙伴应该知道,不同的业务实体间,通常会存在一些相同的字段,比如创建人、创建时间、更新人、更新时间等等,既然每个实体都有,能不能把他们统一维护起来,供每个实体复用呢?今天我们就来看看,基于 TypeORM 如何实现实体复用。
阅读本文您将收获:
实体简介(选读)
若您已对 TypeORM 有所了解,可略过。
实体是一个与数据库表或者 MongoDB 集合对应的类(class),是数据库表或集合在应用程序中的表示。
若要创建一个 TypeORM 实体,需先定义一个类,然后使用 @Entity() 注解来标记它。
例如:
@Entity()
class User {
name: string
tel: string
email: string
// ...
}
嵌入列
嵌入列是一种特殊的实体列,它可以接受一个定义了实体列的类,并将这些列合并到当前实体的数据库表中。
实现
嵌入列通过定义一个特殊的列,即可完成嵌入。例如:
@Column(() => Base)
base: Base
创建基础类
通常业务实体都会包含以下字段:
- 版本
version
- 创建人
createdBy
- 创建时间
createdAt
- 更新人
updatedBy
- 更新时间
updatedAt
- 删除时间
deletedAt
我们可以创建一个基础类 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
实体除去本身定义的业务列外,还会额外嵌入以下列:
- baseVersion
- baseCreatedBy
- baseCreatedAt
- baseUpdatedBy
- baseUpdatedAt
- baseDeletedAt
细心的小伙伴会发现,嵌入到 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 类定义如下,需要注意以下几点:
- 实体类使用
@ChildEntity
注解标记。 - 使用
extends
继承Resource
类。
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
实现实体复用,主要有三种方式,分别是嵌入列、具体表继承和单表继承,其中:
- 嵌入列通过定义实体特殊列的方式将基础类的列嵌入到业务实体中,嵌入到业务实体的列名会变更。
- 具体表继承最符合业务实体定义习惯,通过
extends
关键字继承基础类,不会变更继承的列名,且会为每个业务实体创建单独的数据表。 - 单表继承适用于多个业务实体存储在同一数据表的场景,通过
@TableInheritance
注解标记基础类,@ChildEntity()
注解标记业务实体类,并通过extends
关键字继承基础类。
希望对您有所帮助!ZimuAdmin 对 TypeORM
有深入应用,欢迎 star。
如您对文章内容有任何疑问或想深入讨论,欢迎评论区留下您的问题和见解。
技术简而不凡,创新生生不息。我是 嘟老板,咱们下期再会。
往期干货
- 🧨🧨🧨你想要的 RBAC 权限管理实现全流程来啦!~~ 代码含量过多,请谨慎阅读 ~~
- 💡💡💡Vue3 用了这么久还没体验过 JSX/TSX?来封装个业务弹窗玩玩
- 👏👏👏厉害了 Vue Vine !Vue 组件还能这样写!!!
- TypeORM 知多少?来看看 Node 服务端如何基于 TypeORM 封装 BaseService
- 还在考虑 node 服务端如何管理环境变量?来试试 node-config
- 还在纠结 node 服务端缓存怎么做?来试试 Redis
- 还在好奇 node 后端登录流程怎么做?进来聊聊吧
- 搞定 TS 装饰器,让你写 Node 接口更轻松
- 一文带你了解多数企业系统都在用的 RBAC 权限管理策略
- 还不会搭建 Node 服务?一文带你了解如何用 express + ts 搞定后端