Vai al contenuto principale

Per iniziare

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

TypeORM è un ORM che può essere eseguito su piattaforme Node.js, Browser, Cordova, Ionic, React Native, NativeScript, Expo ed Electron e supporta TypeScript e JavaScript (ES2021).

Il suo obiettivo è supportare sempre le ultime funzionalità JavaScript e fornire caratteristiche aggiuntive che ti aiutano a sviluppare qualsiasi tipo di applicazione basata su database - dalle piccole applicazioni con poche tabelle fino a soluzioni enterprise su larga scala con database multipli.

TypeORM supporta più database di qualsiasi altro ORM JS/TS: Google Spanner, Microsoft SqlServer, MongoDB, MySQL/MariaDB, Oracle, Postgres, SAP HANA e SQLite, così come database derivati e driver diversi.

TypeORM supporta sia i pattern Active Record che Data Mapper, caratteristica unica tra gli ORM JavaScript attualmente esistenti, che ti permette di scrivere applicazioni di alta qualità, a basso accoppiamento, scalabili e mantenibili nel modo più produttivo.

TypeORM è fortemente influenzato da altri ORM come Hibernate, Doctrine ed Entity Framework.

Caratteristiche

  • Supporta sia DataMapper che ActiveRecord (a tua scelta).

  • Entità e colonne.

  • Tipi di colonna specifici per database.

  • Entity manager.

  • Repository e repository personalizzati.

  • Modello object-relational pulito.

  • Associazioni (relazioni).

  • Relazioni eager e lazy.

  • Relazioni unidirezionali, bidirezionali e autoreferenziate.

  • Supporta pattern di ereditarietà multipli.

  • Cascade.

  • Indici.

  • Transazioni.

  • Migrations con generazione automatica.

  • Connection pooling.

  • Replica.

  • Utilizzo di istanze multiple di database.

  • Lavorare con più tipi di database.

  • Query cross-database e cross-schema.

  • QueryBuilder con sintassi elegante, flessibile e potente.

  • Left join e inner join.

  • Paginazione corretta per query con join.

  • Cache delle query.

  • Streaming di risultati grezzi.

  • Logging.

  • Listener e subscriber (hook).

  • Supporta il pattern closure table.

  • Dichiarazione dello schema nei modelli o in file di configurazione separati.

  • Supporta MySQL / MariaDB / Postgres / CockroachDB / SQLite / Microsoft SQL Server / Oracle / SAP Hana / sql.js.

  • Supporta il database NoSQL MongoDB.

  • Funziona su piattaforme Node.js / Browser / Ionic / Cordova / React Native / NativeScript / Expo / Electron.

  • Supporto per TypeScript e JavaScript.

  • Supporto per ESM e CommonJS.

  • Il codice prodotto è performante, flessibile, pulito e mantenibile.

  • Segue tutte le best practice possibili.

  • CLI.

E altro ancora...

Con TypeORM, i tuoi modelli avranno questo aspetto:

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

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column()
firstName: string

@Column()
lastName: string

@Column()
age: number
}

E la tua logica di dominio apparirà così:

const userRepository = AppDataSource.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)

In alternativa, se preferisci usare l'implementazione ActiveRecord, puoi utilizzarla:

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
}

E la tua logica di dominio avrà questo aspetto:

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()

Installazione

  1. Installa il pacchetto npm:

    npm install typeorm

  2. Installa lo shim reflect-metadata:

    npm install reflect-metadata

    e importalo in un punto globale della tua applicazione (ad esempio in app.ts):

    import "reflect-metadata"

  3. Potresti dover installare le dichiarazioni di tipo per Node.js:

    npm install @types/node --save-dev

  4. Installa un driver per il database: consulta la documentazione per ogni driver specifico: mongodb, mssql, mysql/mariadb, oracle, postgres, sap, spanner, sqlite.

Configurazione di TypeScript

Assicurati anche di utilizzare TypeScript versione 4.5 o superiore, e di aver abilitato le seguenti impostazioni in tsconfig.json:

"emitDecoratorMetadata": true,
"experimentalDecorators": true,

Guida Rapida

