跳转到主要内容

实体(Entities)

什么是实体?

实体是映射到数据库表(或在使用MongoDB时映射到集合)的类。 您可以通过定义一个新类并使用@Entity()标记它来创建实体:

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
firstName: string

@Column()
lastName: string

@Column()
isActive: boolean
}

这将创建以下数据库表:

+-------------+--------------+----------------------------+
| user |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| firstName | varchar(255) | |
| lastName | varchar(255) | |
| isActive | boolean | |
+-------------+--------------+----------------------------+

基本实体由列和关系组成。 每个实体必须具有主列(如果使用MongoDB,则是ObjectId列)。

每个实体必须在数据源选项中注册:

import { DataSource } from "typeorm"
import { User } from "./entity/User"

const myDataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
entities: [User],
})

或者您可以指定包含所有实体的整个目录 - 所有实体都将被加载:

import { DataSource } from "typeorm"

const dataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3306,
username: "test",
password: "test",
database: "test",
entities: ["entity/*.js"],
})

如果要为User实体设置替代表名,可以在@Entity中指定:@Entity("my_users")。 如果要为应用程序中的所有数据库表设置基本前缀,可以在数据源选项中指定entityPrefix

在使用实体构造函数时,其参数**必须是可选的。由于ORM在从数据库加载时创建实体类的实例,因此不知道您的构造函数参数。

有关参数@Entity的详细信息,请参阅装饰器参考

实体列

由于数据库表由列组成,因此您的实体也必须由列组成。 您使用@Column标记的每个实体类属性都将映射到数据库表列。

主键列

每个实体必须至少有一个主键列。 有几种类型的主键列:

  • @PrimaryColumn() 创建一个主键列,可以接受任何类型的值。您可以指定列类型。如果不指定列类型,它将从属性类型推断出来。下面的示例将创建一个带有int类型的id,您在保存之前必须手动分配其值。
import { Entity, PrimaryColumn } from "typeorm"

@Entity()
export class User {
@PrimaryColumn()
id: number
}
  • @PrimaryGeneratedColumn() 创建一个主键列,其值将自动生成具有自增值的列。它将创建一个带有auto-increment/serial/sequence/identity(取决于数据库和提供的配置)的int列。在保存之前,您不必手动分配其值-值将自动生成。
import { Entity, PrimaryGeneratedColumn } from "typeorm"

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number
}
  • @PrimaryGeneratedColumn("uuid") 创建一个主键列,其值将自动生成具有uuid的值。Uuid是一个唯一的字符串标识。在保存之前,您不必手动分配其值-值将自动生成。
import { Entity, PrimaryGeneratedColumn } from "typeorm"

@Entity()
export class User {
@PrimaryGeneratedColumn("uuid")
id: string
}

您还可以拥有复合主键列:

import { Entity, PrimaryColumn } from "typeorm"

@Entity()
export class User {
@PrimaryColumn()
firstName: string

@PrimaryColumn()
lastName: string
}

使用save保存实体时,它始终尝试在数据库中使用给定的实体id(或ids)查找实体。 如果找到id/ids,则将更新数据库中的该行。 如果没有具有id/ids的行,将插入新行。

要通过id查找实体,可以使用manager.findOneByrepository.findOneBy。示例:

// 使用单个主键按id查找
const person = await dataSource.manager.findOneBy(Person, { id: 1 })
const person = await dataSource.getRepository(Person).findOneBy({ id: 1 })

// 使用复合主键按id查找
const user = await dataSource.manager.findOneBy(User, {
firstName: "Timber",
lastName: "Saw",
})
const user = await dataSource.getRepository(User).findOneBy({
firstName: "Timber",
lastName: "Saw",
})

特殊列

有几种带有附加功能的特殊列类型:

  • @CreateDateColumn 是一个特殊列,它会自动设置为实体的插入日期。 您不需要设置该列-它将自动设置。

  • @UpdateDateColumn 是一个特殊列,每次调用实体管理器或存储库的save时,它都会自动设置为实体的更新时间。 您不需要设置该列-它将自动设置。

  • @DeleteDateColumn 是一个特殊列,每次调用实体管理器或存储库的软删除时,它都会自动设置为实体的删除时间。 您不需要设置该列-它将自动设置。如果设置了@DeleteDateColumn,则默认范围将是“非删除”。

  • @VersionColumn 是一个特殊列,每次调用实体管理器或存储库的save时,它都会自动设置为实体的版本(递增数)。 您不需要设置该列-它将自动设置。

空间列

MS SQL、MySQL、MariaDB、PostgreSQL 和 CockroachDB 都支持空间列。TypeORM 对每个数据库的支持略有不同,特别是由于列名在不同数据库之间有所变化。

