Typorm ist ein ORM, das in NodeJS-, Browser-, Cordova-, PhoneGap-, Ionic-, React Native-, Nativescript-, Expo- und Elektronenplattformen ausgeführt werden kann und mit TypeScript und JavaScript (ES2021) verwendet werden kann. Sein Ziel ist es, die neuesten JavaScript -Funktionen immer zu unterstützen und zusätzliche Funktionen bereitzustellen, mit denen Sie jede Art von Anwendung entwickeln können, die Datenbanken verwendet - von kleinen Anwendungen mit wenigen Tabellen bis hin zu großen Unternehmensanwendungen mit mehreren Datenbanken.
TypOrM unterstützt sowohl aktive Datensatz- als auch Data Mapper-Muster, im Gegensatz zu allen anderen derzeit vorhandenen JavaScript-Ormen. Dies bedeutet, dass Sie hochwertige, locker gekoppelte, skalierbare und wartbare Anwendungen auf produktivste Weise schreiben können.
Typorm wird stark von anderen Ormen beeinflusst, z. B. Hibernate, Doctrine und Entity Framework.
Ankündigung: Die Zukunft von Typorm
Wir freuen uns, unsere Vision für ein revitalisiertes Typorm zu teilen - eine Strategie, die sich auf den Aufbau einer stabilen, robusten und nachhaltigen Grundlage für langfristig konzentriert. Erfahren Sie, wie wir die Wartung strukturieren und engagierte Ressourcen zusammenbringen, um sicherzustellen, dass Typorme für die kommenden Jahre gedeiht.
Lesen Sie die vollständige Ankündigung
Und mehr ...
Mit Typorm sehen Ihre Modelle so aus:
import { Entity , PrimaryGeneratedColumn , Column } from "typeorm"
@ Entity ( )
export class User {
@ PrimaryGeneratedColumn ( )
id : number
@ Column ( )
firstName : string
@ Column ( )
lastName : string
@ Column ( )
age : number
}
Und Ihre Domain -Logik sieht so aus:
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 )
Wenn Sie es vorziehen, die Implementierung ActiveRecord
zu verwenden, können Sie sie auch verwenden:
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
}
Und Ihre Domain -Logik sieht so aus:
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 ( )
Installieren Sie das NPM -Paket:
npm install typeorm --save
Sie müssen reflect-metadata
shim installieren:
npm install reflect-metadata --save
und importieren Sie es irgendwo am globalen Ort Ihrer App (zum Beispiel in app.ts
):
import "reflect-metadata"
Möglicherweise müssen Sie Knoten -Typierungen installieren:
npm install @types/node --save-dev
Installieren Sie einen Datenbanktreiber:
für MySQL oder Mariadb
npm install mysql --save
(Sie können stattdessen auch mysql2
installieren)
für PostgreSQL oder Cockroachdb
npm install pg --save
für SQLite
npm install sqlite3 --save
Für Microsoft SQL Server
npm install mssql --save
für Sql.js
npm install sql.js --save
für Oracle
npm install oracledb --save
Um den Oracle -Treiber zum Laufen zu bringen, müssen Sie die Installationsanweisungen von ihrer Website von ihrer Website befolgen.
für SAP Hana
npm install @sap/hana-client
npm install hdb-pool
SAP HANA -Support ermöglicht durch das Sponsoring der Neptune -Software.
Für Google Cloud -Schritte
npm install @google-cloud/spanner --save
Geben Sie Ihrem Anwendungscode Authentifizierungsanmeldeinformationen an, indem Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS
festlegen:
# 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.
Um Spanner mit dem Emulator zu verwenden, sollten Sie die Umgebungsvariable SPANNER_EMULATOR_HOST
festlegen:
# Linux/macOS
export SPANNER_EMULATOR_HOST=localhost:9010
# Windows
set SPANNER_EMULATOR_HOST=localhost:9010
für MongoDB (experimentell)
npm install mongodb@^5.2.0 --save
für Nativescript , React-Native und Cordova
Überprüfen Sie die Dokumentation unterstützter Plattformen
Installieren Sie nur eine davon, je nachdem, welche Datenbank Sie verwenden.
Stellen Sie außerdem sicher, dass Sie TypeScript -Version 4.5 oder höher verwenden und die folgenden Einstellungen in tsconfig.json
aktiviert haben:
"emitDecoratorMetadata" : true ,
"experimentalDecorators" : true ,
Möglicherweise müssen Sie es6
auch im Abschnitt lib
der Compiler-Optionen aktivieren oder es6-shim
von @types
installieren.
Der schnellste Weg, um mit Typorm zu beginnen, besteht darin, seine CLI -Befehle zu verwenden, um ein Starterprojekt zu generieren. Schnellstart funktioniert nur, wenn Sie TypOrM in einer NodeJS -Anwendung verwenden. Wenn Sie andere Plattformen verwenden, fahren Sie mit der Schritt-für-Schritt-Anleitung fort.
Führen Sie den folgenden Befehl aus, um ein neues Projekt mit CLI zu erstellen:
npx typeorm init --name MyProject --database postgres
Wo name
der Name Ihres Projekts und database
ist, ist die Datenbank, die Sie verwenden. Die Datenbank kann einer der folgenden Werte sein: mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, sap
, spanner
, oracle
, mongodb
, cordova
, react-native
, expo
, nativescript
.
Dieser Befehl generiert ein neues Projekt im MyProject
-Verzeichnis mit den folgenden Dateien:
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
Sie können auch
typeorm init
in einem vorhandenen Knotenprojekt ausführen, aber seien Sie vorsichtig - es kann einige Dateien überschreiben, die Sie bereits haben.
Der nächste Schritt besteht darin, neue Projektabhängigkeiten zu installieren:
cd MyProject
npm install
Nachdem Sie alle Abhängigkeiten installiert haben, bearbeiten Sie die Datei data-source.ts
und geben Sie dort Ihre eigenen Datenbankverbindungskonfigurationsoptionen ein:
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 : [ ] ,
} )
Insbesondere müssen Sie in den meisten Fällen nur host
, username
, password
, database
und möglicherweise port
konfigurieren.
Sobald Sie mit der Konfiguration fertig sind und alle Knotenmodule installiert sind, können Sie Ihre Anwendung ausführen:
npm start
Das war es, dass Ihre Anwendung erfolgreich ausgeführt und einen neuen Benutzer in die Datenbank einfügen. Sie können weiterhin mit diesem Projekt arbeiten und andere Module integrieren, die Sie benötigen, und mehr Entitäten zu erstellen.
Sie können ein ESM -Projekt generieren, indem Sie
npx typeorm init --name MyProject --database postgres --module esm
-Befehl ausführen.
Sie können ein noch erweiterteres Projekt mit Express erstellen, indem Sie
npx typeorm init --name MyProject --database mysql --express
-Befehl ausführen.
Sie können eine Docker-Compose-Datei generieren, indem Sie
npx typeorm init --name MyProject --database postgres --docker
Befehl ausführen.
Was erwarten Sie von Orm? Zunächst erwarten Sie, dass Datenbanktabellen für Sie erstellt werden und Ihre Daten finden / einfügen / aktualisieren / löschen / löschen, ohne dass viele kaum pflegende SQL -Abfragen schreiben müssen. In dieser Anleitung wird angezeigt, wie Sie TypeMorM von Grund auf neu einrichten und das tun, was Sie von einem ORM erwarten.
Die Arbeit mit einer Datenbank beginnt mit dem Erstellen von Tabellen. Wie sagen Sie Typororm, eine Datenbanktabelle zu erstellen? Die Antwort lautet - durch die Modelle. Ihre Modelle in Ihrer App sind Ihre Datenbanktabellen.
Zum Beispiel haben Sie ein Photo
:
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
Und Sie möchten Fotos in Ihrer Datenbank speichern. Um Dinge in der Datenbank zu speichern, benötigen Sie zunächst eine Datenbanktabelle, und Datenbanktabellen werden aus Ihren Modellen erstellt. Nicht alle Modelle, sondern nur diejenigen, die Sie als Entitäten definieren.
Entity ist Ihr Modell, das von einem @Entity
Decorator dekoriert ist. Für solche Modelle wird eine Datenbanktabelle erstellt. Sie arbeiten überall in TypeOrM mit Einheiten. Sie können andere Vorgänge laden/einfügen/aktualisieren/entfernen und mit ihnen ausführen.
Machen wir unser Photo
zu einer Entität:
import { Entity } from "typeorm"
@ Entity ( )
export class Photo {
id : number
name : string
description : string
filename : string
views : number
isPublished : boolean
}
Jetzt wird eine Datenbanktabelle für die Photo
erstellt, und wir werden in unserer App überall mit ihr arbeiten. Wir haben jedoch eine Datenbanktabelle erstellt. Welche Tabelle kann jedoch ohne Spalten existieren? Erstellen wir in unserer Datenbanktabelle einige Spalten.
Um Datenbankspalten hinzuzufügen, müssen Sie lediglich die Eigenschaften einer Entität dekorieren, die Sie in eine Spalte mit einem @Column
-Dekorateur machen möchten.
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
}
Jetzt werden id
, name
, description
, filename
, views
und isPublished
Spalten zur photo
hinzugefügt. Spaltentypen in bool
Datenbank boolean
string
varchar
von Ihnen verwendeten Eigenschaftstypen abgeleitet number
Die integer
. @Column
Dekorateur.
Wir haben eine Datenbanktabelle mit Spalten generiert, aber eine Sache ist übrig. Jede Datenbanktabelle muss eine Spalte mit einem Primärschlüssel haben.
Jede Entität muss mindestens eine Primärschlüsselspalte haben. Dies ist eine Anforderung und Sie können es nicht vermeiden. Um eine Spalte zu einem Primärschlüssel zu machen, müssen Sie den Dekorator @PrimaryColumn
verwenden.
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
}
Nehmen wir nun an, Sie möchten, dass Ihre ID-Spalte automatisch generiert wird (dies wird als automatische Inkrement- / Sequenz- / Serienn- / Erzeugungs-Identitätsspalte bezeichnet). Dazu müssen Sie den @PrimaryColumn
Decorator in einen @PrimaryGeneratedColumn
Decorator ändern:
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
}
Lassen Sie uns als nächstes unsere Datentypen beheben. Standardmäßig wird die Zeichenfolge einem VARCHAR (255) -ähnlichen Typ zugeordnet (abhängig vom Datenbanktyp). Die Zahl wird einem ganzzahlartigen Typ abgebildet (abhängig vom Datenbankarts). Wir möchten nicht, dass alle unsere Spalten begrenzt Varchars oder Ganzzahlen sind. Richten wir die richtigen Datentypen ein:
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
}
Spaltentypen sind datenbankspezifisch. Sie können jeden Spalten Typ Ihre Datenbank -Unterstützung festlegen. Weitere Informationen zu unterstützten Spaltentypen finden Sie hier.
DataSource
Wenn unsere Entität erstellt wird, erstellen wir nun in die Datei index.ts
und erstellen Sie unsere DataSource
dort:
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 ) )
Wir verwenden Postgres in diesem Beispiel, Sie können jedoch jede andere unterstützte Datenbank verwenden. Um eine andere Datenbank zu verwenden, ändern Sie einfach den type
in den Optionen in den von Ihnen verwendeten Datenbanktyp: mysql
, mariadb
, postgres
, cockroachdb
, sqlite
, mssql
, oracle
, sap
, spanner
, cordova
, nativescript
, react-native
, expo
oder mongodb
. Stellen Sie außerdem sicher, dass Sie Ihren eigenen Host-, Port-, Benutzernamen-, Passwort- und Datenbankeinstellungen verwenden.
Wir haben unsere Fotoentität in die Liste der Entitäten für diese Datenquelle hinzugefügt. Jede Entität, die Sie in Ihrer Verbindung verwenden, muss dort aufgeführt sein.
Durch das Einstellen von synchronize
stellt sicher, dass Ihre Entitäten jedes Mal, wenn Sie die Anwendung ausführen, mit der Datenbank synchronisiert werden.
Wenn Sie jetzt Ihren index.ts
ausführen, wird eine Verbindung mit der Datenbank initialisiert und eine Datenbanktabelle für Ihre Fotos erstellt.
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int(11) | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+
Erstellen wir nun ein neues Foto, um es in der Datenbank zu speichern:
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 )
Sobald Ihr Unternehmen gespeichert ist, wird eine neu generierte ID erhalten. save
gibt eine Instanz desselben Objekts zurück, das Sie an sie übergeben. Es ist keine neue Kopie des Objekts, es ändert seine "ID" und gibt es zurück.
Wir haben gerade ein neues Foto erstellt und es in der Datenbank gespeichert. Wir haben EntityManager
verwendet, um es zu speichern. Mit Entity Manager können Sie jede Entität in Ihrer App manipulieren. Lassen Sie uns zum Beispiel unsere gespeicherte Einheit laden:
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
ist ein Array von Fotobjekten mit den aus der Datenbank geladenen Daten.
Erfahren Sie hier mehr über EntityManager.
Lassen Sie uns nun unseren Code neu auffüllen und Repository
anstelle von EntityManager
verwenden. Jedes Unternehmen hat ein eigenes Repository, das alle Operationen mit seiner Entität abwickelt. Wenn Sie viel mit Unternehmen zu tun haben, sind Repositories bequemer zu verwenden als 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 )
Erfahren Sie hier mehr über Repository.
Versuchen wir mehr Ladevorgänge mit dem Repository:
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 )
Laden wir nun ein einzelnes Foto aus der Datenbank, aktualisieren Sie es und speichern Sie es:
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 )
Jetzt wird ein Foto mit id = 1
in der Datenbank aktualisiert.
Entfernen wir nun unser Foto aus der Datenbank:
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 )
Jetzt wird ein Foto mit id = 1
aus der Datenbank entfernt.
Lassen Sie uns eine Eins-zu-Eins-Beziehung zu einer anderen Klasse erstellen. Lassen Sie uns eine neue Klasse in PhotoMetadata.ts
erstellen. Diese Photometadata-Klasse soll die zusätzliche Meta-Information unseres Fotos enthalten:
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
}
Hier verwenden wir einen neuen Dekorateur namens @OneToOne
. Es ermöglicht uns, eine Eins-zu-Eins-Beziehung zwischen zwei Einheiten zu schaffen. type => Photo
ist eine Funktion, die die Klasse der Entität zurückgibt, mit der wir unsere Beziehung herstellen möchten. Wir sind gezwungen, eine Funktion zu verwenden, die eine Klasse zurückgibt, anstatt die Klasse aufgrund der Sprachspezifikationen direkt zu verwenden. Wir können es auch als () => Photo
schreiben, aber wir verwenden type => Photo
als Konvention, um die Code -Lesbarkeit zu erhöhen. Die Typvariable selbst enthält nichts.
Wir fügen auch einen @JoinColumn
Decorator hinzu, der darauf hinweist, dass diese Seite der Beziehung die Beziehung besitzt. Beziehungen können unidirektional oder bidirektional sein. Nur eine Seite des Verwandten kann besitzen. Auf der Eigentümerseite der Beziehung ist @JoinColumn
Decorator erforderlich.
Wenn Sie die App ausführen, sehen Sie eine neu generierte Tabelle und enthält eine Spalte mit einem Fremdkasten für die Fotobeziehung:
+-------------+--------------+----------------------------+
| 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 |
+-------------+--------------+----------------------------+
Lassen Sie uns nun ein Foto und seine Metadaten speichern und sie aneinander anbringen.
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" ,
)
Beziehungen können unidirektional oder bidirektional sein. Derzeit ist unsere Beziehung zwischen Photometadaten und Foto unidirektional. Der Besitzer der Beziehung ist Photometadata, und das Foto weiß nichts über Photometadata. Dies macht es kompliziert, von der Fotoseite von der Fotoseite auf Fotometadaten zuzugreifen. Um dieses Problem zu beheben, sollten wir eine umgekehrte Beziehung hinzufügen und Beziehungen zwischen Fotometadaten und foto -bidirektionalem Zusammenhang herstellen. Lassen Sie uns unsere Entitäten ändern:
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
ist eine Funktion, die den Namen der inversen Seite der Beziehung zurückgibt. Hier zeigen wir, dass in der Metadateneigenschaft der Fotokasse Photometadata in der Fotoklasse speichern. Anstatt eine Funktion zu übergeben, die eine Eigenschaft des Fotos zurückgibt, können Sie alternativ einfach eine Zeichenfolge an @OneToOne
Decorator wie "metadata"
übergeben. Aber wir haben diesen funktionsübergreifenden Ansatz verwendet, um unser Refactoring zu erleichtern.
Beachten Sie, dass wir den @JoinColumn
Decorator nur auf einer Seite einer Beziehung verwenden sollten. Unabhängig von der Seite, die Sie diesen Dekorateur anziehen, ist die besitzende Seite der Beziehung. Die Besitzseite einer Beziehung enthält eine Spalte mit einem Fremdschlüssel in der Datenbank.
Wenn Sie ESM in Ihrem TypeScript -Projekt verwenden, sollten Sie den Relation
-Wrapper -Typ in Bezug auf Eigenschaften verwenden, um Probleme mit kreisförmiger Abhängigkeit zu vermeiden. Lassen Sie uns unsere Entitäten ändern:
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 >
}
Laden wir jetzt unser Foto und seine Fotometadaten in einer einzigen Frage. Es gibt zwei Möglichkeiten, dies zu tun - mithilfe von find*
-Methoden oder mit der Funktionen QueryBuilder
. Verwenden wir zuerst find*
-Methode. find*
Methoden ermöglichen es Ihnen, ein Objekt mit der Schnittstelle FindOneOptions
/ FindManyOptions
anzugeben.
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 ,
} ,
} )
Hier enthalten Fotos eine Reihe von Fotos aus der Datenbank, und jedes Foto enthält seine Fotometadaten. Weitere Informationen zu Optionen finden Sie in dieser Dokumentation.
Die Verwendung von Fundoptionen ist gut und tot einfach, aber wenn Sie eine komplexere Abfrage benötigen, sollten Sie stattdessen QueryBuilder
verwenden. QueryBuilder
können komplexere Fragen auf elegante Weise verwendet werden:
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
ermöglicht die Erstellung und Ausführung von SQL -Abfragen fast jeder Komplexität. Wenn Sie mit QueryBuilder
zusammenarbeiten, denken Sie, dass Sie eine SQL -Abfrage erstellen. In diesem Beispiel werden "Foto" und "Metadaten" Aliase auf ausgewählte Fotos angewendet. Sie verwenden Aliase, um auf Spalten und Eigenschaften der ausgewählten Daten zugreifen zu können.
Wir können Kaskadenoptionen in unseren Beziehungen einrichten, in den Fällen, in denen unser zugehöriges Objekt gespeichert wird, wenn das andere Objekt gespeichert wird. Wechseln Sie @OneToOne
-Dekorator unseres Fotos ein bisschen:
export class Photo {
// ... other columns
@ OneToOne ( ( ) => PhotoMetadata , ( metadata ) => metadata . photo , {
cascade : true ,
} )
metadata : PhotoMetadata
}
Durch die Verwendung von cascade
können wir nicht separat Fotos speichern und Metadatenobjekte separat speichern. Jetzt können wir einfach ein Fotoobjekt speichern, und das Metadatenobjekt wird aufgrund von Kaskadenoptionen automatisch gespeichert.
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." )
Beachten Sie, dass wir jetzt die metadata
des Fotos anstelle der photo
-Eigenschaft der Metadaten wie zuvor festlegen. Die cascade
-Funktion funktioniert nur, wenn Sie das Foto von der Seite des Fotos mit seinen Metadaten verbinden. Wenn Sie die Metadatenseite einstellen, wird die Metadaten nicht automatisch gespeichert.
Lassen Sie uns eine viel zu eins/eins-zu-Viele-Beziehung herstellen. Angenommen, ein Foto hat einen Autor, und jeder Autor kann viele Fotos haben. Lassen Sie uns zunächst eine Author
erstellen:
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
enthält eine umgekehrte Seite einer Beziehung. OneToMany
ist immer eine umgekehrte Seite der Beziehung, und es kann nicht ohne ManyToOne
auf der anderen Seite der Beziehung existieren.
Fügen wir nun die Eigentümerseite der Beziehung in die Fotoeinheit hinzu:
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
}
In vielen Beziehungen zwischen und eins und eins, ist die Eigentümerseite immer viel zu eins. Dies bedeutet, dass die Klasse, die @ManyToOne
verwendet, die ID des zugehörigen Objekts speichert.
Nachdem Sie die Bewerbung ausgeführt haben, erstellt das ORM die author
-Tabelle:
+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+
Es wird auch die photo
ändern, eine neue Spalte author
hinzugefügt und einen Fremdschlüssel dafür erstellt:
+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int(11) | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int(11) | FOREIGN KEY |
+-------------+--------------+----------------------------+
Lassen Sie uns eine viel zu viele Beziehung herstellen. Nehmen wir an, ein Foto kann in vielen Alben sein, und jedes Album kann viele Fotos enthalten. Lassen Sie uns eine Album
erstellen:
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
ist erforderlich, um anzugeben, dass dies die Eigentümerseite der Beziehung ist.
Fügen wir nun die umgekehrte Seite unserer Beziehung zur Photo
hinzu:
export class Photo {
// ... other columns
@ ManyToMany ( ( ) => Album , ( album ) => album . photos )
albums : Album [ ]
}
Nachdem Sie die Anwendung ausgeführt haben, erstellt das ORM eine Tabelle des Junction -Junction -Tanks .
+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int(11) | PRIMARY KEY FOREIGN KEY |
| photo_id | int(11) | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+
Vergessen Sie nicht, die Album
mit Ihrer Verbindung im ORM zu registrieren:
const options : DataSourceOptions = {
// ... other options
entities : [ Photo , PhotoMetadata , Author , Album ] ,
}
Lassen Sie uns nun Alben und Fotos in unsere Datenbank einfügen:
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
entspricht:
{
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"
} ]
}
Sie können QueryBuilder verwenden, um SQL -Abfragen fast jeder Komplexität zu erstellen. Zum Beispiel können Sie dies tun:
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 ( )
Diese Abfrage wählt alle veröffentlichten Fotos mit "My" oder "Mishka" -Namen aus. Es wird die Ergebnisse aus Position 5 (Pagination Offset) ausgewählt und nur 10 Ergebnisse (Paginationsgrenze) ausgewählt. Das Auswahlergebnis wird in absteigender Reihenfolge per ID bestellt. Die Fotoalben werden beigetreten und ihre Metadaten werden innen zusammen sein.
Sie werden den Query -Builder in Ihrer Anwendung häufig verwenden. Erfahren Sie hier mehr über QueryBuilder.
Schauen Sie sich die Proben in der Probe an, um Beispiele für die Nutzung zu erhalten.
Es gibt ein paar Repositorys, mit denen Sie klonen und beginnen können:
Es gibt verschiedene Erweiterungen, die die Arbeit mit Typorm vereinfachen und in andere Module integrieren:
data-source.ts
nach Generierung von Migrationen/Entitäten-TypeMOrM-CodeBase-Syncrelations
- Typormenbeziehungenrelations
basierend auf einer GraphQL-Abfrage-Typorm-Relations-Graphql Erfahren Sie hier über den Beitrag und wie Sie Ihre Entwicklungsumgebung hier einrichten.
Dieses Projekt besteht dank aller Personen, die einen Beitrag leisten:
Open Source ist hart und zeitaufwändig. Wenn Sie in die Zukunft von Typorm investieren möchten, können Sie Sponsor werden und unser Kernteam mehr Zeit für die Verbesserungen und die neuen Funktionen von Typorm verbringen. Sponsor werden
Werden Sie Goldsponsor und erhalten Sie von unseren Kernvertretern eine erstklassige technische Unterstützung. Goldensponsor werden