Il modo più veloce per iniziare con TypeORM è utilizzare i comandi CLI per generare un progetto starter. La guida rapida funziona solo se usi TypeORM in un'applicazione Node.js. Per altre piattaforme, procedi con la guida passo-passo.

Per creare un nuovo progetto con la CLI, esegui:

npx typeorm init --name MyProject --database postgres

Dove name è il nome del progetto e database il database da usare. Il database può essere uno dei seguenti valori: mysql, mariadb, postgres, cockroachdb, sqlite, mssql, sap, spanner, oracle, mongodb, cordova, react-native, expo, nativescript.

Questo comando genererà un nuovo progetto nella directory MyProject con questi file:

MyProject
├── src // place of your TypeScript code
│ ├── entities // place where your entities (database models) are stored
│ │ └── User.ts // sample entity
│ ├── migrations // 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

Puoi eseguire typeorm init anche su progetti Node.js esistenti, ma con cautela: potrebbe sovrascrivere file esistenti.

Installa ora le dipendenze del progetto:

cd MyProject
npm install

Dopo aver installato le dipendenze, modifica data-source.ts inserendo le tue credenziali di connessione al database:

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: [],
})

Generalmente sarà sufficiente configurare: host, username, password, database e eventualmente port.

Completata la configurazione e installati i moduli, avvia l'applicazione:

npm start

Fatto! L'applicazione dovrebbe eseguirsi correttamente inserendo un nuovo utente nel database. Puoi ora espandere il progetto integrando altri moduli e creando nuove entità.

Per generare un progetto ESM esegui: npx typeorm init --name MyProject --database postgres --module esm

Per un progetto più avanzato con Express preinstallato: npx typeorm init --name MyProject --database mysql --express

Puoi generare un file docker-compose eseguendo il comando npx typeorm init --name MyProject --database postgres --docker.

Guida Passo-Passo

Cosa ti aspetti da un ORM? Innanzitutto, ti aspetti che crei le tabelle del database per te e trovi/inserisca/aggiorni/elimini i tuoi dati senza il fastidio di dover scrivere numerose query SQL difficili da mantenere. Questa guida ti mostrerà come configurare TypeORM da zero e fargli fare ciò che ti aspetti da un ORM.

Creare un modello

Lavorare con un database inizia con la creazione di tabelle. Come fai a dire a TypeORM di creare una tabella del database? La risposta è: attraverso i modelli. I modelli nella tua applicazione corrispondono alle tabelle del tuo database.

Ad esempio, hai un modello Photo:

export class Photo {
id: number
name: string
description: string
filename: string
views: number
isPublished: boolean
}

E vuoi memorizzare foto nel tuo database. Per memorizzare elementi nel database, prima hai bisogno di una tabella, e le tabelle del database vengono create dai tuoi modelli. Non tutti i modelli, ma solo quelli che definisci come entità.

Creare un'entità

Un'entità è il tuo modello decorato con un decoratore @Entity. Una tabella del database verrà creata per questi modelli. Lavori con le entità ovunque in TypeORM. Puoi caricarle/inserirle/aggiornarle/rimuoverle ed eseguire altre operazioni.

Trasformiamo il nostro modello Photo in un'entità:

import { Entity } from "typeorm"

@Entity()
export class Photo {
id: number
name: string
description: string
filename: string
views: number
isPublished: boolean
}

Ora verrà creata una tabella per l'entità Photo e potremo lavorarci ovunque nella nostra app. Abbiamo creato una tabella, ma quale tabella può esistere senza colonne? Creiamo alcune colonne nella nostra tabella del database.

Aggiungere colonne alla tabella

Per aggiungere colonne al database, devi decorare le proprietà di un'entità che vuoi trasformare in colonne con un decoratore @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
}

Ora le colonne id, name, description, filename, views e isPublished verranno aggiunte alla tabella photo. I tipi di colonna nel database sono dedotti dai tipi di proprietà usati, es. number diventa integer, string diventa varchar, boolean diventa bool, ecc. Ma puoi usare qualsiasi tipo di colonna supportato dal tuo database specificandolo esplicitamente nel decoratore @Column.

Abbiamo generato una tabella con colonne, ma manca un elemento fondamentale. Ogni tabella del database deve avere una colonna con chiave primaria.

