Vai al contenuto principale
Traduzione Beta Non Ufficiale

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

Aggiornamento da 0.3 a 1.0

Questa è la guida per l'aggiornamento dalla versione 0.3.x a 1.0.

Aggiornamento automatizzato

Il pacchetto @typeorm/codemod può automatizzare la maggior parte delle modifiche di breaking change descritte in questa guida:

npx @typeorm/codemod v1 src/

Questo aggiornerà il tuo codice sul posto — usa --dry per visualizzare in anteprima le modifiche senza applicarle. Il codemod gestisce rinomine degli import, sostituzioni di API, sintassi delle opzioni di ricerca, aggiornamenti delle dipendenze e altro. Le modifiche che non possono essere automatizzate vengono lasciate come commenti TODO per la revisione manuale.

Consulta il README del codemod per l'utilizzo completo e le opzioni.

Requisiti di piattaforma

Node.js 20+

Il target JavaScript minimo è ora ES2023, che richiede Node.js 20 o successivo. Se stai utilizzando una versione precedente di Node.js, effettua l'aggiornamento prima di aggiornare TypeORM.

Buffer sostituito con Uint8Array su piattaforme non Node

Il polyfill del browser Buffer è stato rimosso. Su piattaforme non Node (browser, Deno, Bun), i dati binari sono ora rappresentati come Uint8Array. Gli utenti Node.js non sono interessati — il Buffer di Node estende Uint8Array e continua a funzionare come prima.

Modifiche ai driver

MySQL / MariaDB

Opzione connectorPackage rimossa

L'opzione connectorPackage è stata rimossa, insieme al supporto per il vecchio client mysql. L'unico client database supportato ora è mysql2, che TypeORM tenterà di caricare per impostazione predefinita. Se stavi utilizzando mysql nel tuo progetto, sostituiscilo semplicemente con mysql2.

Comportamento predefinito di legacySpatialSupport cambiato in false

L'opzione legacySpatialSupport ora è predefinito false, il che significa che TypeORM utilizza le funzioni spaziali conformi allo standard ST_GeomFromText e ST_AsText introdotte in MySQL 5.7 e richieste da MySQL 8.0+. Le funzioni legacy GeomFromText e AsText sono state rimosse in MySQL 8.0.

Se stai utilizzando MySQL 5.6 o versioni precedenti e fai affidamento sui tipi spaziali, imposta esplicitamente legacySpatialSupport: true:

new DataSource({
type: "mysql",
legacySpatialSupport: true,
// ...
})

Opzioni di colonna width e zerofill rimosse

MySQL 8.0.17 ha deprecato la larghezza di visualizzazione per i tipi interi e l'attributo ZEROFILL, e MySQL 8.4 li ha rimossi completamente. TypeORM non supporta più le opzioni width e zerofill per le colonne. Se le stavi utilizzando, rimuovile dalle definizioni delle colonne:

// Before
@Column({ type: "int", width: 9, zerofill: true })
postCode: number

// After
@Column({ type: "int" })
postCode: number

Se hai bisogno di formattazione visuale con riempimento zero, gestiscila nel livello applicativo utilizzando String.prototype.padStart() o la funzione MySQL LPAD() in una query raw. L'opzione unsigned per i tipi interi non è interessata da questa modifica.

SQLite

Il pacchetto sqlite3 è stato abbandonato. Utilizza invece better-sqlite3:

// Before
new DataSource({
type: "sqlite",
database: "db.sqlite",
})

// After
new DataSource({
type: "better-sqlite3",
database: "db.sqlite",
})

Opzione flags rimossa

Il pacchetto sqlite3 accettava flag di apertura a livello C (OPEN_URI, OPEN_SHAREDCACHE, ecc.). better-sqlite3 non supporta questa funzionalità — utilizza invece le opzioni dedicate:

  • readonly per la modalità di sola lettura

  • enableWAL per la modalità journal WAL

Opzione busyTimeout rinominata in timeout

Il pacchetto sqlite3 utilizzava busyTimeout per configurare il timeout di occupato di SQLite. better-sqlite3 utilizza invece timeout (predefinito: 5000ms):

// Before
new DataSource({
type: "sqlite",
database: "db.sqlite",
busyTimeout: 2000,
})

// After
new DataSource({
type: "better-sqlite3",
database: "db.sqlite",
timeout: 2000,
})

MongoDB

