typeorm-basic

中文文档一:https://typeorm.bootcss.com/

中文文档二:https://typeorm.biunav.com/zh/

中文文档其三:https://orkhan.gitbook.io/typeorm/docs/zh_cn/logging

数据库

只有在建立连接后才能与数据库进行交互。 TypeORM 的 Connection 不会像看起来那样设置单个数据库连接,而是设置连接池。 如果你对数据库连接感兴趣,请参阅 QueryRunner 文档。 QueryRunner 的每个实例都是一个独立的数据库连接。一旦调用 Connectionconnect 方法,就建立连接池设置。 如果使用 createConnection 函数设置连接,则会自动调用 connect 方法。调用 close 时会断开连接(关闭池中的所有连接)。 通常情况下,你只能在应用程序启动时创建一次连接,并在完全使用数据库后关闭它。实际上,如果要为站点构建后端,并且后端服务器始终保持运行,则不需要关闭连接。

连接选项 Connection Options

概述

连接选项是你传递给 createConnection 或在 ormconfig 文件中定义的连接配置。不同的数据库有自己的特定连接选项。

多个连接,数据库,模式和主从复制设置

使用多个连接

import {createConnections} from "typeorm";

const connections = await createConnections([{
    name: "db1Connection",
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "root",
    password: "admin",
    database: "db1",
    entities: [__dirname + "/entity/*{.js,.ts}"],
    synchronize: true
}, {
    name: "db2Connection",
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "root",
    password: "admin",
    database: "db2",
    entities: [__dirname + "/entity/*{.js,.ts}"],
    synchronize: true
}]);

此方法允许你连接到已拥有的任意数量的数据库,每个数据库都有自己的配置,自己的实体和整体 ORM 范围和设置。

对于每个连接,将创建一个新的 Connection 实例。 你必须为创建的每个连接指定唯一的名称。

也可以从 ormconfig 文件加载所有连接选项:

import {createConnections} from "typeorm";

const connections = await createConnections();

指定要按名称创建的连接:

import {createConnection} from "typeorm";

const connection = await createConnection("db2Connection");

使用连接时,必须指定连接名称以获取特定连接:

import {getConnection} from "typeorm";

const db1Connection = getConnection("db1Connection");
// 现在可以使用"db1"数据库...

const db2Connection = getConnection("db2Connection");
// 现在可以使用"db2"数据库...

使用此方法的好处是你可以使用不同的登录凭据,主机,端口甚至数据库类型来配置多个连接。

但是缺点可能是需要管理和使用多个连接实例。

在单个连接中使用多个数据库

数据库连接中指定的 database 字段作为主数据库,如果实体中不填 具体字段则默认是 database 字段。想要使用连接的 mysql 服务中的其他数据库,只要在实体上直接声明即可

如果你不想创建多个连接,但是想在一个连接中使用多个数据库,则可以指定使用的每个实体的数据库名称:

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

@Entity({ database: "secondDB" })
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

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

@Entity({ database: "thirdDB" })
export class Photo {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    url: string;

}

user 实体将在 secondDB 数据库内创建,Photo 实体则在 thirdDB 数据库内。

如果要从其他数据库中选择数据,则只需提供一个实体:

const users = await connection
    .createQueryBuilder()
    .select()
    .from(User, "user")
    .addFrom(Photo, "photo")
    .andWhere("photo.userId = user.id")
    .getMany(); // userId因其跨数据库请求而不是外键

此代码将生成以下 sql 查询(取决于数据库类型):

SELECT * FROM "secondDB"."question" "question", "thirdDB"."photo" "photo" 
    WHERE "photo"."userId" = "user"."id"

还可以指定表而不是实体:

const users = await connection
    .createQueryBuilder()
    .select()
    .from("secondDB.user", "user")
    .addFrom("thirdDB.photo", "photo")
    .andWhere("photo.userId = user.id")
    .getMany(); // userId因其跨数据库请求而不是外键

实体

概述

实体是一个映射到数据库表的类。 你可以通过定义一个新类来创建一个实体,并用 @Entity() 来标记:

或使用 MongoDB 时的集合

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"]
});

@Entity()

如果要为 User 实体使用替代表名,可以在 @ Entity 中指定:@Entity(“my_users”)

如果要为应用程序中的所有数据库表设置基本前缀,可以在连接选项中指定 entityPrefix

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

你还可以指定一些其他实体选项:

例子:

@Entity({
    name: "users",
    engine: "MyISAM",
    database: 'example_dev',
    schema: 'schema_with_best_tables',
    synchronize: false,
    orderBy: {
        name: "ASC",
        id: "DESC"
    }
})
export class User {}

实体列

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

每个实体必须至少有一个主列。

@PrimaryColumn()

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

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

import { Entity, PrimaryColumn } from "typeorm";

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

@PrimaryGeneratedColumn()

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

@PrimaryGeneratedColumn("uuid")

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

特殊列

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

列类型

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

enum 列类型

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

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

}

具有生成值的列

你可以使用 @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 中可用选项列表:

实体继承

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

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

其实就是普通的 JS 继承

嵌入式实体

通过使用 embedded columns,可以减少应用程序中的重复(使用组合而不是继承)。

嵌入列是一个列,它接受具有自己列的类,并将这些列合并到当前实体的数据库表中。

例如:假设我们有 UserEmployeeStudent 实体。这些属性都有少量的共同点,first namelast name 属性。

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

@Entity()
export class User {
    
    @PrimaryGeneratedColumn()
    id: string;
    
    @Column()
    firstName: string;
    
    @Column()
    lastName: string;
    
    @Column()
    isActive: boolean;
    
}

我们可以做的是通过创建一个包含 firstNamelastName 的新类:

import {Entity, Column} from "typeorm";

export class Name {
    
    @Column()
    first: string;
    
    @Column()
    last: string;
    
}

然后 "connect" 实体中的这些列:

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
import {Name} from "./Name";

@Entity()
export class User {
    
    @PrimaryGeneratedColumn()
    id: string;
    
    @Column(type => Name)
    name: Name;
    
    @Column()
    isActive: boolean;
    
}

Name 实体中定义的所有列将合并为 useremployeestudent

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

这种方式可以减少实体类中的代码重复。 你可以根据需要在嵌入式类中使用尽可能多的列(或关系)。 甚至可以在嵌入式类中嵌套嵌套列。

分离实体定义

关系

在应用层处理就好

在数据库中,我的字段仅仅是一个 id,作为外键

而在 typeorm 中却是一个数组,这个是最奇怪的地方

加入我要查询一个关系表,我可以先拿到 id 外键,然后再根据这个 id 去关系表中查询

如果是 typeorm 难道是只要查到了 就可以从 数组中取? 但是这样我就得自己去遍历数组了

疑问

typeId,在 chara 类型中,我是查询到 typeId 再直接插入的

如果使用了 MantToOne 能做到我不查询 id 吗? 比如 客户端的语义化输入是 type live,我直接 insert live,能否自动转换成 typeId

不行的,关系是 Entity 之间的关系,live 只是一个字符串,框架没有处理,所以还是需要根据字符串查询出 EventType 实体,再与实体建立关系,所以定义实体的时候也只能写 外键列,写其他列是没有意义的

然后 groupEvent ,能否做到我直接更新 groupEvent ,然后 event-list 也一起更新?

操作实体

Repository

Repository 就像 EntityManager 一样,但其操作仅限于具体实体。

你可以通过 getRepository(Entity)Connection#getRepositoryEntityManager#getRepository 访问存储库。

import { getRepository } from "typeorm";
import { User } from "./entity/User";

const userRepository = getRepository(User); // 你也可以通过getConnection().getRepository()或getManager().getRepository() 获取
const user = await userRepository.findOne(1);
user.name = "Umed";
await userRepository.save(user);

有三种类型的存储库:

Repository API

const manager = repository.manager;
const target = repository.target;
if (repository.hasId(user)) {
  // ... do something
}
const userId = repository.getId(user); // userId === 1

EntityManager

使用 EntityManager,你可以管理(insert, update, delete, load 等)任何实体。EntityManager 就像放一个实体存储库的集合的地方。

你可以通过 getManager()Connection 访问实体管理器。

如何使用它:

import { getManager } from "typeorm";
import { User } from "./entity/User";

const entityManager = getManager(); // 你也可以通过 getConnection().manager 获取
const user = await entityManager.findOne(User, 1);
user.name = "Umed";
await entityManager.save(user);

EntityManager API

对比

EntityManager 是数据库维度的,每次执行增删改查的时候,需要先获取 repository 再进行操作

我觉得简单理解为获取 repository 再操作就可以了

当多个实体可以复用相同的服务的时候,应该使用 EntityManager 进行实体操作

增删改查(Repository API)

TypeORM 支持存储库设计模式,因此每个实体都有自己的存储库。可以从数据库连接获得这些存储库。