Creare una colonna primaria

Ogni entità deve avere almeno una colonna di chiave primaria. È un requisito imprescindibile. Per rendere una colonna una chiave primaria, usa il decoratore @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
}

Creare una colonna auto-generata

Supponiamo ora che tu voglia una colonna id auto-generata (auto-incremento/sequenza/serial/identità generata). Per farlo, sostituisci il decoratore @PrimaryColumn con @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
}

Tipi di dati delle colonne

Ora sistemiamo i tipi di dati. Di default, le stringhe diventano varchar(255) (dipende dal database), i numeri diventano integer, ma non vogliamo colonne limitate solo a varchar o integer. Configuriamo i tipi di dati corretti:

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
}

I tipi di colonna sono specifici del database. Puoi impostare qualsiasi tipo supportato dal tuo DB. Maggiori informazioni sui tipi supportati sono disponibili qui.

Creare un nuovo DataSource

Ora che la nostra entità è creata, creiamo il file index.ts e configuriamo il 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
try {
await AppDataSource.initialize()
} catch (error) {
console.log(error)
}

In questo esempio utilizziamo Postgres, ma puoi scegliere qualsiasi altro database supportato. Per usare un database diverso, modifica il valore type nelle opzioni con il tipo di database che intendi usare: mysql, mariadb, postgres, cockroachdb, sqlite, mssql, oracle, sap, spanner, cordova, nativescript, react-native, expo o mongodb. Assicurati anche di utilizzare le tue impostazioni personalizzate per host, porta, username, password e database.

Abbiamo aggiunto la nostra entità Photo all'elenco delle entità per questa sorgente dati. Ogni entità che utilizzi nella connessione deve essere elencata qui.

L'impostazione synchronize garantisce che le tue entità vengano sincronizzate con il database ogni volta che esegui l'applicazione.

Esecuzione dell'applicazione

Ora, se esegui index.ts, verrà inizializzata una connessione al database e verrà creata una tabella per le tue foto.

+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(100) | |
| description | text | |
| filename | varchar(255) | |
| views | int | |
| isPublished | boolean | |
+-------------+--------------+----------------------------+

Creazione e inserimento di una foto nel database

Creiamo ora una nuova foto da salvare nel database:

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)

Dopo il salvataggio, l'entità otterrà un nuovo id generato automaticamente. Il metodo save restituisce un'istanza dello stesso oggetto che gli hai passato. Non è una nuova copia dell'oggetto, ma modifica il suo "id" e lo restituisce.

Utilizzo di Entity Manager

Abbiamo appena creato una nuova foto e l'abbiamo salvata nel database. Abbiamo usato EntityManager per salvarla. Con entity manager puoi manipolare qualsiasi entità nella tua app. Ad esempio, carichiamo l'entità appena salvata:

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 sarà un array di oggetti Photo con i dati caricati dal database.

Scopri di più su EntityManager.

Utilizzo dei Repository

Rifattorizziamo ora il codice utilizzando Repository invece di EntityManager. Ogni entità ha il proprio repository che gestisce tutte le operazioni relative a quell'entità. Quando lavori intensamente con le entità, i Repository sono più pratici da usare rispetto agli EntityManager:

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)

Scopri di più sui Repository qui.

Caricamento dal database

Proviamo altre operazioni di caricamento utilizzando il 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)

Aggiornamento nel database

Ora carichiamo una singola foto dal database, la modifichiamo e la salviamo:

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)

Ora la foto con id = 1 sarà aggiornata nel database.

Rimozione dal database

Rimuoviamo ora la nostra foto dal database:

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)

Ora la foto con id = 1 sarà rimossa dal database.

Creazione di una relazione uno-a-uno

Creiamo una relazione uno-a-uno con un'altra classe. Creiamo una nuova classe in PhotoMetadata.ts. Questa classe PhotoMetadata dovrebbe contenere i metadati aggiuntivi della nostra foto:

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
}

