- 版本:0.2.29
- GitHub: https://github.com/typeorm/typeorm
- NPM: https://npmjs.net.cn/package/%40nativescript-community%2Ftypeorm
- 下载
- 昨天:0
- 上周:0
- 上个月:0
TypeORM 是一个 ORM,可以在 NodeJS、浏览器、Cordova、PhoneGap、Ionic、React Native、NativeScript、Expo 和 Electron 平台上运行,并且可以使用 TypeScript 和 JavaScript (ES5、ES6、ES7、ES8)。其目标是始终支持最新的 JavaScript 功能,并提供有助于您开发任何类型的应用程序(从小型应用程序到大型企业级应用程序)的附加功能。
TypeORM 支持两种模式:活动记录 和 数据映射,与目前所有其他 JavaScript ORM 不同,这意味着您可以以最有效的方式编写高质量、松耦合、可扩展、可维护的应用程序。
TypeORM 受其他 ORM(如 Hibernate、Doctrine 和 Entity Framework)的影响很大。
功能
- 支持 数据映射 和 活动记录(任选其一)
- 实体和列
- 特定于数据库的列类型
- 实体管理器
- 仓库和自定义仓库
- 清晰的面向对象关系模型
- 关联(关系)
- eager 和 lazy 关联
- 单向、双向和自引用关联
- 支持多种继承模式
- 级联
- 索引
- 事务
- 迁移和自动迁移生成
- 连接池
- 复制
- 使用多个数据库连接
- 处理多种数据库类型
- 跨数据库和跨模式查询
- 优雅的语法、灵活且强大的 QueryBuilder
- 左连接和内连接
- 使用连接的查询的分页
- 查询缓存
- 流式处理原始结果
- 日志记录
- 监听器和订阅者(钩子)
- 支持闭包表模式
- 在模型或单独的配置文件中声明模式
- 以 json / xml / yml / env 格式配置连接
- 支持 MySQL / MariaDB / Postgres / CockroachDB / SQLite / Microsoft SQL Server / Oracle / SAP Hana / sql.js
- 支持 MongoDB NoSQL 数据库
- 在 NodeJS / 浏览器 / Ionic / Cordova / React Native / NativeScript / Expo / Electron 平台上运行
- 支持 TypeScript 和 JavaScript
- 生成的代码性能高、灵活、整洁且易于维护
- 遵循所有可能的最佳实践
- 命令行界面
更多...
使用 TypeORM,您的模型将如下所示
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
age: number;
}
您的领域逻辑如下所示
const repository = connection.getRepository(User);
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.age = 25;
await repository.save(user);
const allUsers = await repository.find();
const firstUser = await repository.findOne(1); // find by id
const timber = await repository.findOne({ firstName: "Timber", lastName: "Saw" });
await repository.remove(timber);
如果您更喜欢使用 ActiveRecord
实现,也可以使用它
import {Entity, PrimaryGeneratedColumn, Column, BaseEntity} from "typeorm";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
age: number;
}
您的领域逻辑将如下所示
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.age = 25;
await user.save();
const allUsers = await User.find();
const firstUser = await User.findOne(1);
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });
await timber.remove();
安装
-
安装 npm 包
npm install typeorm --save
-
您需要安装
reflect-metadata
模块npm install reflect-metadata --save
并将其导入应用程序的全局位置(例如在
app.ts
中)import "reflect-metadata";
-
你可能需要安装 node typings
npm install @types/node --save-dev
-
安装数据库驱动程序
-
用于 MySQL 或 MariaDB
npm install mysql --save
(你也可以安装mysql2
) -
用于 PostgreSQL 或 CockroachDB
npm install pg --save
-
用于 SQLite
npm install sqlite3 --save
-
用于 Microsoft SQL Server
npm install mssql --save
-
用于 sql.js
npm install sql.js --save
-
用于 Oracle
npm install oracledb --save
为了让 Oracle 驱动程序工作,你需要遵循其网站上的安装说明。他们的 网站。
-
用于 SAP Hana
npm config set @sap:registry https://npm.sap.com
npm i @sap/hana-client
npm i hdb-poolSAP Hana 支持由 Neptune Software 赞助。
-
用于 MongoDB(实验性)
npm install mongodb --save
-
用于 NativeScript、react-native 和 Cordova
查看 支持的平台文档
根据你使用的数据库,只安装其中之一。
-
TypeScript 配置
此外,请确保你使用的是 TypeScript 版本 3.3 或更高版本,并在 tsconfig.json
中启用了以下设置
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
你还可以在编译器选项的 lib
部分中启用 es6
,或从 @types
安装 es6-shim
。
快速入门
开始使用 TypeORM 的最快方式是使用其 CLI 命令来生成一个入门项目。快速入门只能在 NodeJS 应用程序中使用 TypeORM 的情况下工作。如果你使用的是其他平台,请转到 分步指南。
首先,全局安装 TypeORM
npm install typeorm -g
然后转到你想要创建新项目的目录,并运行以下命令
typeorm init --name MyProject --database mysql
其中 name
是你的项目名称,database
是你将要使用的数据库。数据库可以是以下值之一:mysql
、mariadb
、postgres
、cockroachdb
、sqlite
、mssql
、oracle
、mongodb
、cordova
、react-native
、expo
、nativescript
。
此命令将在 MyProject
目录中生成一个新的项目,包含以下文件
MyProject
├── src // place of your TypeScript code
│ ├── entity // place where your entities (database models) are stored
│ │ └── User.ts // sample entity
│ ├── migration // place where your migrations are stored
│ └── index.ts // start point of your application
├── .gitignore // standard gitignore file
├── ormconfig.json // ORM and database connection configuration
├── package.json // node module dependencies
├── README.md // simple readme file
└── tsconfig.json // TypeScript compiler options
你还可以在现有的 node 项目上运行
typeorm init
,但要小心 - 它可能会覆盖你已有的某些文件。
下一步是安装新项目的依赖项
cd MyProject
npm install
在安装过程中,编辑 ormconfig.json
文件,并将你自己的数据库连接配置选项放在那里
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "test",
"password": "test",
"database": "test",
"synchronize": true,
"logging": false,
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migration/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
]
}
特别是,你通常只需要配置 host
、username
、password
、database
以及可能 port
选项。
完成配置并安装所有节点模块后,你可以运行你的应用程序
npm start
就这样,你的应用程序应该成功运行并将在数据库中插入一个新的用户。你可以继续使用此项目并集成其他所需的模块,开始创建更多实体。
你可以通过运行
typeorm init --name MyProject --database mysql --express
命令生成一个安装了 express 的更高级的项目。
你可以通过运行
typeorm init --name MyProject --database postgres --docker
命令生成 docker-compose 文件。
分步指南
你对ORM有什么期待?首先,你期待它能为你创建数据库表,并且能让你轻松地进行数据的查找/插入/更新/删除,而不必编写大量难以维护的SQL查询。本指南将向您展示如何从头开始设置TypeORM,并使其满足你对ORM的期待。
创建一个模型
与数据库打交道的第一步是创建表。你如何让TypeORM创建数据库表?答案是——通过模型。你应用中的模型就是你的数据库表。
例如,你有一个Photo
模型
export class Photo {
id: number;
name: string;
description: string;
filename: string;
views: number;
isPublished: boolean;
}
你希望将照片存储到数据库中。要存储数据到数据库,首先需要一个数据库表,数据库表是由你的模型创建的。不是所有模型,只有那些你定义为实体的模型。
创建一个实体
实体是你的模型,通过@Entity
装饰器进行装饰。将为这样的模型创建数据库表。你可以在TypeORM的任何地方使用实体。你可以对它们进行加载/插入/更新/删除等操作。
让我们将我们的Photo
模型设为实体
import {Entity} from "typeorm";
@Entity()
export class Photo {
id: number;
name: string;
description: string;
filename: string;
views: number;
isPublished: boolean;
}
现在,将为Photo
实体创建数据库表,我们可以在应用中的任何地方与之交互。我们已经创建了数据库表,但是没有列的表能存在吗?让我们在我们的数据库表中创建一些列。
添加表列
要添加数据库列,你只需要用@Column
装饰器装饰你想成为列的实体属性。
import {Entity, Column} from "typeorm";
@Entity()
export class Photo {
@Column()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
现在,将添加id
、name
、description
、filename
、views
和isPublished
列到photo
表中。数据库中的列类型是从你使用的属性类型推断的,例如number
将被转换为integer
,string
转换为varchar
,boolean
转换为bool
等。但你可以通过在@Column
装饰器中显式指定列类型来使用数据库支持的任何列类型。
我们已生成了具有列的数据库表,但还有一件事要完成。每个数据库表都必须有一个具有主键的列。
创建主键列
每个实体必须至少有一个主键列。这是一个要求,你不能避免它。为了使列成为主键,你需要使用@PrimaryColumn
装饰器。
import {Entity, Column, PrimaryColumn} from "typeorm";
@Entity()
export class Photo {
@PrimaryColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
创建自动生成的列
现在,假设你想你的id列是自动生成的(这被称为自动递增/序列/序列号/生成标识列)。为了做到这一点,你需要将@PrimaryColumn
装饰器更改为@PrimaryGeneratedColumn
装饰器
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
description: string;
@Column()
filename: string;
@Column()
views: number;
@Column()
isPublished: boolean;
}
列数据类型
接下来,让我们固定我们的数据类型。默认情况下,字符串映射到类似于varchar(255)的类型(取决于数据库类型)。数字映射到类似于integer的类型(取决于数据库类型)。我们不希望所有列都是受限的varchar或integer。让我们设置正确的数据类型
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column({
length: 100
})
name: string;
@Column("text")
description: string;
@Column()
filename: string;
@Column("double")
views: number;
@Column()
isPublished: boolean;
}
列类型是数据库特定的。你可以设置数据库支持的任何列类型。有关支持的列类型的更多信息,请参阅这里。
创建数据库连接
现在,当我们的实体创建完成后,让我们创建一个index.ts
(或app.ts
,无论你叫什么)文件,并在其中设置我们的连接
import "reflect-metadata";
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "test",
entities: [
Photo
],
synchronize: true,
logging: false
}).then(connection => {
// here you can start to work with your entities
}).catch(error => console.log(error));
在这个例子中,我们使用MySQL,但你可以使用任何其他支持的数据库。要使用其他数据库,只需将选项中的type
更改为你要使用的数据库类型:mysql
、mariadb
、postgres
、cockroachdb
、sqlite
、mssql
、oracle
、cordova
、nativescript
、react-native
、expo
或mongodb
。同时,请确保使用你自己的主机、端口、用户名、密码和数据库名设置。
我们将我们的Photo实体添加到了此连接的实体列表中。你在这个连接中使用到的每个实体都必须在那里列出。
设置synchronize
确保每次运行应用程序时,你的实体都将与数据库同步。
从目录中加载所有实体
稍后,当我们创建更多实体时,我们需要将它们添加到我们的配置中的实体中。这很不方便,因此我们可以设置整个目录,从这个目录中所有实体都将连接并在我们的连接中使用。
import {createConnection} from "typeorm";
createConnection({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
password: "admin",
database: "test",
entities: [
__dirname + "/entity/*.js"
],
synchronize: true,
}).then(connection => {
// here you can start to work with your entities
}).catch(error => console.log(error));
但请注意这种方法。如果你使用ts-node
,则需要指定.ts
文件的路径。如果你使用outDir
,则需要指定outDir目录中的.js
文件路径。如果你使用outDir
并且删除或重命名你的实体,请确保清除outDir
目录并重新编译你的项目,因为当你删除源.ts
文件时,它们的编译后的.js
版本不会从输出目录中删除,并且仍然会被TypeORM加载,因为它们存在于outDir
目录中。
运行应用程序
现在,如果你运行你的index.ts
,将初始化数据库连接并为你的照片创建一个数据库表。
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
创建并插入照片到数据库中
现在让我们创建一个新的照片以将其保存到数据库中
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(connection => {
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
return connection.manager
.save(photo)
.then(photo => {
console.log("Photo has been saved. Photo id is", photo.id);
});
}).catch(error => console.log(error));
一旦你的实体被保存,它将获得一个新的生成id。《save》方法返回一个与传递给它的相同对象的实例。它不是对象的新副本,它修改其“id”并返回它。
使用async/await语法
让我们利用最新的ES8(ES2017)功能,并使用async/await语法
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
await connection.manager.save(photo);
console.log("Photo has been saved");
}).catch(error => console.log(error));
使用实体管理器
我们刚刚创建了一个新的照片并将其保存到数据库中。我们使用了EntityManager
来保存它。使用实体管理器,你可以操作你应用中的任何实体。例如,让我们加载我们保存的实体
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let savedPhotos = await connection.manager.find(Photo);
console.log("All photos from the db: ", savedPhotos);
}).catch(error => console.log(error));
savedPhotos
将是一个包含从数据库加载数据的Photo对象的数组。
了解更多关于EntityManager的信息这里。
使用仓库
现在让我们重构我们的代码,并使用Repository
而不是EntityManager
。每个实体都有自己的仓库,负责处理与其实体的所有操作。当你大量处理实体时,仓库比实体管理器更方便使用。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1;
photo.isPublished = true;
let photoRepository = connection.getRepository(Photo);
await photoRepository.save(photo);
console.log("Photo has been saved");
let savedPhotos = await photoRepository.find();
console.log("All photos from the db: ", savedPhotos);
}).catch(error => console.log(error));
了解更多关于仓库的信息这里。
从数据库加载数据
让我们尝试使用仓库进行更多加载操作
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let allPhotos = await photoRepository.find();
console.log("All photos from the db: ", allPhotos);
let firstPhoto = await photoRepository.findOne(1);
console.log("First photo from the db: ", firstPhoto);
let meAndBearsPhoto = await photoRepository.findOne({ name: "Me and Bears" });
console.log("Me and Bears photo from the db: ", meAndBearsPhoto);
let allViewedPhotos = await photoRepository.find({ views: 1 });
console.log("All viewed photos: ", allViewedPhotos);
let allPublishedPhotos = await photoRepository.find({ isPublished: true });
console.log("All published photos: ", allPublishedPhotos);
let [allPhotos, photosCount] = await photoRepository.findAndCount();
console.log("All photos: ", allPhotos);
console.log("Photos count: ", photosCount);
}).catch(error => console.log(error));
在数据库中更新
现在让我们从数据库中加载单个照片,更新它并保存它
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let photoToUpdate = await photoRepository.findOne(1);
photoToUpdate.name = "Me, my friends and polar bears";
await photoRepository.save(photoToUpdate);
}).catch(error => console.log(error));
现在数据库中的照片id = 1
将被更新。
从数据库中删除
现在让我们从数据库中删除我们的照片
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
createConnection(/*...*/).then(async connection => {
/*...*/
let photoToRemove = await photoRepository.findOne(1);
await photoRepository.remove(photoToRemove);
}).catch(error => console.log(error));
现在数据库中的照片id = 1
将被删除。
创建一对一关系
让我们与另一个类创建一个一对一关系。让我们在PhotoMetadata.ts
中创建一个新的类。这个PhotoMetadata类应该包含我们照片的附加元信息
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Photo} from "./Photo";
@Entity()
export class PhotoMetadata {
@PrimaryGeneratedColumn()
id: number;
@Column("int")
height: number;
@Column("int")
width: number;
@Column()
orientation: string;
@Column()
compressed: boolean;
@Column()
comment: string;
@OneToOne(type => Photo)
@JoinColumn()
photo: Photo;
}
这里,我们使用了一个名为 @OneToOne
的新装饰器。它允许我们在两个实体之间创建一对一关系。type => Photo
是一个函数,返回我们想要建立关系的实体类。由于语言的具体规定,我们被迫使用返回类的函数,而不是直接使用类。我们也可以写成 () => Photo
,但为了提高代码可读性,我们使用 type => Photo
作为约定。类型变量本身不包含任何内容。
我们还添加了 @JoinColumn
装饰器,它表示关系的这一侧将拥有关系。关系可以是单向的或双向的。关系的一侧只能拥有。在关系的拥有侧使用 @JoinColumn
装饰器是必须的。
如果您运行应用程序,您将看到一个新创建的表,它将包含一个用于照片关系的外键列
+-------------+--------------+----------------------------+
| photo_metadata |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| height | int(11) | |
| width | int(11) | |
| comment | varchar(255) | |
| compressed | boolean | |
| orientation | varchar(255) | |
| photoId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
保存一对一关系
现在让我们保存一个照片,其元数据和将它们关联起来。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
import {PhotoMetadata} from "./entity/PhotoMetadata";
createConnection(/*...*/).then(async connection => {
// create a photo
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.isPublished = true;
// create a photo metadata
let metadata = new PhotoMetadata();
metadata.height = 640;
metadata.width = 480;
metadata.compressed = true;
metadata.comment = "cybershoot";
metadata.orientation = "portrait";
metadata.photo = photo; // this way we connect them
// get entity repositories
let photoRepository = connection.getRepository(Photo);
let metadataRepository = connection.getRepository(PhotoMetadata);
// first we should save a photo
await photoRepository.save(photo);
// photo is saved. Now we need to save a photo metadata
await metadataRepository.save(metadata);
// done
console.log("Metadata is saved, and relation between metadata and photo is created in the database too");
}).catch(error => console.log(error));
关系的一方
关系可以是单向的或双向的。目前,PhotoMetadata 和 Photo 之间的关系是单向的。关系拥有者是 PhotoMetadata,而 Photo 对 PhotoMetadata 一无所知。这使得从 Photo 方面访问 PhotoMetadata 变得复杂。为了解决这个问题,我们应该添加一个反向关系,并将 PhotoMetadata 和 Photo 之间的关系改为双向。让我们修改我们的实体
import {Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn} from "typeorm";
import {Photo} from "./Photo";
@Entity()
export class PhotoMetadata {
/* ... other columns */
@OneToOne(type => Photo, photo => photo.metadata)
@JoinColumn()
photo: Photo;
}
import {Entity, Column, PrimaryGeneratedColumn, OneToOne} from "typeorm";
import {PhotoMetadata} from "./PhotoMetadata";
@Entity()
export class Photo {
/* ... other columns */
@OneToOne(type => PhotoMetadata, photoMetadata => photoMetadata.photo)
metadata: PhotoMetadata;
}
photo => photo.metadata
是一个函数,它返回关系的反向一侧的名称。在这里,我们显示 Photo 类的 metadata 属性是我们在 Photo 类中存储 PhotoMetadata 的地方。您可以选择不传递返回照片属性的函数,而是简单地传递一个字符串到 @OneToOne
装饰器,如 "metadata"
。但为了使重构更容易,我们使用了这种函数类型的方法。
注意,我们应该只在关系的一侧使用 @JoinColumn
装饰器。无论您将此装饰器放在哪一侧,它都将成为关系的拥有侧。关系的拥有侧包含数据库中的外键列。
加载具有其关系的对象
现在让我们在单个查询中加载我们的照片及其照片元数据。有两种方法可以做到这一点 - 使用 find*
方法或使用 QueryBuilder
功能。让我们首先使用 find*
方法。 find*
方法允许您指定具有 FindOneOptions
/ FindManyOptions
接口的对象。
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
import {PhotoMetadata} from "./entity/PhotoMetadata";
createConnection(/*...*/).then(async connection => {
/*...*/
let photoRepository = connection.getRepository(Photo);
let photos = await photoRepository.find({ relations: ["metadata"] });
}).catch(error => console.log(error));
在这里,photos 将包含来自数据库的照片数组,并且每张照片都将包含其照片元数据。有关 Find 选项的更多信息,请参阅 此文档。
使用 find 选项很好,而且非常简单,但如果您需要更复杂的查询,您应该使用 QueryBuilder
。 QueryBuilder
允许以优雅的方式使用更复杂的查询
import {createConnection} from "typeorm";
import {Photo} from "./entity/Photo";
import {PhotoMetadata} from "./entity/PhotoMetadata";
createConnection(/*...*/).then(async connection => {
/*...*/
let photos = await connection
.getRepository(Photo)
.createQueryBuilder("photo")
.innerJoinAndSelect("photo.metadata", "metadata")
.getMany();
}).catch(error => console.log(error));
QueryBuilder
允许创建和执行几乎任何复杂性的 SQL 查询。当您使用 QueryBuilder
时,请想象您正在创建一个 SQL 查询。在这个例子中,“photo”和“metadata”是应用于所选照片的别名。您使用别名来访问所选数据的列和属性。
使用级联以自动保存相关对象
我们可以设置关系中的级联选项,在需要我们的相关对象在另一个对象保存时保存的情况下。让我们稍微改变一下我们照片的 @OneToOne
装饰器
export class Photo {
/// ... other columns
@OneToOne(type => PhotoMetadata, metadata => metadata.photo, {
cascade: true,
})
metadata: PhotoMetadata;
}
使用 cascade
允许我们不再分别保存照片和元数据对象。现在我们可以简单地保存照片对象,由于级联选项,元数据对象将被自动保存。
createConnection(options).then(async connection => {
// create photo object
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.isPublished = true;
// create photo metadata object
let metadata = new PhotoMetadata();
metadata.height = 640;
metadata.width = 480;
metadata.compressed = true;
metadata.comment = "cybershoot";
metadata.orientation = "portrait";
photo.metadata = metadata; // this way we connect them
// get repository
let photoRepository = connection.getRepository(Photo);
// saving a photo also save the metadata
await photoRepository.save(photo);
console.log("Photo is saved, photo metadata is saved too.")
}).catch(error => console.log(error));
请注意,我们现在设置照片的 metadata
属性,而不是之前的元数据的 photo
属性。只有当您从照片的一侧将照片与其元数据连接时,cascade
功能才会工作。如果您设置元数据的一侧,则元数据不会自动保存。
创建多对一/一对多关系
让我们创建一个多对一/一对多关系。假设一张照片有一个作者,每个作者可以有多个照片。首先,让我们创建一个 Author
类
import {Entity, Column, PrimaryGeneratedColumn, OneToMany, JoinColumn} from "typeorm";
import {Photo} from "./Photo";
@Entity()
export class Author {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(type => Photo, photo => photo.author) // note: we will create author property in the Photo class below
photos: Photo[];
}
Author
包含关系的反向一侧。 OneToMany
总是关系的反向一侧,并且没有关系另一侧的 ManyToOne
是无法存在的。
现在让我们将关系的所有者一侧添加到 Photo 实体中
import {Entity, Column, PrimaryGeneratedColumn, ManyToOne} from "typeorm";
import {PhotoMetadata} from "./PhotoMetadata";
import {Author} from "./Author";
@Entity()
export class Photo {
/* ... other columns */
@ManyToOne(type => Author, author => author.photos)
author: Author;
}
在多对一/一对多关系中,所有者一侧总是多对一。这意味着使用 @ManyToOne
的类将存储相关对象的 id。
运行应用程序后,ORM 将创建 author
表
+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
它还会修改 photo
表,添加一个新的 author
列并为它创建外键
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
创建多对多关系
让我们创建一个多对一/多对多关系。假设一张照片可以属于多个相册,每个相册也可以包含多个照片。让我们创建一个 Album
类
import {Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable} from "typeorm";
@Entity()
export class Album {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToMany(type => Photo, photo => photo.albums)
@JoinTable()
photos: Photo[];
}
@JoinTable
是必需的,用于指定这是关系的所有者一侧。
现在让我们将关系的反向一侧添加到 Photo
类中
export class Photo {
/// ... other columns
@ManyToMany(type => Album, album => album.photos)
albums: Album[];
}
运行应用程序后,ORM 将创建一个名为 album_photos_photo_albums 的 联合表
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
不要忘记在 ORM 中将 Album
类与您的连接注册
const options: ConnectionOptions = {
// ... other options
entities: [Photo, PhotoMetadata, Author, Album]
};
现在让我们将相册和照片插入到我们的数据库中
let connection = await createConnection(options);
// create a few albums
let album1 = new Album();
album1.name = "Bears";
await connection.manager.save(album1);
let album2 = new Album();
album2.name = "Me";
await connection.manager.save(album2);
// create a few photos
let photo = new Photo();
photo.name = "Me and Bears";
photo.description = "I am near polar bears";
photo.filename = "photo-with-bears.jpg";
photo.views = 1
photo.isPublished = true
photo.albums = [album1, album2];
await connection.manager.save(photo);
// now our photo is saved and albums are attached to it
// now lets load them:
const loadedPhoto = await connection
.getRepository(Photo)
.findOne(1, { relations: ["albums"] });
loadedPhoto
将等于
{
id: 1,
name: "Me and Bears",
description: "I am near polar bears",
filename: "photo-with-bears.jpg",
albums: [{
id: 1,
name: "Bears"
}, {
id: 2,
name: "Me"
}]
}
使用 QueryBuilder
您可以使用 QueryBuilder 构建几乎所有复杂性的 SQL 查询。例如,您可以这样做
let photos = await connection
.getRepository(Photo)
.createQueryBuilder("photo") // first argument is an alias. Alias is what you are selecting - photos. You must specify it.
.innerJoinAndSelect("photo.metadata", "metadata")
.leftJoinAndSelect("photo.albums", "album")
.where("photo.isPublished = true")
.andWhere("(photo.name = :photoName OR photo.name = :bearName)")
.orderBy("photo.id", "DESC")
.skip(5)
.take(10)
.setParameters({ photoName: "My", bearName: "Mishka" })
.getMany();
此查询选择所有发布且名称为 "My" 或 "Mishka" 的照片。它将选择从位置 5(分页偏移)开始的结果,并选择仅 10 个结果(分页限制)。选择结果将按 id 降序排列。照片的相册将左连接,其元数据将内连接。
您将在应用程序中大量使用查询构建器。有关 QueryBuilder 的更多信息,请参阅 此处。
示例
请参阅 sample 中的示例,以了解使用示例。
有一些仓库您可以克隆并开始使用
- 如何使用 TypeScript 与 TypeORM 一起使用的示例
- 如何使用 JavaScript 与 TypeORM 一起使用的示例
- 如何使用 JavaScript 和 Babel 与 TypeORM 一起使用的示例
- 如何在浏览器中使用 TypeScript 和 SystemJS 与 TypeORM 一起使用的示例
- 如何使用 Express 与 TypeORM 一起使用的示例
- 如何使用 Koa 与 TypeORM 一起使用的示例
- 如何使用 TypeORM 与 MongoDB 一起使用的示例
- 如何在 Cordova/PhoneGap 应用程序中使用 TypeORM 的示例
- 如何在 Ionic 应用程序中使用 TypeORM 的示例
- 如何在 React Native 应用程序中使用 TypeORM 的示例
- 如何在 Nativescript-Vue 应用程序中使用 TypeORM 的示例
- 如何在 Nativescript-Angular 应用程序中使用 TypeORM 的示例
- 如何使用 JavaScript 在 Electron 中使用 TypeORM 的示例
- 如何使用 TypeScript 在 Electron 中使用 TypeORM 的示例
扩展
有几个扩展简化了使用 TypeORM 和将其与其他模块集成的工作
- TypeORM + GraphQL 框架
- TypeORM 与 TypeDI 集成
- TypeORM 与 routing-controllers 集成
- 从现有数据库生成模型 - typeorm-model-generator
- 固定值加载器 - typeorm-fixtures-cli
贡献
本项目的存在离不开所有贡献者
赞助商
开源项目既困难又耗时。如果您想投资TypeORM的未来,您可以成为赞助商,让我们的核心团队有更多时间专注于TypeORM的改进和新功能。成为赞助商
金牌赞助商
成为金牌赞助商,并获得我们核心贡献者的高级技术支持。成为金牌赞助商