TypeOmm是一个可以在Nodejs,浏览器,Cordova,Phonegap,Ionic,React Native,NativeScript,Expo和Electron Platforms中运行的ORM,并且可以与Typescript和JavaScript(ES2021)一起使用。它的目标是始终支持最新的JavaScript功能,并提供其他功能,以帮助您开发使用数据库的任何类型的应用程序 - 从具有几个表格的小应用程序到具有多个数据库的大型企业应用程序。
与当前存在的所有其他JavaScript ORM不同,TypeOmm支持主动记录和数据映射器模式,这意味着您可以以最有生产力的方式编写高质量,松散,可扩展,可维护的应用程序。
TypeOmm受其他ORM的影响很大,例如冬眠,学说和实体框架。
公告:Typeorm的未来
我们很高兴分享我们对振兴的型号的愿景,这是一种旨在为长期建立稳定,健壮和可持续的基础的战略。了解我们如何结构维护并将专用资源汇总在一起,以确保Typeorm在未来几年中蓬勃发展。
阅读完整的公告
还有更多...
使用Typeorm,您的模型看起来像这样:
import { Entity , PrimaryGeneratedColumn , Column } from "typeorm"
@ Entity ( )
export class User {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
firstName : string
@ Column ( )
lastName : string
@ Column ( )
age : number
}
和您的域逻辑看起来像这样:
const userRepository = MyDataSource . getRepository ( User )
const user = new User ( )
user . firstName = "Timber"
user . lastName = "Saw"
user . age = 25
await userRepository . save ( user )
const allUsers = await userRepository . find ( )
const firstUser = await userRepository . findOneBy ( {
id : 1 ,
} ) // find by id
const timber = await userRepository . findOneBy ( {
firstName : "Timber" ,
lastName : "Saw" ,
} ) // find by firstName and lastName
await userRepository . 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 . findOneBy ( {
id : 1 ,
} )
const timber = await User . findOneBy ( {
firstName : "Timber" ,
lastName : "Saw"
} )
await timber . remove ( )
安装NPM软件包:
npm install typeorm --save
您需要安装reflect-metadata
垫片:
npm install reflect-metadata --save
并将其导入应用程序的全局位置(例如在app.ts
中):
import "reflect-metadata"
您可能需要安装节点键入:
npm install @types/node --save-dev
安装数据库驱动程序:
对于MySQL或Mariadb
npm install mysql --save
(您也可以安装mysql2
)
用于postgresql或cockroackdb
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 install @sap/hana-client
npm install hdb-pool
SAP HANA支持由Neptune软件的赞助成为可能。
对于Google Cloud Spanner
npm install @google-cloud/spanner --save
通过设置环境变量GOOGLE_APPLICATION_CREDENTIALS
来为您的应用程序代码提供身份验证凭证:
# Linux/macOS
export GOOGLE_APPLICATION_CREDENTIALS= " KEY_PATH "
# Windows
set GOOGLE_APPLICATION_CREDENTIALS=KEY_PATH
# Replace KEY_PATH with the path of the JSON file that contains your service account key.
要将Spanner与模拟器一起使用,请设置SPANNER_EMULATOR_HOST
环境变量:
# Linux/macOS
export SPANNER_EMULATOR_HOST=localhost:9010
# Windows
set SPANNER_EMULATOR_HOST=localhost:9010
对于蒙古(实验)
npm install mongodb@^5.2.0 --save
对于nimentscript ,反应本和科尔多瓦
检查支持平台的文档
仅安装其中一个,具体取决于您使用的数据库。
另外,请确保您正在使用Typescript版本4.5或更高版本,并且您已在tsconfig.json
中启用了以下设置:
"emitDecoratorMetadata" : true ,
"experimentalDecorators" : true ,
您可能还需要在编译器选项的lib
部分中启用es6
,或从@types
安装es6-shim
。
开始使用TypeOmm的最快方法是使用其CLI命令来生成一个入门项目。快速启动仅在nodejs应用程序中使用typeorm时才能起作用。如果您使用其他平台,请继续访问分步指南。
要使用CLI创建一个新项目,请运行以下命令:
npx typeorm init --name MyProject --database postgres
name
是项目的名称, database
是您使用的数据库。数据库可以是以下值之一: mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, sap
, spanner
,SPANNER, oracle
, mongodb
,MONGODB, cordova
,CORDOVA,REACT react-native
NATIVEN,REACT-NATICATION,EXPO, 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
│ ├── data-source.ts // data source and all connection configuration
│ └── index.ts // start point of your application
├── .gitignore // standard gitignore file
├── package.json // node module dependencies
├── README.md // simple readme file
└── tsconfig.json // TypeScript compiler options
您还可以在现有节点项目上运行
typeorm init
,但要小心 - 它可能会覆盖您已经拥有的某些文件。
下一步是安装新的项目依赖项:
cd MyProject
npm install
安装了所有依赖关系后,请编辑data-source.ts
文件,然后将您自己的数据库连接配置选项放在其中:
export const AppDataSource = new DataSource ( {
type : "postgres" ,
host : "localhost" ,
port : 5432 ,
username : "test" ,
password : "test" ,
database : "test" ,
synchronize : true ,
logging : true ,
entities : [ Post , Category ] ,
subscribers : [ ] ,
migrations : [ ] ,
} )
特别是在大多数情况下,您只需要配置host
, username
, password
, database
以及port
选项即可。
完成配置并安装了所有节点模块后,您就可以运行您的应用程序:
npm start
就是这样,您的应用程序应成功运行并将新用户插入数据库。您可以继续使用此项目,并整合所需的其他模块并开始创建更多实体。
您可以通过运行
npx typeorm init --name MyProject --database postgres --module esm
命令来生成ESM项目。
您可以通过运行
npx typeorm init --name MyProject --database mysql --express
命令来生成一个更高级的项目。
您可以通过运行
npx typeorm init --name MyProject --database postgres --docker
命令来生成docker-compose文件。
您对ORM有什么期望?首先,您期望它将为您创建数据库表,并查找 /插入 / update /删除数据,而无需编写很多几乎无法维护的SQL查询。本指南将向您展示如何从头开始设置Typeorm并使其完成您从ORM期望的事情。
使用数据库开始创建表。您如何告诉TypeOMM创建数据库表?答案是 - 通过模型。您的应用程序中的模型是您的数据库表。
例如,您有一个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
Decorator装饰要制成的实体属性即可。
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
to bool
等。但是,您可以通过将列类型明确指定到该数据库类型中的任何列类型支持中@Column
Decorator。
我们生成了一个带有列的数据库表,但是剩下一件事。每个数据库表必须具有带有主键的列。
每个实体必须至少具有一个主密钥列。这是一个要求,您无法避免。要使专栏成为主键,您需要使用@PrimaryColumn
Decorator。
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
Decorator:
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)类型(取决于数据库类型)。该数字映射到类似整数的类型(取决于数据库类型)。我们不希望所有列是有限的瓦尔查尔斯或整数。让我们设置正确的数据类型:
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
}
列类型是数据库特定的。您可以设置数据库支持的任何列类型。有关支持的列类型的更多信息,请参见此处。
DataSource
现在,当创建我们的实体时,让我们创建index.ts
文件并在此处设置我们的DataSource
:
import "reflect-metadata"
import { DataSource } from "typeorm"
import { Photo } from "./entity/Photo"
const AppDataSource = new DataSource ( {
type : "postgres" ,
host : "localhost" ,
port : 5432 ,
username : "root" ,
password : "admin" ,
database : "test" ,
entities : [ Photo ] ,
synchronize : true ,
logging : false ,
} )
// to initialize the initial connection with the database, register all entities
// and "synchronize" database schema, call "initialize()" method of a newly created database
// once in your application bootstrap
AppDataSource . initialize ( )
. then ( ( ) => {
// here you can start to work with your database
} )
. catch ( ( error ) => console . log ( error ) )
在此示例中,我们正在使用Postgres,但是您可以使用任何其他支持的数据库。要使用另一个数据库,只需将选项中的type
更改为您正在使用的数据库类型: mysql
, mariadb
, postgres
, cockroachdb
, sqlite
,sqlite, mssql
,oracle, oracle
, sap
, spanner
,spanner, cordova
,cordova,nativeScript, nativescript
,native-native,react-native, react-native
,exterative,exterative, expo
或mongodb
。还要确保使用自己的主机,端口,用户名,密码和数据库设置。
我们将照片实体添加到此数据源的实体列表中。您在连接中使用的每个实体都必须在此处列出。
设置synchronize
确保每次运行应用程序时,您的实体都将与数据库同步。
现在,如果您运行index.ts
,将初始化与数据库的连接,并创建一个用于照片的数据库表。
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
现在,让我们创建一张新照片以将其保存在数据库中:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const 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 AppDataSource . manager . save ( photo )
console . log ( "Photo has been saved. Photo id is" , photo . id )
保存您的实体后,它将获得新生成的ID。 save
方法返回您传递给它的相同对象的实例。它不是对象的新副本,它将修改其“ ID”并返回它。
我们刚刚创建了一张新照片并将其保存在数据库中。我们使用EntityManager
保存它。使用实体管理器您可以操纵应用程序中的任何实体。例如,让我们加载我们保存的实体:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const savedPhotos = await AppDataSource . manager . find ( Photo )
console . log ( "All photos from the db: " , savedPhotos )
savedPhotos
将是一系列照片对象,并具有从数据库中加载的数据。
在此处了解有关EntityManager的更多信息。
现在,让我们重构我们的代码,并使用Repository
代替EntityManager
。每个实体都有自己的存储库,可以处理所有操作及其实体。当您与实体打交道很多时,存储库的使用比EntityManagers更方便:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const 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
const photoRepository = AppDataSource . getRepository ( Photo )
await photoRepository . save ( photo )
console . log ( "Photo has been saved" )
const savedPhotos = await photoRepository . find ( )
console . log ( "All photos from the db: " , savedPhotos )
在此处了解有关存储库的更多信息。
让我们使用存储库尝试更多负载操作:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const allPhotos = await photoRepository . find ( )
console . log ( "All photos from the db: " , allPhotos )
const firstPhoto = await photoRepository . findOneBy ( {
id : 1 ,
} )
console . log ( "First photo from the db: " , firstPhoto )
const meAndBearsPhoto = await photoRepository . findOneBy ( {
name : "Me and Bears" ,
} )
console . log ( "Me and Bears photo from the db: " , meAndBearsPhoto )
const allViewedPhotos = await photoRepository . findBy ( { views : 1 } )
console . log ( "All viewed photos: " , allViewedPhotos )
const allPublishedPhotos = await photoRepository . findBy ( { isPublished : true } )
console . log ( "All published photos: " , allPublishedPhotos )
const [ photos , photosCount ] = await photoRepository . findAndCount ( )
console . log ( "All photos: " , photos )
console . log ( "Photos count: " , photosCount )
现在,让我们从数据库中加载一张照片,对其进行更新并保存:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const photoToUpdate = await photoRepository . findOneBy ( {
id : 1 ,
} )
photoToUpdate . name = "Me, my friends and polar bears"
await photoRepository . save ( photoToUpdate )
现在,带有id = 1
照片将在数据库中更新。
现在,让我们从数据库中删除我们的照片:
import { Photo } from "./entity/Photo"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const photoToRemove = await photoRepository . findOneBy ( {
id : 1 ,
} )
await photoRepository . remove ( photoToRemove )
现在,带有id = 1
照片将从数据库中删除。
让我们与另一类建立一对一的关系。让我们在PhotoMetadata.ts
中创建一个新类。该光最大的类应该包含我们照片的附加元信息:
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 ( ( ) => 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 { Photo } from "./entity/Photo"
import { PhotoMetadata } from "./entity/PhotoMetadata"
// create a photo
const 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
// create a photo metadata
const 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
const photoRepository = AppDataSource . getRepository ( Photo )
const metadataRepository = AppDataSource . 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 the relation between metadata and photo is created in the database too" ,
)
关系可以是单向或双向的。当前,我们在光载和照片之间的关系是单向的。关系的所有者是Photometadata,照片对Photmetadata一无所知。这使得从照片侧访问Photmetadata变得复杂。为了解决此问题,我们应该添加一个反关系,并在光度计和照片双向之间建立关系。让我们修改我们的实体:
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
JoinColumn ,
} from "typeorm"
import { Photo } from "./Photo"
@ Entity ( )
export class PhotoMetadata {
/* ... other columns */
@ OneToOne ( ( ) => 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 ( ( ) => PhotoMetadata , ( photoMetadata ) => photoMetadata . photo )
metadata : PhotoMetadata
}
photo => photo.metadata
是返回关系逆侧的名称的函数。在这里,我们证明了照片类的元数据属性是我们在照片类中存储Photometadata的地方。您可以简单地将字符串传递给@OneToOne
Decorator,例如"metadata"
,而不是传递返回照片属性的函数。但是我们使用这种功能型方法来使我们的重构更容易。
请注意,我们应该仅在关系的一侧使用@JoinColumn
装饰器。无论您将该装饰师放在哪个方面,都将是关系的自然一面。关系的自有侧包含数据库中带有外键的列。
如果您在打字稿项目中使用ESM,则应在关系属性中使用Relation
包装器类型,以避免循环依赖性问题。让我们修改我们的实体:
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
JoinColumn ,
Relation ,
} from "typeorm"
import { Photo } from "./Photo"
@ Entity ( )
export class PhotoMetadata {
/* ... other columns */
@ OneToOne ( ( ) => Photo , ( photo ) => photo . metadata )
@ JoinColumn ( )
photo : Relation < Photo >
}
import {
Entity ,
Column ,
PrimaryGeneratedColumn ,
OneToOne ,
Relation ,
} from "typeorm"
import { PhotoMetadata } from "./PhotoMetadata"
@ Entity ( )
export class Photo {
/* ... other columns */
@ OneToOne ( ( ) => PhotoMetadata , ( photoMetadata ) => photoMetadata . photo )
metadata : Relation < PhotoMetadata >
}
现在,让我们将照片及其照片元数据加载到一个查询中。有两种方法可以做到 - 使用find*
方法或使用QueryBuilder
功能。让我们首先使用find*
方法。 find*
方法允许您使用FindOneOptions
/ FindManyOptions
接口指定对象。
import { Photo } from "./entity/Photo"
import { PhotoMetadata } from "./entity/PhotoMetadata"
import { AppDataSource } from "./index"
const photoRepository = AppDataSource . getRepository ( Photo )
const photos = await photoRepository . find ( {
relations : {
metadata : true ,
} ,
} )
在这里,照片将包含数据库中的一系列照片,每张照片都包含其照片元数据。在此文档中了解有关查找选项的更多信息。
使用查找选项很简单,但很简单,但是如果您需要更复杂的查询,则应改用QueryBuilder
。 QueryBuilder
允许以优雅的方式使用更复杂的查询:
import { Photo } from "./entity/Photo"
import { PhotoMetadata } from "./entity/PhotoMetadata"
import { AppDataSource } from "./index"
const photos = await AppDataSource . getRepository ( Photo )
. createQueryBuilder ( "photo" )
. innerJoinAndSelect ( "photo.metadata" , "metadata" )
. getMany ( )
QueryBuilder
允许创建和执行几乎任何复杂性的SQL查询。当您使用QueryBuilder
时,请像创建SQL查询一样思考。在此示例中,“照片”和“元数据”是应用于选定照片的别名。您使用别名访问所选数据的列和属性。
我们可以在关系中设置级联选项,如果我们希望在保存另一个对象时保存相关对象。让我们稍微更改照片的@OneToOne
装饰仪:
export class Photo {
// ... other columns
@ OneToOne ( ( ) => PhotoMetadata , ( metadata ) => metadata . photo , {
cascade : true ,
} )
metadata : PhotoMetadata
}
使用cascade
使我们现在不得立即保存照片并分别保存元数据对象。现在,我们可以简单地保存一个照片对象,并且由于级联选项,将自动保存元数据对象。
import { AppDataSource } from "./index"
// create photo object
const 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
const 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
const photoRepository = AppDataSource . getRepository ( Photo )
// saving a photo also save the metadata
await photoRepository . save ( photo )
console . log ( "Photo is saved, photo metadata is saved too." )
请注意,我们现在设置了照片的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 ( ( ) => Photo , ( photo ) => photo . author ) // note: we will create author property in the Photo class below
photos : Photo [ ]
}
Author
包含关系的反面。 OneToMany
始终是关系的反面,并且在关系的另一侧没有ManyToOne
。
现在,让我们将关系的所有者侧添加到照片实体中:
import { Entity , Column , PrimaryGeneratedColumn , ManyToOne } from "typeorm"
import { PhotoMetadata } from "./PhotoMetadata"
import { Author } from "./Author"
@ Entity ( )
export class Photo {
/* ... other columns */
@ ManyToOne ( ( ) => 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 ( ( ) => Photo , ( photo ) => photo . albums )
@ JoinTable ( )
photos : Photo [ ]
}
需要@JoinTable
来指定这是关系的所有者方。
现在,让我们添加我们与Photo
类的关系的倒数:
export class Photo {
// ... other columns
@ ManyToMany ( ( ) => Album , ( album ) => album . photos )
albums : Album [ ]
}
运行应用程序后,ORM将创建一个prolem_photos_photo_albums enction表:
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
不要忘记在ORM中使用您的连接注册Album
类:
const options : DataSourceOptions = {
// ... other options
entities : [ Photo , PhotoMetadata , Author , Album ] ,
}
现在,让我们将相册和照片插入我们的数据库中:
import { AppDataSource } from "./index"
// create a few albums
const album1 = new Album ( )
album1 . name = "Bears"
await AppDataSource . manager . save ( album1 )
const album2 = new Album ( )
album2 . name = "Me"
await AppDataSource . manager . save ( album2 )
// create a few photos
const 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 AppDataSource . manager . save ( photo )
// now our photo is saved and albums are attached to it
// now lets load them:
const loadedPhoto = await AppDataSource . getRepository ( Photo ) . findOne ( {
where : {
id : 1 ,
} ,
relations : {
albums : true ,
} ,
} )
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构建几乎任何复杂性的SQL查询。例如,您可以做到这一点:
const photos = await AppDataSource . 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 ( )
此查询选择所有已发布的照片都以“我的”或“ Mishka”名称。它将从位置5(分页偏移)中选择结果,仅选择10个结果(分页限制)。选择结果将按照降序订购。相册将加入,他们的元数据将加入。
您将在您的应用程序中使用查询构建器。在此处了解有关QueryBuilder的更多信息。
查看样品中的样本以获取使用示例。
有一些存储库可以克隆并从:
有几种扩展可以简化使用TypeOmm并将其集成到其他模块:
data-source.ts
。relations
对象 - 类型摩托关系relations
在此处了解贡献以及如何在此处建立您的开发环境。
该项目的存在得益于所有贡献的人:
开源很难且耗时。如果您想投资Typeorm的未来,您可以成为赞助商,并让我们的核心团队在TypeOmm的改进和新功能上花费更多的时间。成为赞助商
成为黄金赞助商,并从我们的核心贡献者那里获得高级技术支持。成为黄金赞助商