MS SQL 和 MySQL / MariaDB 的 TypeORM 支持将几何图形以 well-known text (WKT) 的形式提供和接受,因此几何列应该标记为 string 类型。

import { Entity, PrimaryColumn, Column } from "typeorm"

@Entity()
export class Thing {
@PrimaryColumn()
id: number

@Column("point")
point: string

@Column("linestring")
linestring: string
}

...

const thing = new Thing()
thing.point = "POINT(1 1)"
thing.linestring = "LINESTRING(0 0,1 1,2 2)"

TypeORM 的 PostgreSQL 和 CockroachDB 支持使用 GeoJSON 作为交换格式,因此几何列应该标记为 objectGeometry(或其子类,如 Point),在导入 geojson 类型 后或使用 TypeORM 内置的 GeoJSON 类型

import {
Entity,
PrimaryColumn,
Column,
Point,
LineString,
MultiPoint
} from "typeorm"

@Entity()
export class Thing {
@PrimaryColumn()
id: number

@Column("geometry")
point: Point

@Column("geometry")
linestring: LineString

@Column("geometry", {
spatialFeatureType: "MultiPoint",
srid: 4326,
})
multiPointWithSRID: MultiPoint
}

...

const thing = new Thing()
thing.point = {
type: "Point",
coordinates: [116.443987, 39.920843],
}
thing.linestring = {
type("LineString",
coordinates: [
[-87.623177, 41.881832],
[-90.199402, 38.627003],
[-82.446732, 38.413651],
[-87.623177, 41.881832],
],
}
thing.multiPointWithSRID = {
type: "MultiPoint",
coordinates: [
[100.0, 0.0],
[101.0, 1.0],
],
}

TypeORM试图尽力处理,但有时无法确定插入的值或PostGIS函数的结果应如何处理为几何值。因此,您可能会编写类似以下代码的代码,其中值从GeoJSON转换为PostGIS的geometry,并从json转换为GeoJSON:

import { Point } from "typeorm"

const origin: Point = {
type: "Point",
coordinates: [0, 0],
}

await dataSource.manager
.createQueryBuilder(Thing, "thing")
// 将序列化的GeoJSON转换为具有与表规范相匹配的SRID的几何值
.where(
"ST_Distance(geom, ST_SetSRID(ST_GeomFromGeoJSON(:origin), ST_SRID(geom))) > 0",
)
.orderBy({
"ST_Distance(geom, ST_SetSRID(ST_GeomFromGeoJSON(:origin), ST_SRID(geom)))":
{
order: "ASC",
},
})
.setParameters({
// 将GeoJSON转换为字符串
origin: JSON.stringify(origin),
})
.getMany()

await dataSource.manager
.createQueryBuilder(Thing, "thing")
// 将几何值结果转换为GeoJSON,以JSON格式处理(以便TypeORM知道对其进行反序列化)
.select("ST_AsGeoJSON(ST_Buffer(geom, 0.1))::json geom")
.from("thing")
.getMany()

列类型

TypeORM支持大多数常用的数据库支持的列类型。列类型是特定于数据库类型的,这提供了更大的灵活性,使您的数据库模式看起来更加多样化。

您可以将列类型指定为@Column的第一个参数,或在@Column的列选项中指定,例如:

@Column("int")

或者

@Column({ type: "int" })

如果要指定其他类型参数,可以通过列选项来实现。例如:

@Column("varchar", { length: 200 })

或者

@Column({ type: "int", width: 200 })

关于bigint类型的注意事项:在SQL数据库中使用的bigint列类型不适合常规的number类型,而是将属性映射为string

mysql / mariadb的列类型

bitintintegertinyintsmallintmediumintbigintfloatdoubledouble precisiondecdecimalnumericfixedboolbooleandatedatetimetimestamptimeyearcharncharnational charvarcharnvarcharnational varchartexttinytextmediumtextbloblongtexttinyblobmediumbloblongblobenumsetjsonbinaryvarbinarygeometrypointlinestringpolygonmultipointmultilinestringmultipolygongeometrycollectionuuidinet4inet6

注意:UUID、INET4和INET6仅适用于mariadb以及使它们可用的相应版本。

postgres的列类型

intint2int4int8smallintintegerbigintdecimalnumericrealfloatfloat4float8double precisionmoneycharacter varyingvarcharcharacterchartextcitexthstorebyteabitvarbitbit varyingtimetztimestamptztimestamptimestamp without time zonetimestamp with time zonedatetimetime without time zonetime with time zoneintervalboolbooleanenumpointlinelsegboxpathpolygoncirclecidrinetmacaddrtsvectortsqueryuuidxmljsonjsonbint4rangeint8rangenumrangetsrangetstzrangedaterangegeometrygeographycubeltree

cockroachdb的列类型

arrayboolbooleanbytesbyteablobdatenumericdecimaldecfloatfloat4float8double precisionrealinetintintegerint2int8int64smallintbigintintervalstringcharacter varyingcharactercharchar varyingvarchartexttimetime without time zonetimestamptimestamptztimestamp without time zonetimestamp with time zonejsonjsonbuuid

注意:CockroachDB将所有数值数据类型都返回为string。但是,如果您省略列类型并将属性定义为number,ORM将把字符串解析为数字。

sqlite / cordova / react-native / expo的列类型

intint2int8integertinyintsmallintmediumintbigintdecimalnumericfloatdoublerealdouble precisiondatetimevarying charactercharacternative charactervarcharncharnvarchar2unsigned big intbooleanblobtextclobdate

mssql的列类型

intbigintbitdecimalmoneynumericsmallintsmallmoneytinyintfloatrealdatedatetime2datetimedatetimeoffsetsmalldatetimetimecharvarchartextncharnvarcharntextbinaryimagevarbinaryhierarchyidsql_varianttimestampuniqueidentifierxmlgeometrygeographyrowversion

oracle的列类型

charncharnvarchar2varchar2longrawlong rawnumbernumericfloatdecdecimalintegerintsmallintrealdouble precisiondatetimestamptimestamp with time zonetimestamp with local time zoneinterval year to monthinterval day to secondbfileblobclobnclobrowidurowid

spanner的列类型

boolint64float64numericstringjsonbytesdatetimestamparray

enum列类型

enum列类型由postgresmysql支持。有多种可能的列定义方式:

使用TypeScript的枚举:

export enum UserRole {
ADMIN = "admin",
EDITOR = "editor",
GHOST = "ghost",
}

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column({
type: "enum",
enum: UserRole,
default: UserRole.GHOST,
})
role: UserRole
}

