实体

实体是什么?

实体是一个映射到数据库表(或使用 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 { createConnection, Connection } from "typeorm";
import { User } from "./entity/User";

const connection: Connection = await createConnection({
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "test",
    password: "test",
    database: "test",
    entities: [User]
});

或者你可以指定包含所有实体的整个目录, 该目录下所有实体都将被加载:

import { createConnection, Connection } from "typeorm";

const connection: Connection = await createConnection({
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "test",
    password: "test",
    database: "test",
    entities: ["entity/*.js"]
});

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

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

Decorators reference中了解有关参数@Entity 的更多信息。

实体列

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

主列

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

  • @PrimaryColumn() 创建一个主列,它可以获取任何类型的任何值。你也可以指定列类型。 如果未指定列类型,则将从属性类型自动推断。

下面的示例将使用int类型创建 id,你必须在保存之前手动分配。

import { Entity, PrimaryColumn } from "typeorm";

@Entity()
export class User {
    @PrimaryColumn()
    id: number;
}
  • @PrimaryGeneratedColumn() 创建一个主列,该值将使用自动增量值自动生成。 它将使用auto-increment /serial /sequence创建int列(取决于数据库)。 你不必在保存之前手动分配其值,该值将会自动生成。

import { Entity, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;
}
  • @PrimaryGeneratedColumn("uuid") 创建一个主列,该值将使用uuid自动生成。 Uuid 是一个独特的字符串 id。 你不必在保存之前手动分配其值,该值将自动生成。

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.findOnerepository.findOne。 例:

// 使用单个主键查找一个id
const person = await connection.manager.findOne(Person, 1);
const person = await connection.getRepository(Person).findOne(1);

// 使用复合主键找到一个id
const user = await connection.manager.findOne(User, { firstName: "Timber", lastName: "Saw" });
const user = await connection.getRepository(User).findOne({ firstName: "Timber", lastName: "Saw" });

特殊列

有几种特殊的列类型可以使用:

  • @CreateDateColumn 是一个特殊列,自动为实体插入日期。无需设置此列,该值将自动设置。

  • @UpdateDateColumn 是一个特殊列,在每次调用实体管理器或存储库的save时,自动更新实体日期。无需设置此列,该值将自动设置。

  • @VersionColumn 是一个特殊列,在每次调用实体管理器或存储库的save时自动增长实体版本(增量编号)。无需设置此列,该值将自动设置。

空间列

MS SQL,MySQL/MariaDB 和 PostgreSQL 都支持 Spatial 列。由于各数据库列名不同,TypeORM 对数据库的支持略有差异。

MS SQL 和 MySQL/MariaDB 的 TypeORM 支持well-known text(WKT)的 geometries,因此 geometry 列 应该是用string类型标记。

TypeORM 的 PostgreSQL 支持使用GeoJSON作为交换格式,因此 geometry 列应在导入后标记为objectGeometry(或子类,例如Point)。

TypeORM尝试做正确的事情,但并不总是能够确定何时插入的值或PostGIS函数的结果应被视为几何。 因此,你可能会发现自己编写的代码类似于以下代码,其中值从GeoJSON转换为PostGIS geometry,并作为json转换为GeoJSON:

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

await getManager()
    .createQueryBuilder(Thing, "thing")
    // 将字符串化的GeoJSON转换为具有与表规范匹配的SRID的geometry
    .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)))",
        "ASC"
    )
    .setParameters({
        // 字符串化 GeoJSON
        origin: JSON.stringify(origin)
    })
    .getMany();

await getManager()
    .createQueryBuilder(Thing, "thing")
    // 将geometry结果转换为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", length: 200 })

mysql/mariadb的列类型

int, tinyint, smallint, mediumint, bigint, float, double, dec, decimal, numeric, date, datetime, timestamp, time, year, char, varchar, nvarchar, text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob, enum, json, binary, geometry, point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection

postgres的列类型