Qui utilizziamo un nuovo decoratore chiamato @OneToOne. Ci permette di creare una relazione uno-a-uno tra due entità. Aggiungiamo anche un decoratore @JoinColumn, che indica che questo lato della relazione ne sarà il proprietario. Le relazioni possono essere unidirezionali o bidirezionali. Solo un lato della relazione può essere proprietario. L'utilizzo del decoratore @JoinColumn è obbligatorio sul lato proprietario della relazione.

Se esegui l'app, vedrai una nuova tabella generata, che conterrà una colonna con una chiave esterna per la relazione con la foto:

+-------------+--------------+----------------------------+
| photo_metadata |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| height | int | |
| width | int | |
| comment | varchar(255) | |
| compressed | boolean | |
| orientation | varchar(255) | |
| photoId | int | FOREIGN KEY |
+-------------+--------------+----------------------------+

Salvataggio di una relazione uno-a-uno

Salviamo ora una foto e i suoi metadati, collegandoli tra loro.

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)

// The 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",
)

Lato inverso della relazione

Le relazioni possono essere unidirezionali o bidirezionali. Attualmente, la relazione tra PhotoMetadata e Photo è unidirezionale. Il proprietario della relazione è PhotoMetadata, e Photo non sa nulla di PhotoMetadata. Ciò rende complicato accedere a PhotoMetadata dal lato di Photo. Per risolvere questo problema, dovremmo aggiungere una relazione inversa e rendere bidirezionali le relazioni tra PhotoMetadata e Photo. Modifichiamo le nostre entità:

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 è una funzione che restituisce il nome del lato inverso della relazione. In questo caso, mostriamo che la proprietà metadata della classe Photo è dove memorizziamo PhotoMetadata nella classe Photo. Invece di passare una funzione che restituisce una proprietà della foto, potresti alternativamente passare una stringa al decoratore @OneToOne, come "metadata". Abbiamo usato questo approccio basato su funzioni per semplificare il refactoring.

Nota che dovremmo usare il decoratore @JoinColumn solo su un lato della relazione. Qualunque lato su cui posizioni questo decoratore sarà il lato proprietario (owning side) della relazione. Il lato proprietario di una relazione contiene una colonna con una chiave esterna nel database.

Relazioni nei progetti ESM

Se usi ESM nel tuo progetto TypeScript, dovresti usare il tipo wrapper Relation nelle proprietà di relazione per evitare problemi di dipendenze circolari. Modifichiamo le nostre entità:

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

Caricamento di oggetti con le loro relazioni

Ora carichiamo la nostra foto e i suoi metadati in una singola query. Ci sono due modi per farlo: usando i metodi find* o usando la funzionalità QueryBuilder. Usiamo prima il metodo find*. I metodi find* ti permettono di specificare un oggetto con l'interfaccia 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,
},
})

Qui, photos conterrà un array di foto dal database, e ogni foto includerà i suoi metadati. Maggiori informazioni sulle opzioni di ricerca in questa documentazione.

Usare le opzioni di ricerca è buono e semplicissimo, ma se hai bisogno di query più complesse, dovresti usare invece QueryBuilder. QueryBuilder permette di creare query più complesse in modo elegante:

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 permette la creazione e l'esecuzione di query SQL quasi di qualsiasi complessità. Quando lavori con QueryBuilder, pensa come se stessi creando una query SQL. In questo esempio, "photo" e "metadata" sono alias applicati alle foto selezionate. Usi gli alias per accedere a colonne e proprietà dei dati selezionati.

Utilizzo delle cascade per salvare automaticamente oggetti correlati

Possiamo impostare opzioni di cascade nelle nostre relazioni, nei casi in cui vogliamo che il nostro oggetto correlato venga salvato automaticamente quando l'altro oggetto viene salvato. Modifichiamo leggermente il nostro decoratore @OneToOne nella foto:

export class Photo {
// ... other columns

@OneToOne(() => PhotoMetadata, (metadata) => metadata.photo, {
cascade: true,
})
metadata: PhotoMetadata
}

Usare cascade ci permette di non salvare separatamente le foto e separatamente gli oggetti metadata. Ora possiamo semplicemente salvare un oggetto foto, e l'oggetto metadata verrà salvato automaticamente grazie alle opzioni di 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.")