注意:支持字符串、数字和混合类型的枚举。

使用包含枚举值的数组:

export type UserRoleType = "admin" | "editor" | "ghost",

@Entity()
export class User {

@PrimaryGeneratedColumn()
id: number;

@Column({
type: "enum",
enum: ["admin", "editor", "ghost"],
default: "ghost"
})
role: UserRoleType
}

set列类型

set列类型由mariadbmysql支持。有多种可能的列定义方式:

使用TypeScript的枚举:

export enum UserRole {
ADMIN = "admin",
EDITOR = "editor",
GHOST = "ghost",
}

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column({
type: "set",
enum: UserRole,
default: [UserRole.GHOST, UserRole.EDITOR],
})
roles: UserRole[]
}

使用包含set值的数组:

export type UserRoleType = "admin" | "editor" | "ghost",

@Entity()
export class User {

@PrimaryGeneratedColumn()
id: number;

@Column({
type: "set",
enum: ["admin", "editor", "ghost"],
default: ["ghost", "editor"]
})
roles: UserRoleType[]
}

simple-array列类型

有一种特殊的列类型称为simple-array,它可以将基本数组值存储在单个字符串列中。所有值由逗号分隔。例如:

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column("simple-array")
names: string[]
}
const user = new User()
user.names = ["Alexander", "Alex", "Sasha", "Shurik"]

将以Alexander,Alex,Sasha,Shurik的形式存储在单个数据库列中。当您从数据库加载数据时,姓名将作为姓名数组返回,就像您存储它们一样。

请注意,您在写入的值中不能有任何逗号。

simple-json列类型

有一种特殊的列类型称为simple-json,它可以存储可以通过JSON.stringify存储在数据库中的任何值。当您没有数据库中的JSON类型并且希望存储和加载对象而无需麻烦时,这非常有用。例如:

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column("simple-json")
profile: { name: string; nickname: string }
}
const user = new User()
user.profile = { name: "John", nickname: "Malkovich" }

将以{"name":"John","nickname":"Malkovich"}的形式存储在单个数据库列中。当您从数据库加载数据时,您将通过JSON.parse获得您的对象/数组/基本值。

生成值的列

您可以使用@Generated装饰器创建具有生成值的列。例如:

@Entity()
export class User {
@PrimaryColumn()
id: number

@Column()
@Generated("uuid")
uuid: string
}

uuid值将自动生成并存储到数据库中。

除了"uuid"之外,还有"increment"、"identity"(仅适用于Postgres 10+)和"rowid"(仅适用于CockroachDB)生成类型,但某些数据库平台对于这种类型的生成有一些限制(例如,某些数据库只能有一个增量列,或者某些数据库要求增量是主键)。

列选项

列选项为实体列定义了附加选项。您可以在@Column中指定列选项:

@Column({
type: "varchar",
length: 150,
unique: true,
// ...
})
name: string;