存储库是 TypeORM 中增删改查的主体

Insert

insert - 插入新实体或实体数组

await repository.insert({
  firstName: "Timber",
  lastName: "Timber"
});

await manager.insert(User, [
  {
    firstName: "Foo",
    lastName: "Bar"
  },
  {
    firstName: "Rizz",
    lastName: "Rak"
  }
]);

Create + Save

create - 创建 User 的新实例。 接受具有用户属性的对象文字,该用户属性将写入新创建的用户对象(可选)。

const user = repository.create({
  id: 1,
  firstName: "Timber",
  lastName: "Saw"
}); 
// 等价于
const user =repository.create();
use.firstName = "Timber";
user.lastName = "Saw"''

create 方法只是创建了一个实例,并没有更改数据库对应的表

save - 保存给定实体或实体数组:

await repository.save(user);
await repository.save([category1, category2, category3]);

Merge + Save

merge - 将多个实体合并为一个实体。

const user = new User();
repository.merge(user, { firstName: "Timber" }, { lastName: "Saw" }); 
// 和 user.firstName = "Timber"; user.lastName = "Saw";一样
const newGroup = await this.groupsRepository.create(createGroupDto);
const test = this.groupsRepository.merge(newGroup, {
  members: ['honoka', 'kotori'],
});
console.log('test: ', test);
await this.groupsRepository.save(newGroup);

test: Group

Preload + Save

preload - 从给定的普通 javascript 对象创建一个新实体。 如果实体已存在于数据库中,则它将加载它(以及与之相关的所有内容),并将所有值替换为给定对象中的新值,并返回新实体。 新实体实际上是从数据库加载的所有属性都替换为新对象的实体。

const partialUser = {
  id: 1,
  firstName: "Rizzrak",
  profile: {
    id: 1
  }
};
const user = await repository.preload(partialUser);
// user将包含partialUser中具有partialUser属性值的所有缺失数据:
// { id: 1, firstName: "Rizzrak", lastName: "Saw", profile: { id: 1, ... } }

findOne

findOne - 查找匹配某些 ID 或查找选项的第一个实体。

const user = await repository.findOne(1);
const timber = await repository.findOne({ firstName: "Timber" });

findByIds

findByIds - 按 ID 查找多个实体。

const users = await repository.findByIds([1, 2, 3]);
// 使用单个主键查找一个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" });

findOneOrFail

findOneOrFail - - findOneOrFail - 查找匹配某些 ID 或查找选项的第一个实体。 如果没有匹配,则 Rejects 一个 promise。

const user = await repository.findOneOrFail(1);
const timber = await repository.findOneOrFail({ firstName: "Timber" });

Find

find - 查找指定条件的实体。

const timbers = await repository.find({ firstName: "Timber" });

不指定条件时,相当于 findAll,返回一个实体数组

group:  [
  Group { groupName: 'muse', members: [ 'honoka' ] },
  Group { groupName: 'aqours', members: [ 'string' ] }
]

Count

count - 符合指定条件的实体数量。对分页很有用。

onst count = await repository.count({ firstName: "Timber" });

findAndCount

findAndCount - 查找指定条件的实体。还会计算与给定条件匹配的所有实体数量, 但是忽略分页设置 (skiptake 选项)。

const [timbers, timbersCount] = await repository.findAndCount({ firstName: "Timber" });

Query

query - 执行原始 SQL 查询。

onst rawData = await repository.query(`SELECT * FROM USERS`);

查询选项

所有存储库和管理器 find 方法都接受可用于查询所需数据的特殊选项,而无需使用 QueryBuilder

Where

查询实体的简单条件

userRepository.find({ where: { firstName: "Timber", lastName: "Saw" } });

查询嵌入实体列应该根据定义它的层次结构来完成。 例:

userRepository.find({ where: { name: { first: "Timber", last: "Saw" } } });

使用 OR 运算符查询:

userRepository.find({
    where: [{ firstName: "Timber", lastName: "Saw" }, { firstName: "Stan", lastName: "Lee" }]
});

将执行以下查询:

SELECT * FROM "user" WHERE ("firstName" = 'Timber' AND "lastName" = 'Saw') OR ("firstName" = 'Stan' AND "lastName" = 'Lee')

Order

选择排序

userRepository.find({
    order: {
        name: "ASC",
        id: "DESC"
    }
});

OR 运算符

使用 OR 运算符查询:

userRepository.find({
    where: [{ firstName: "Timber", lastName: "Saw" }, { firstName: "Stan", lastName: "Lee" }]
});

将执行以下查询:

SELECT * FROM "user" WHERE ("firstName" = 'Timber' AND "lastName" = 'Saw') OR ("firstName" = 'Stan' AND "lastName" = 'Lee')

其他

select - 表示必须选择对象的哪些属性

userRepository.find({ select: ["firstName", "lastName"] });

relations - 关系需要加载主体。 也可以加载子关系(join 和 leftJoinAndSelect 的简写)

userRepository.find({ relations: ["profile", "photos", "videos"] });
userRepository.find({ relations: ["profile", "photos", "videos", "videos.video_attributes"] });

join - 需要为实体执行联接,扩展版对的 "relations"。

userRepository.find({
    join: {
        alias: "user",
        leftJoinAndSelect: {
            profile: "user.profile",
            photo: "user.photos",
            video: "user.videos"
        }
    }
});

cache - 启用或禁用查询结果缓存。 有关更多信息和选项,请参见 caching

userRepository.find({
    cache: true
});

lock - 启用锁查询。 只能在 findOne 方法中使用。 lock 是一个对象,可以定义为:

{ mode: "optimistic", version: number|Date }
{ mode: "pessimistic_read"|"pessimistic_write"|"dirty_read" }
userRepository.findOne(1, {
    lock: { mode: "optimistic", version: 1 }
})

分页

返回多个实体的 find 方法(findfindAndCountfindByIds),同时也接受以下选项:

skip - 偏移(分页)

userRepository.find({
    skip: 5
});

take - limit (分页) - 得到的最大实体数,相当于是 limit

userRepository.find({
    take: 10
});

如果你正在使用带有 MSSQL 的 typeorm,并且想要使用 takelimit,你必须正确使用 order,否则将会收到以下错误:'FETCH 语句中 NEXT 选项的使用无效。'

userRepository.find({
    order: {
        columnName: "ASC"
    },
    skip: 0,
    take: 10
});

完整示例

userRepository.find({
    select: ["firstName", "lastName"],
    relations: ["profile", "photos", "videos"],
    where: {
        firstName: "Timber",
        lastName: "Saw"
    },
    order: {
        name: "ASC",
        id: "DESC"
    },
    skip: 5,
    take: 10,
    cache: true
});

进阶选项

TypeORM 提供了许多内置运算符,可用于创建更复杂的查询

import { Not } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    title: Not("About #1")
});
SELECT * FROM "post" WHERE "title" != 'About #1'
import { LessThan } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    likes: LessThan(10)
});
SELECT * FROM "post" WHERE "likes" < 10
import { LessThanOrEqual } from "typeorm";
const loadedPosts = await connection.getRepository(Post).find({
    likes: LessThanOrEqual(10)
});
SELECT * FROM "post" WHERE "likes" <= 10
import { Between } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    likes: Between(1, 10)
});
SELECT * FROM "post" WHERE "likes" BETWEEN 1 AND 10
import { Like } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    title: Like("%out #%")
});
SELECT * FROM "post" WHERE "title" LIKE '%out #%'
SELECT * FROM "post" WHERE "title" ILIKE '%out #%'
import { In } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    title: In(["About #2", "About #3"])
});
SELECT * FROM "post" WHERE "title" IN ('About #2','About #3')
import { Any } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    title: Any(["About #2", "About #3"])
});
SELECT * FROM "post" WHERE "title" = ANY(['About #2','About #3'])
SELECT * FROM "post" WHERE "title" IS NULL
import { Raw } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    likes: Raw("1 + likes = 4")
});
SELECT * FROM "post" WHERE 1 + "likes" = 4

注意:注意 Raw 操作符。 它应该从提供的表达式执行纯 SQL,而不能包含用户输入,否则将导致 SQL 注入。

你还可以将这些运算符与 Not 运算符组合使用:

import { Not, MoreThan, Equal } from "typeorm";

const loadedPosts = await connection.getRepository(Post).find({
    likes: Not(MoreThan(10)),
    title: Not(Equal("About #2"))
});
SELECT * FROM "post" WHERE NOT("likes" > 10) AND NOT("title" = 'About #2')

原始查询

    async findOneProjectRecord({ date, projectName, recordType }: QueryOneProjectRecordDto) {
        const repository = this.getRepositoryByProject(projectName);
        const { recordTypeId } = await this.recordTypeRepository.findOne({
            where: { name: 'twitter_follower' },
        });
        const projectRecord = await repository.query(`
            SELECT
                '${date}' as date,
                GROUP_CONCAT(record ORDER BY member_id SEPARATOR ',') as records
            FROM canon_record.llss_seiyuu
            WHERE date = ?
            GROUP BY date;
        `, [date]);
        return projectRecord[0];
    }