TypeORM ora richiede il driver Node.js mongodb v7 o successivo (^7.0.0). Il supporto per i driver mongodb v5/v6 è stato rimosso.

Opzioni di connessione deprecate rimosse

Sono state rimosse le seguenti opzioni di connessione MongoDB:

Removed optionAction
appnameUse appName (camelCase) instead
fsyncUse writeConcern: { journal: true } instead
jUse writeConcern: { journal: true } instead
keepAliveRemove — always enabled since MongoDB Driver v6.0
keepAliveInitialDelayRemove — not configurable since MongoDB Driver v6.0
sslUse tls instead
sslCAUse tlsCAFile instead
sslCRLRemove — no replacement in modern driver
sslCertUse tlsCertificateKeyFile instead
sslKeyUse tlsCertificateKeyFile instead
sslPassUse tlsCertificateKeyFilePassword instead
sslValidateUse tlsAllowInvalidCertificates (inverted) instead
useNewUrlParserRemove — no-op since MongoDB Driver v4.0
useUnifiedTopologyRemove — no-op since MongoDB Driver v4.0
wUse writeConcern: { w: 1 } instead
wtimeoutUse writeConcern: { wtimeoutMS: 2500 } instead
wtimeoutMSUse writeConcern: { wtimeoutMS: 2500 } instead

Metodo stats() rimosso

Il metodo stats() è stato rimosso da MongoQueryRunner, MongoEntityManager e MongoRepository. Il comando sottostante collStats è stato deprecato nel server MongoDB 6.2 e il metodo Collection.stats() è stato rimosso nel driver MongoDB v7.

Utilizza invece lo stadio di aggregazione $collStats. Nota che la struttura della risposta è diversa — proprietà come count, size e storageSize sono nidificate sotto storageStats invece che al livello principale.

// Before
const stats = await mongoRepository.stats()
console.log(stats.count)
console.log(stats.size)
console.log(stats.totalIndexSize)

// After — use $collStats aggregation stage
const [stats] = await dataSource.mongoManager
.aggregate(MyEntity, [{ $collStats: { storageStats: {} } }])
.toArray()
console.log(stats.storageStats.count)
console.log(stats.storageStats.size)
console.log(stats.storageStats.totalIndexSize)

Funzioni globali getMongoRepository e getMongoManager rimosse

Le funzioni globali deprecate getMongoRepository() e getMongoManager() sono state rimosse. Utilizza invece i corrispondenti metodi delle istanze su DataSource o EntityManager:

// Before
import { getMongoManager, getMongoRepository } from "typeorm"

const manager = getMongoManager()
const repository = getMongoRepository(User)

// After
const manager = dataSource.mongoManager
const repository = dataSource.getMongoRepository(User)

Tipi

I tipi MongoDB interni (ObjectId, ecc.) non sono più riesportati da typeorm. Importali direttamente da mongodb:

// Before
import { ObjectId } from "typeorm"

// After
import { ObjectId } from "mongodb"

MS SQL Server

Opzione di connessione domain rimossa

L'opzione deprecata domain in SqlServerConnectionCredentialsOptions è stata rimossa. Utilizza invece l'opzione authentication con tipo NTLM:

// Before
new DataSource({
type: "mssql",
domain: "MYDOMAIN",
username: "user",
password: "pass",
// ...
})

// After
new DataSource({
type: "mssql",
authentication: {
type: "ntlm",
options: {
domain: "MYDOMAIN",
userName: "user",
password: "pass",
},
},
// ...
})

options.isolation e options.connectionIsolationLevel

L'opzione options.isolation in SqlServerDataSourceOptions è stata rinominata in options.isolationLevel poiché non era corretta in primo luogo. Inoltre, nota che il formato del valore è cambiato da READ_COMMITTED a READ COMMITTED (trattino basso sostituito da spazio) per allinearsi al formato atteso utilizzato da TypeORM nell'intero codice. Aggiorna di conseguenza le opzioni del DataSource:

// Before
new DataSource({
type: "mssql",
options: {
isolation: "READ_COMMITTED",
connectionIsolationLevel: "READ_COMMITTED",
// ...
},
// ...
})

// After
new DataSource({
type: "mssql",
options: {
isolationLevel: "READ COMMITTED",
connectionIsolationLevel: "READ COMMITTED",
// ...
},
// ...
})

SAP HANA