int, int2, int4, int8, smallint, integer, bigint, decimal, numeric, real, float, float4, float8, double precision, money, character varying, varchar, character, char, text, citext, hstore, bytea, bit, varbit, bit varying, timetz, timestamptz, timestamp, timestamp without time zone, timestamp with time zone, date, time, time without time zone, time with time zone, interval, bool, boolean, enum, point, line, lseg, box, path, polygon, circle, cidr, inet, macaddr, tsvector, tsquery, uuid, xml, json, jsonb, int4range, int8range, numrange, tsrange, tstzrange, daterange, int4multirange, int8multirange, nummultirange, tsmultirange, tstzmultirange, multidaterange, geometry, geography

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

int, int2, int8, integer, tinyint, smallint, mediumint, bigint, decimal, numeric, float, double, real, double precision, datetime, varying character, character, native character, varchar, nchar, nvarchar2, unsigned big int, boolean, blob, text, clob, date

mssql的列类型

int, bigint, bit, decimal, money, numeric, smallint, smallmoney, tinyint, float, real, date, datetime2, datetime, datetimeoffset, smalldatetime, time, char, varchar, text, nchar, nvarchar, ntext, binary, image, varbinary, hierarchyid, sql_variant, timestamp, uniqueidentifier, xml, geometry, geography, rowversion

oracle的列类型

char, nchar, nvarchar2, varchar2, long, raw, long raw, number, numeric, float, dec, decimal, integer, int, smallint, real, double precision, date, timestamp, timestamp with time zone, timestamp with local time zone, interval year to month, interval day to second, bfile, blob, clob, nclob, rowid, urowid

enum 列类型

postgresmysql都支持enum列类型。 并有多种列定义方式:

使用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
}

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值。 当你从数据库加载数据时,name 将作为 names 数组返回,就像之前存储它们一样。

注意不能在值里面有任何逗号。

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 返回 object/array/primitive。

具有生成值的列

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

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

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

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

除了"uuid"之外,还有"increment"生成类型,但是对于这种类型的生成,某些数据库平台存在一些限制(例如,某些数据库只能有一个增量列,或者其中一些需要增量才能成为主键)。

列选项

列选项定义实体列的其他选项。 你可以在@ Column上指定列选项:

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

ColumnOptions中可用选项列表:

  • type: ColumnType - 列类型。其中之一在上面.

  • name: string - 数据库表中的列名。

默认情况下,列名称是从属性的名称生成的。 你也可以通过指定自己的名称来更改它。

  • length: number - 列类型的长度。 例如,如果要创建varchar(150)类型,请指定列类型和长度选项。

  • width: number - 列类型的显示范围。 仅用于MySQL integer types

  • onUpdate: string - ON UPDATE触发器。 仅用于 MySQL.

  • nullable: boolean - 在数据库中使列NULLNOT NULL。 默认情况下,列是nullable:false

  • update: boolean - 指示"save"操作是否更新列值。如果为false,则只能在第一次插入对象时编写该值。 默认值为"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列类型中使用,以指定允许的枚举值列表。 你也可以指定数组或指定枚举类。

  • asExpression: string - 生成的列表达式。 仅在MySQL中使用。

  • generatedType: "VIRTUAL"|"STORED" - 生成的列类型。 仅在MySQL中使用。

  • hstoreType: "object"|"string" -返回HSTORE列类型。 以字符串或对象的形式返回值。 仅在Postgres中使用。

  • array: boolean - 用于可以是数组的 postgres 列类型(例如 int [])

  • transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType } - 用于将任意类型EntityType的属性编组为数据库支持的类型DatabaseType

注意:大多数列选项都是特定于 RDBMS 的,并且在MongoDB中不可用。

实体继承

你可以使用实体继承减少代码中的重复。

例如,你有Photo, Question, Post 三个实体:

@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;
}

来自父实体的所有列(relations,embeds 等)(父级也可以扩展其他实体)将在最终实体中继承和创建。

树实体

TypeORM 支持存储树结构的 Adjacency 列表和 Closure 表模式。

邻接列表

邻接列表是一个具有自引用的简单模型。 这种方法的好处是简单,缺点是你不能因为连接限制而立刻加载一个树实体。

例如:

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

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

    @Column()
    name: string;

    @Column()
    description: string;

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

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

Closure 表

closure 表以特殊方式在单独的表中存储父和子之间的关系。 它在读写方面都很有效。

要了解有关 closure 表的更多信息,请查看 this awesome presentation by 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;
}

Last updated