ColumnOptions中可用的选项列表:

  • type: ColumnType - 列类型。可以是上述列类型之一。
  • name: string - 数据库表中的列名称。默认情况下,列名称是根据属性的名称生成的。您可以通过指定自己的名称来更改它。
  • length: number - 列类型的长度。例如,如果要创建varchar(150)类型,可以指定列类型和长度选项。
  • width: number - 列类型的显示宽度。仅用于MySQL整数类型
  • onUpdate: string - ON UPDATE触发器。仅在MySQL中使用。
  • nullable: boolean - 在数据库中将列设置为NULLNOT NULL。默认情况下,列是nullable: false
  • update: boolean - 指示列值是否通过“save”操作进行更新。如果为false,您只能在首次插入对象时写入此值。默认值为true
  • insert: boolean - 指示列值是否在插入对象时设置。默认值为true
  • select: boolean - 定义在进行查询时是否默认隐藏此列。当设置为false时,标准查询将不显示列数据。默认情况下,列是select: true
  • default: string - 添加数据库级别的列DEFAULT值。
  • primary: boolean - 将列标记为主键。与@PrimaryColumn使用相同。
  • unique: boolean - 将列标记为唯一列(创建唯一约束)。
  • comment: string - 数据库列的注释。并非所有数据库类型都支持。
  • precision: number - 十进制(精确数值)列的精度(仅适用于十进制列),即存储的值的最大位数。用于某些列类型。
  • scale: number - 十进制(精确数值)列的标度(仅适用于十进制列),表示小数点右侧的数字位数,不能大于精度。用于某些列类型。
  • zerofill: boolean - 在数字列上添加ZEROFILL属性。仅适用于MySQL。如果为true,MySQL会自动将UNSIGNED属性添加到该列。
  • unsigned: boolean - 在数字列上添加UNSIGNED属性。仅适用于MySQL。
  • charset: string - 定义列的字符集。不适用于所有数据库类型。
  • collation: string - 定义列的排序规则。
  • enum: string[]|AnyEnum - 在enum列类型中用于指定允许的枚举值列表。可以指定值数组或指定枚举类。
  • enumName: string - 定义所使用枚举的名称。
  • asExpression: string - 生成的列表达式。仅在MySQL中使用。
  • generatedType: "VIRTUAL"|"STORED" - 生成的列类型。仅在MySQL中使用。
  • hstoreType: "object"|"string" - HSTORE列的返回类型。将值返回为字符串或对象。仅在Postgres中使用。
  • array: boolean - 用于可以是数组的postgres和cockroachdb列类型(例如int[])。
  • transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType } - 用于将任意类型EntityType的属性转换为数据库支持的类型DatabaseType。也支持数组的转换器,写入时将按照自然顺序应用,并在读取时以相反顺序应用。例如[lowercase, encrypt]将在写入时先将字符串转换为小写,然后加密,而在读取时解密然后不进行任何操作。

注意:这些列选项大多是特定于关系数据库管理系统(RDBMS)的,不适用于MongoDB

实体继承

您可以通过使用实体继承来减少代码中的重复。

例如,您有PhotoQuestionPost实体:

@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string

@Column()
size: string
}

@Entity()
export class Question {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string

@Column()
answersCount: number
}

@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string

@Column()
viewCount: number
}

正如您所见,所有这些实体都有共同的列:idtitledescription。为了减少重复并产生更好的抽象,我们可以为它们创建一个名为Content的基类:

export abstract class Content {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column()
description: string
}
@Entity()
export class Photo extends Content {
@Column()
size: string
}

@Entity()
export class Question extends Content {
@Column()
answersCount: number
}

@Entity()
export class Post extends Content {
@Column()
viewCount: number
}

所有来自父实体的列(关系、嵌入等)(父实体也可以扩展其他实体)都将被继承并创建在最终实体中。

树状实体

TypeORM支持使用邻接列表和闭包表模式存储树结构。

邻接列表

邻接列表是一种具有自引用关系的简单模型。 这种方法的好处是简单性,缺点是由于联接限制,无法一次加载大型树结构。 示例:

import {
Entity,
Column,
PrimaryGeneratedColumn,
ManyToOne,
OneToMany,
} from "typeorm"

@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@Column()
description: string

@ManyToOne((type) => Category, (category) => category.children)
parent: Category

@OneToMany((type) => Category, (category) => category.parent)
children: Category[]
}

闭包表

闭包表在一个单独的表中以特殊的方式存储父子关系。 它在读取和写入时都非常高效。 要了解有关闭包表的更多信息,请查看Bill Karwin的精彩演示文稿。 示例:

import {
Entity,
Tree,
Column,
PrimaryGeneratedColumn,
TreeChildren,
TreeParent,
TreeLevelColumn,
} from "typeorm"

@Entity()
@Tree("closure-table")
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@Column()
description: string

@TreeChildren()
children: Category[]

@TreeParent()
parent: Category

@TreeLevelColumn()
level: number
}