Sono stati rimossi diversi alias deprecati per le connessioni SAP HANA.

  • hanaClientDriver è stato rimosso. Utilizza driver.

  • pool.max è stato rimosso. Utilizza pool.maxConnectedOrPooled.

  • pool.requestTimeout è stato rimosso. Utilizza pool.maxWaitTimeoutIfPoolExhausted.

  • pool.idleTimeout è stato rimosso. Utilizza pool.maxPooledIdleTime (in secondi).

  • pool.min, pool.maxWaitingRequests e pool.checkInterval sono stati rimossi senza sostituti.

Nota anche i cambiamenti nel comportamento predefinito della configurazione del pool:

  • pool.maxPooledIdleTime ora è predefinito 30 secondi e non utilizza più fallback su pool.idleTimeout.

  • pool.maxWaitTimeoutIfPoolExhausted ora è predefinito 0 e non utilizza più fallback su pool.requestTimeout.

Expo

Il supporto per il driver SQLite legacy di Expo è stato rimosso. L'API legacy è stata rimossa da Expo nell'SDK v52. Aggiorna a Expo SDK v52 o successivo e utilizza la moderna API asincrona di SQLite:

// Before
new DataSource({
type: "expo",
database: "db.sqlite",
})

// After — use Expo SDK v52+ with the modern async API
new DataSource({
type: "expo",
database: "db.sqlite",
driver: require("expo-sqlite"),
})

Redis (cache)

Rimosso il supporto per i client Redis legacy (v3) in RedisQueryResultCache. Aggiorna a Redis client v4 o successivo (redis, ioredis):

// Before — redis v3
import { createClient } from "redis"
const client = createClient()

// After — redis v4+
import { createClient } from "redis"
const client = createClient()
await client.connect()

Data Source

ConnectionDataSource

DataSource ha sostituito Connection nella v0.3. L'alias retrocompatibile è stato ora rimosso:

// Before
import { Connection, ConnectionOptions } from "typeorm"

const connection = await createConnection(options)
await connection.close()

// After
import { DataSource, DataSourceOptions } from "typeorm"

const dataSource = new DataSource(options)
await dataSource.initialize()
await dataSource.destroy()

Le seguenti ridenominazioni si applicano ovunque:

BeforeAfter
ConnectionDataSource
ConnectionOptionsDataSourceOptions
BaseConnectionOptionsBaseDataSourceOptions
MysqlConnectionOptionsMysqlDataSourceOptions
(same pattern for all drivers)
connection.connect()dataSource.initialize()
connection.close()dataSource.destroy()
connection.isConnecteddataSource.isInitialized

Proprietà name rimossa

La proprietà deprecata name su DataSource e BaseDataSourceOptions è stata rimossa. Le connessioni denominate sono state deprecate nella v0.3 quando ConnectionManager è stato rimosso. Se stavi utilizzando name per identificare le connessioni, gestisci invece direttamente le tue istanze di DataSource.

Nota: il codice che legge dataSource.name riceverà ora undefined invece di "default". Se utilizzi questo valore nella logica di logging o multi-tenant, aggiorna di conseguenza.

La proprietà .connection in varie classi è ora .dataSource

La proprietà connection nelle classi Driver, QueryRunner, EntityManager, QueryBuilder, EntityMetadata e *Event è stata rinominata in dataSource. Per EntityManager, questo cambiamento era stato annunciato nella 0.3, ma non era stato effettivamente implementato. Per facilitare la transizione, è stato aggiunto un getter deprecato che restituisce lo stesso valore di dataSource.

Varie

La classe ConnectionManager è stata rimossa. Se la utilizzavi per gestire connessioni multiple, crea e gestisci direttamente le tue istanze DataSource.

Anche ConnectionOptionsReader è stato semplificato: all() è stato rinominato in get() (restituisce tutte le configurazioni come array), mentre i vecchi metodi get(name) e has(name) sono stati rimossi.

const reader = new ConnectionOptionsReader()

// when your ormconfig has a single data source
const [options] = await reader.get()

// when you need a specific config from multiple data sources
const allOptions = await reader.get()
const postgresOptions = allOptions.find((o) => o.type === "postgres")

Funzioni globali di comodità rimosse

Le seguenti funzioni globali deprecate sono state rimosse:

  • createConnection / createConnections

  • getConnection

  • getConnectionManager

  • getConnectionOptions

  • getManager

  • getSqljsManager

  • getRepository

  • getTreeRepository

  • createQueryBuilder