通过占位符传入参数,多个占位符顺次接受数组

占位符参考:https://github.com/mysqljs/mysql#escaping-query-values

各个数据库有不同的规则

https://github.com/typeorm/typeorm/issues/881

Update

update - 通过给定的更新选项或实体 ID 部分更新实体

其实是 findAndUpdate

参数:

  1. criteria 查询参数,id,ids
  2. updateDto

返回值:

  1. 没看懂,怪怪的
await repository.update({ firstName: "Timber" }, { firstName: "Rizzrak" });
// 执行 UPDATE user SET firstName = Rizzrak WHERE firstName = Timber

await repository.update(1, { firstName: "Rizzrak" });
// 执行 UPDATE user SET firstName = Rizzrak WHERE id = 1

Increment

increment - 增加符合条件的实体某些列值。

await manager.increment(User, { firstName: "Timber" }, "age", 3);

Decrement

decrement - 减少符合条件的实体某些列值。

await manager.decrement(User, { firstName: "Timber" }, "age", 3);

Remove

remove - 删除给定的实体或实体数组。

它将删除单个事务中的所有给定实体(在实体的情况下,管理器不是事务性的)。

await repository.remove(user);
await repository.remove([category1, category2, category3]);

Delete

delete - 根据实体 id, ids 或给定的条件删除实体:

await repository.delete(1);
await repository.delete([1, 2, 3]);
await repository.delete({ firstName: "Timber" });

Clear

clear - 清除给定表中的所有数据 (truncates/drops)。

await repository.clear();

扩展 repository#

日志

开启日志

你只需在连接选项中设置 logging:true 即可启用所有查询和错误的记录:

{
    name: "mysql",
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "test",
    password: "test",
    database: "test",
    ...
    logging: true
}

日志选项

可以在连接选项中启用不同类型的日志记录:

{
    host: "localhost",
    ...
    logging: ["query", "error"]
}

如果要启用失败查询的日志记录,则只添加 error

{
    host: "localhost",
    ...
    logging: ["error"]
}

还可以使用其他选项:

你可以根据需要指定任意数量的选项。 如果要启用所有日志记录,只需指定 logging:“all”

记录耗时长的查询

如果遇到性能问题,可以通过在连接选项中设置 maxQueryExecutionTime 来记录执行时间过长的查询:

{
    host: "localhost",
    ...
    maxQueryExecutionTime: 1000
}

此代码将记录所有运行超过 1秒 的查询。

更改默认记录器

TypeORM 附带 4 种不同类型的记录器:

你可以在连接选项中启用其中任何一个:

{
    host: "localhost",
    ...
    logging: true,
    logger: "file"
}

使用自定义记录器

你可以通过实现 Logger 接口来创建自己的记录器类:

import { Logger } from 'typeorm';

export class MyCustomLogger implements Logger {
    // 实现logger类的所有方法
}

并在连接选项中指定它:

import { createConnection } from 'typeorm';
import { MyCustomLogger } from './logger/MyCustomLogger';

createConnection({
    name: 'mysql',
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'test',
    password: 'test',
    database: 'test',
    logger: new MyCustomLogger(),
});	

如果在 ormconfig 文件中定义了 然后你可以使用它并以下面的方式覆盖它:

import { createConnection, getConnectionOptions } from "typeorm";
import { MyCustomLogger } from "./logger/MyCustomLogger";

// getConnectionOptions将从ormconfig文件中读取选项并将其返回到connectionOptions对象中,
// 然后你只需向其附加其他属性
getConnectionOptions().then(connectionOptions => {
  return createConnection(
    Object.assign(connectionOptions, {
      logger: new MyCustomLogger()
    })
  );
});

记录器方法可接受 QueryRunner。 如果要记录其他数据将会很有帮助。 此外,通过查询运行程序,你可以访问在持久/删除期间传递的其他数据。 例如:

// 用户在实体保存期间发送请求
postRepository.save(post, { data: { request: request } });

// 在logger中你可以这样访问它:
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
    const requestUrl = queryRunner && queryRunner.data["request"] ? "(" + queryRunner.data["request"].url + ") " : "";
    console.log(requestUrl + "executing query: " + sql);
}