Nota che ora impostiamo la proprietà metadata della foto, invece della proprietà photo del metadata come prima. La funzionalità cascade funziona solo se colleghi la foto ai suoi metadati dal lato della foto. Se impostassi il lato dei metadati, il metadata non verrebbe salvato automaticamente.

Creazione di una relazione many-to-one / one-to-many

Creiamo una relazione many-to-one/one-to-many. Supponiamo che una foto abbia un autore, e ogni autore possa avere molte foto. Prima, creiamo una classe 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 contiene il lato inverso di una relazione. OneToMany è sempre il lato inverso della relazione, e non può esistere senza ManyToOne sull'altro lato della relazione.

Ora aggiungiamo il lato proprietario della relazione nell'entità Photo:

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
}

Nelle relazioni many-to-one / one-to-many, il lato proprietario è sempre many-to-one. Ciò significa che la classe che usa @ManyToOne memorizzerà l'id dell'oggetto correlato.

Dopo aver eseguito l'applicazione, l'ORM creerà la tabella author:

+-------------+--------------+----------------------------+
| author |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
+-------------+--------------+----------------------------+

Modificherà anche la tabella photo, aggiungendo una nuova colonna author e creando una chiave esterna per essa:

+-------------+--------------+----------------------------+
| photo |
+-------------+--------------+----------------------------+
| id | int | PRIMARY KEY AUTO_INCREMENT |
| name | varchar(255) | |
| description | varchar(255) | |
| filename | varchar(255) | |
| isPublished | boolean | |
| authorId | int | FOREIGN KEY |
+-------------+--------------+----------------------------+

Creazione di una relazione molti-a-molti

Creiamo una relazione molti-a-molti. Supponiamo che una foto possa appartenere a molti album e che ogni album possa contenere molte foto. Creiamo una classe 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 è necessario per specificare che questo è il lato proprietario della relazione.

Ora aggiungiamo il lato inverso della relazione nella classe Photo:

export class Photo {
// ... other columns

@ManyToMany(() => Album, (album) => album.photos)
albums: Album[]
}

Dopo aver eseguito l'applicazione, l'ORM creerà una album_photos_photo_albums tabella di giunzione:

+-------------+--------------+----------------------------+
| album_photos_photo_albums |
+-------------+--------------+----------------------------+
| album_id | int | PRIMARY KEY FOREIGN KEY |
| photo_id | int | PRIMARY KEY FOREIGN KEY |
+-------------+--------------+----------------------------+

Ricorda di registrare la classe Album nella tua connessione dell'ORM:

const options: DataSourceOptions = {
// ... other options
entities: [Photo, PhotoMetadata, Author, Album],
}

Ora inseriamo album e foto nel nostro database:

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 conterrà:

{
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"
}]
}

Utilizzo di QueryBuilder

Puoi utilizzare QueryBuilder per creare query SQL di quasi qualsiasi complessità. Ad esempio:

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()

Questa query seleziona tutte le foto pubblicate con nomi "My" o "Mishka". Recupera i risultati a partire dalla posizione 5 (offset di paginazione) limitandosi a 10 risultati (limite di paginazione). I risultati saranno ordinati per ID in ordine decrescente. Gli album delle foto saranno uniti con LEFT JOIN e i loro metadati con INNER JOIN.

Utilizzerai frequentemente il query builder nella tua applicazione. Scopri di più su QueryBuilder qui.

Esempi pratici

Consulta gli esempi nella directory sample per vedere utilizzi concreti.

Ecco alcuni repository che puoi clonare per iniziare:

Estensioni

Esistono diverse estensioni che semplificano l'utilizzo di TypeORM e la sua integrazione con altri moduli:

Contributi

Scopri come contribuire qui e come configurare il tuo ambiente di sviluppo qui.

Questo progetto esiste grazie a tutte le persone che contribuiscono:

L'open source è impegnativo e richiede tempo. Se vuoi investire nel futuro di TypeORM, puoi diventare sponsor e permettere al nostro team di dedicare più tempo a miglioramenti e nuove funzionalità. Diventa sponsor

Diventa sponsor Gold e ricevi supporto tecnico premium dai nostri contributori principali. Diventa sponsor Gold