Utilizza i metodi equivalenti sulla tua istanza DataSource:

// Before
const repo = getRepository(User)
const qb = createQueryBuilder("user")

// After
const repo = dataSource.getRepository(User)
const qb = dataSource.createQueryBuilder("user")

Configurazione tramite variabili d'ambiente rimossa

La classe deprecata ConnectionOptionsEnvReader e la capacità di configurare connessioni tramite TYPEORM_CONNECTION, TYPEORM_URL e altre variabili d'ambiente TYPEORM_* sono state rimosse. Anche il formato file ormconfig.env non è più supportato. TypeORM non carica più automaticamente file .env né dipende da dotenv.

Utilizza invece un file di configurazione TypeScript o JavaScript:

// ormconfig.ts
export default {
type: process.env.DB_TYPE,
url: process.env.DB_URL,
// ...
}

Cambiamenti comportamentali

Comportamento predefinito di invalidWhereValuesBehavior cambiato in throw

Questo è un cambiamento comportamentale significativo che potrebbe interrompere le applicazioni esistenti in fase di runtime.

Il comportamento predefinito per i valori null e undefined nelle condizioni where è cambiato. Precedentemente, i valori null e undefined venivano ignorati silenziosamente (la proprietà veniva saltata). Ora, entrambi generano un errore per impostazione predefinita.

Questo cambiamento previene bug subdoli in cui query come findBy({ id: undefined }) restituivano silenziosamente tutte le righe invece di fallire.

// v0.3: silently returns all posts (null is ignored)
// v1.0: throws TypeORMError
await repository.find({ where: { text: null } })

// v0.3: silently returns all posts (undefined is ignored)
// v1.0: throws TypeORMError
await repository.find({ where: { text: undefined } })

Per corrispondere ai valori null, utilizza l'operatore IsNull():

import { IsNull } from "typeorm"

await repository.find({ where: { text: IsNull() } })

Per ripristinare il comportamento precedente, imposta invalidWhereValuesBehavior nelle opzioni del tuo data source:

new DataSource({
// ...
invalidWhereValuesBehavior: {
null: "ignore",
undefined: "ignore",
},
})

Questa impostazione protegge tutte le API di alto livello — operazioni di ricerca, metodi di modifica del repository/manager e queryBuilder.setFindOptions() (l'unico metodo QueryBuilder interessato). Il resto dei metodi QueryBuilder (.where(), .andWhere(), .orWhere()) non è interessato — i valori null e undefined passano così come sono. Consulta Gestione di null e undefined per i dettagli completi.

Hashing

L'implementazione interna di hashing è stata sostituita con crypto integrato in Node.js. Se utilizzi la cache dei risultati delle query di TypeORM, le voci della cache esistenti saranno invalidate dopo l'aggiornamento perché la funzione di hash produce un output diverso. Le cache verranno ricostruite automaticamente — potresti vedere un breve aumento dei cache miss.

Pattern glob

I pattern glob (utilizzati per la scoperta dei file di entità/migrazione) sono ora gestiti da tinyglobby invece che da glob. Questa è una sostituzione diretta per la maggior parte dei progetti.

orphanedRowAction: "nullify" con chiavi esterne non nullable

Quando orphanedRowAction è "nullify" (il valore predefinito) e la colonna della chiave esterna è non nullable, i figli orfani vengono ora eliminati invece di generare una violazione di vincolo del database. In precedenza, TypeORM tentava di impostare la FK su null, il che falliva per le colonne non nullable.

Se facevi affidamento sull'errore per prevenire l'eliminazione accidentale dei figli, imposta orphanedRowAction: "disable" sulla relazione per preservare il comportamento precedente.

Righe di giunzione many-to-many ed entità soft-deleted

Chiamare recover() su un'entità soft-deleted con relazioni many-to-many non genera più una violazione di chiave duplicata. Le righe della tabella di giunzione non vengono toccate da softRemove, ma in precedenza TypeORM non poteva vederle durante il caricamento dell'entità per il recupero, causando il tentativo di inserimenti duplicati.

Come effetto collaterale, i confronti di giunzione many-to-many durante save() ora includono le entità correlate soft-deleted. Se imposti esplicitamente un array di relazioni che esclude un'entità soft-deleted, la sua riga di giunzione verrà rimossa:

// photo2 was independently soft-deleted but its junction row exists
user.manyToManyPhotos = [photo1] // photo2 excluded
await manager.save(user) // junction row for photo2 is now removed

Questo si applica solo quando la proprietà della relazione è impostata esplicitamente. Se è undefined, non viene eseguito alcun confronto e le righe di giunzione rimangono intatte.

Colonne

Opzione readonly rimossa

L'opzione deprecata readonly per le colonne è stata rimossa. Utilizza invece l'opzione update — nota che accetta il valore opposto:

// Before
@Column({ readonly: true })
authorName: string

// After
@Column({ update: false })
authorName: string

unsigned su ColumnNumericOptions rimosso

La proprietà deprecata unsigned su ColumnNumericOptions (utilizzata con gli overload del tipo colonna decimal/float come @Column("decimal", { unsigned: true })) è stata rimossa, poiché MySQL ha deprecato UNSIGNED per i tipi numerici non interi. L'opzione unsigned su ColumnOptions per i tipi interi non è interessata.

Relazioni

nullable: false ora usa INNER JOIN

Le relazioni contrassegnate con nullable: false ora utilizzano INNER JOIN invece di LEFT JOIN quando caricate tramite relations, eager loading o opzioni di ricerca. Si applica solo ai tipi di relazione che possiedono la colonna di join (ManyToOne e OneToOne lato proprietario).

Questo è semanticamente corretto poiché una chiave esterna non nullable garantisce l'esistenza dell'entità correlata e consente all'ottimizzatore del database di produrre piani di query più efficienti.

Potenziale breaking change: Se il tuo database contiene righe che violano il vincolo NOT NULL (es. chiavi esterne orfane o nullable: false impostato ma la colonna è effettivamente nullable nel DB), queste righe verranno escluse dai risultati. Verifica l'integrità dei dati o cambia la relazione in nullable: true se necessario.

// INNER JOIN — related entity is guaranteed to exist
@ManyToOne(() => User, { nullable: false })
author: User

// LEFT JOIN — related entity may not exist (default)
@ManyToOne(() => User)
optionalEditor: User

Le relazioni OneToMany, ManyToMany e OneToOne inverse usano sempre LEFT JOIN indipendentemente dall'impostazione nullable, poiché questi tipi non hanno una colonna di join nella tabella corrente.

Eccezione per soft-delete: Se l'entità correlata ha una @DeleteDateColumn, viene usato LEFT JOIN anche per relazioni nullable: false (a meno che non sia impostato withDeleted: true). Questo previene che le entità correlate soft-deleted filtrino le righe genitore.

Repository

findOneById

Il metodo deprecato findOneById è stato rimosso da EntityManager, Repository, BaseEntity, MongoEntityManager e MongoRepository. Usa invece findOneBy:

// Before
const user = await manager.findOneById(User, 1)
const user = await repository.findOneById(1)
const user = await User.findOneById(1)

// After
const user = await manager.findOneBy(User, { id: 1 })
const user = await repository.findOneBy({ id: 1 })
const user = await User.findOneBy({ id: 1 })

Per entità MongoDB con @ObjectIdColumn(), findOneBy funziona allo stesso modo — TypeORM traduce automaticamente il nome della proprietà in _id.

findByIds rimosso

Il metodo deprecato findByIds è stato rimosso da EntityManager, Repository e BaseEntity. Utilizza invece findBy con l'operatore In:

// Before
const users = await repository.findByIds([1, 2, 3])

// After
import { In } from "typeorm"

const users = await repository.findBy({ id: In([1, 2, 3]) })

exist rinominato in exists

Il metodo deprecato Repository.exist() è stato rimosso. Usa invece exists() — il comportamento è identico:

// Before
const hasUsers = await userRepository.exist({ where: { isActive: true } })

// After
const hasUsers = await userRepository.exists({ where: { isActive: true } })

AbstractRepository, @EntityRepository e getCustomRepository rimossi

La classe AbstractRepository, il decoratore @EntityRepository e il metodo getCustomRepository() sono stati rimossi. Questi erano deprecati nella v0.3 a favore di Repository.extend():

// Before
@EntityRepository(User)
class UserRepository extends AbstractRepository<User> {
findByName(name: string) {
return this.repository.findOneBy({ name })
}
}
const userRepo = dataSource.getCustomRepository(UserRepository)

// After
const UserRepository = dataSource.getRepository(User).extend({
findByName(name: string) {
return this.findOneBy({ name })
},
})

Sono state rimosse anche le seguenti classi di errore: CustomRepositoryDoesNotHaveEntityError, CustomRepositoryCannotInheritRepositoryError, CustomRepositoryNotFoundError.

Decoratore @RelationCount e loadRelationCountAndMap rimossi

Il decoratore @RelationCount e il metodo SelectQueryBuilder.loadRelationCountAndMap() sono stati rimossi. Utilizza invece @VirtualColumn o una sotto-query nel tuo query builder:

// Before
@RelationCount((post: Post) => post.categories)
categoryCount: number

// After — use @VirtualColumn with a sub-query
// Replace the junction table name and column names to match your schema
@VirtualColumn({
query: (alias) =>
`SELECT COUNT(*) FROM post_categories_category WHERE postId = ${alias}.id`,
})
categoryCount: number

Opzioni di ricerca

Opzione join rimossa

La proprietà deprecata join su FindOneOptions e FindManyOptions è stata rimossa, insieme all'interfaccia JoinOptions.

leftJoinAndSelectrelations

Se usavi leftJoinAndSelect, sostituiscila con la sintassi object di relationsrelations esegue sempre LEFT JOIN con selezione, che è equivalente:

// Before
const posts = await repository.find({
join: {
alias: "post",
leftJoinAndSelect: {
categories: "post.categories",
author: "post.author",
},
},
})

// After
const posts = await repository.find({
relations: { categories: true, author: true },
})

Altri tipi di join → QueryBuilder

L'opzione relations supporta solo LEFT JOIN con selezione. Se usavi innerJoinAndSelect, innerJoin o leftJoin (senza select), passa all'API QueryBuilder:

// Before — innerJoinAndSelect
const posts = await repository.find({
join: {
alias: "post",
innerJoinAndSelect: {
categories: "post.categories",
},
},
})

// After — QueryBuilder with innerJoinAndSelect
const posts = await repository
.createQueryBuilder("post")
.innerJoinAndSelect("post.categories", "categories")
.getMany()

// Before — leftJoin (without select)
const posts = await repository.find({
join: {
alias: "post",
leftJoin: {
categories: "post.categories",
},
},
where: { categories: { isRemoved: false } },
})

// After — QueryBuilder with leftJoin
const posts = await repository
.createQueryBuilder("post")
.leftJoin("post.categories", "categories")
.where("categories.isRemoved = :isRemoved", { isRemoved: false })
.getMany()

Questa distinzione è rilevante nella pratica. Ad esempio, PostgreSQL e CockroachDB non consentono FOR UPDATE sul lato nullable di un outer join, quindi le query che combinano locking con relazioni join potrebbero richiedere INNER JOIN:

// Before — innerJoinAndSelect + lock
const post = await repository.findOne({
join: {
alias: "post",
innerJoinAndSelect: {
categories: "post.categories",
},
},
lock: { mode: "pessimistic_write", tables: ["category"] },
})

// After — QueryBuilder with innerJoinAndSelect + lock
const post = await repository
.createQueryBuilder("post")
.innerJoinAndSelect("post.categories", "categories")
.setLock("pessimistic_write", undefined, ["categories"])
.getOne()

Locking con relazioni annidate → QueryBuilder

L'opzione relations non può essere utilizzata con il pessimistic locking su tabelle unite perché relations utilizza sempre LEFT JOIN, e PostgreSQL/CockroachDB rifiutano FOR UPDATE sul lato nullable degli outer join. Utilizza invece QueryBuilder con innerJoinAndSelect:

// Before — nested relations + lock via find options
const post = await repository.findOne({
where: { id: 1 },
join: {
alias: "post",
innerJoinAndSelect: {
categories: "post.categories",
images: "categories.images",
},
},
lock: { mode: "pessimistic_write", tables: ["images"] },
})

// After — QueryBuilder with innerJoinAndSelect + lock
const post = await repository
.createQueryBuilder("post")
.innerJoinAndSelect("post.categories", "categories")
.innerJoinAndSelect("categories.images", "images")
.where("post.id = :id", { id: 1 })
.setLock("pessimistic_write", undefined, ["images"])
.getOne()

Nota che il blocco della tabella principale funziona ancora con relations — solo il blocco delle tabelle unite richiede QueryBuilder con inner join.

Rimozione di select basato su stringhe

La sintassi deprecata con array di stringhe per le opzioni select è stata rimossa. Utilizza invece la sintassi ad oggetto:

// Before
const users = await repository.find({
select: ["id", "name"],
})

// After
const users = await repository.find({
select: { id: true, name: true },
})

Il tipo rimosso è FindOptionsSelectByString.

Rimozione di relations basato su stringhe

La sintassi deprecata con array di stringhe per le opzioni relations è stata rimossa. Utilizza invece la sintassi ad oggetto:

// Before
const users = await repository.find({
relations: ["profile", "posts"],
})

// After
const users = await repository.find({
relations: { profile: true, posts: true },
})

Il tipo rimosso è FindOptionsRelationByString.

QueryBuilder

Punto e virgola rifiutati nei metodi di espressione SQL raw

I metodi select(), addSelect(), groupBy(), addGroupBy(), orderBy() e addOrderBy() su tutti i query builder (SelectQueryBuilder, UpdateQueryBuilder, SoftDeleteQueryBuilder e il base QueryBuilder) ora rifiutano input contenenti punti e virgola in fase di esecuzione per prevenire attacchi di stacking di istruzioni SQL. I metodi orderBy() convalidano inoltre che i valori di direzione dell'ordinamento siano "ASC" o "DESC" e i valori per i null siano "NULLS FIRST" o "NULLS LAST". Se disponi di espressioni SQL legittime che contengono punti e virgola (ad esempio all'interno di stringhe letterali), utilizza invece il binding dei parametri:

// This now throws
qb.select("col; DROP TABLE post")

// Use parameter binding for values
qb.where("post.title = :title", { title: "value;with;semicolons" })

printSql rimosso

Il metodo printSql() sui query builder è stato rimosso. Era ridondante perché tutte le query eseguite vengono già registrate automaticamente tramite il logger configurato quando la registrazione delle query è abilitata. Usa getSql() o getQueryAndParameters() per ispezionare l'SQL generato:

// Before
const users = await dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })
.printSql()
.getMany()

// After — inspect SQL before executing
const qb = dataSource
.getRepository(User)
.createQueryBuilder("user")
.where("user.id = :id", { id: 1 })

console.log(qb.getSql())
// or: const [sql, params] = qb.getQueryAndParameters()

const users = await qb.getMany()

Per registrare automaticamente tutte le query eseguite, abilita la registrazione delle query nel tuo DataSource:

new DataSource({
// ...
logging: ["query"],
})

onConflict rimosso

Il metodo onConflict() su InsertQueryBuilder è stato rimosso. Accettava stringhe SQL raw che erano specifiche del driver e soggette a errori. Utilizza invece orIgnore() o orUpdate():

// Before
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.onConflict(`("id") DO NOTHING`)
.execute()

// After
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.orIgnore()
.execute()

// Before
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.onConflict(`("id") DO UPDATE SET "title" = :title`)
.setParameter("title", post.title)
.execute()

// After
await dataSource
.createQueryBuilder()
.insert()
.into(Post)
.values(post)
.orUpdate(["title"], ["id"])
.execute()

Overload dell'oggetto orUpdate rimosso

L'overload basato su oggetti di orUpdate() che accettava { columns?, overwrite?, conflict_target? } è stato rimosso. Utilizza invece la firma basata su array:

// Before
.orUpdate({ conflict_target: ["date"], overwrite: ["title"] })

// After
.orUpdate(["title"], ["date"])

setNativeParameters rimosso

// Before
qb.setNativeParameters({ key: "value" })

// After
qb.setParameters({ key: "value" })

Alias di tipo WhereExpression rimosso

// Before
import { WhereExpression } from "typeorm"

// After
import { WhereExpressionBuilder } from "typeorm"

replacePropertyNames rimosso

Il metodo protetto deprecato replacePropertyNames() su QueryBuilder è stato rimosso. Se lo stavi sovrascrivendo in una sottoclasse personalizzata, la sovrascrittura non viene più chiamata.

Modalità di lock deprecate rimosse

// Before
.setLock("pessimistic_partial_write")

// After
.setLock("pessimistic_write")
.setOnLocked("skip_locked")

// Before
.setLock("pessimistic_write_or_fail")

// After
.setLock("pessimistic_write")
.setOnLocked("nowait")

Lo stesso vale per le opzioni di ricerca:

// Before
{ lock: { mode: "pessimistic_partial_write" } }

// After
{ lock: { mode: "pessimistic_write", onLocked: "skip_locked" } }

// Before
{ lock: { mode: "pessimistic_write_or_fail" } }

// After
{ lock: { mode: "pessimistic_write", onLocked: "nowait" } }

Migrazioni

getAllMigrations rimosso

Il metodo deprecato getAllMigrations() è stato rimosso da MigrationExecutor. Utilizza invece getPendingMigrations() o getExecutedMigrations(), oppure accedi direttamente a dataSource.migrations per la lista delle classi di migrazione registrate:

// Before
const migrations = await migrationExecutor.getAllMigrations()

// After — depending on what you need
const pending = await migrationExecutor.getPendingMigrations()
const executed = await migrationExecutor.getExecutedMigrations()
const registered = dataSource.migrations

QueryRunner.loadedTables e loadedViews rimossi

// Before
const tables = queryRunner.loadedTables
const views = queryRunner.loadedViews

// After
const tables = await queryRunner.getTables()
const views = await queryRunner.getViews()

Nota: le sostituzioni sono metodi asincroni, non proprietà sincrone.

Sistema di container

L'integrazione deprecata del container IoC è stata rimossa: useContainer(), getFromContainer(), ContainerInterface, ContainedType e UseContainerOptions.

TypeORM non include più il supporto integrato per il container IoC. I pacchetti typeorm-typedi-extensions e typeorm-routing-controllers-extensions non sono più compatibili. Le sezioni seguenti spiegano come migrare in base alla tua configurazione.

Sottoscrittori e migrazioni con dipendenze

TypeORM istanzia internamente subscriber e migrazioni utilizzando sempre un costruttore senza argomenti, quindi non puoi passare istanze pre-costruite. Se le tue migrazioni necessitano di accesso a servizi, utilizza il DataSource (disponibile tramite queryRunner.dataSource) all'interno della migrazione stessa:

// Before
import { useContainer } from "typeorm"
import { Container } from "typedi"
useContainer(Container)

// After — access dependencies via the DataSource inside the migration
export class MyMigration1234 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
const repo = queryRunner.dataSource.getRepository(User)
// ...
}
}

Accesso ai repository e all'entity manager

Se in precedenza utilizzavi typeorm-typedi-extensions per iniettare EntityManager o repository nei tuoi servizi, utilizza invece direttamente il DataSource:

// Before (with typeorm-typedi-extensions)
import { InjectManager, InjectRepository } from "typeorm-typedi-extensions"

class UserService {
@InjectManager()
private manager: EntityManager

@InjectRepository(User)
private userRepository: Repository<User>
}

// After — access from the DataSource instance
class UserService {
private manager: EntityManager
private userRepository: Repository<User>

constructor(dataSource: DataSource) {
this.manager = dataSource.manager
this.userRepository = dataSource.getRepository(User)
}
}

Utilizzo con un framework DI

Se utilizzi un framework DI, registra il DataSource (o i suoi repository) come provider nel tuo container:

// typedi example
import { DataSource } from "typeorm"
import { Container } from "typedi"

const dataSource = new DataSource({
/* ... */
})
await dataSource.initialize()
Container.set(DataSource, dataSource)
Container.set("UserRepository", dataSource.getRepository(User))

NestJS

Gli utenti di NestJS non sono interessati — il pacchetto @nestjs/typeorm ha la propria integrazione che non dipende dal sistema di container rimosso da TypeORM. Tuttavia, @nestjs/typeorm v10 e l'attuale v11.0.0 tentano di registrare la classe Connection rimossa e causeranno un crash all'avvio. Assicurati di utilizzare una versione di @nestjs/typeorm che includa la correzione per la compatibilità con TypeORM v1.

Altre rimozioni interne

Le seguenti API interne sono state rimosse. Queste modifiche ti riguardano solo se stavi sviluppando driver personalizzati, estendendo QueryBuilder o utilizzando API di metadati di basso livello:

RemovedReplacement
Broadcaster.broadcastLoadEventsForAll()No replacement — use individual event subscribers
DriverUtils.buildColumnAlias()Use DriverUtils.buildAlias()
EntityMetadata.createPropertyPath() (static)Removed with no public replacement
QueryExpressionMap.nativeParametersUse QueryExpressionMap.parameters
RdbmsSchemaBuilder.renameTables()Removed