Zum Hauptinhalt springen

Migration zu v1

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Dies ist der Migrationsleitfaden für das Upgrade von Version 0.3.x auf 1.0.

Plattformanforderungen

Node.js 20+

Das minimale JavaScript-Ziel ist nun ES2023, was Node.js 20 oder höher erfordert. Wenn du eine ältere Node.js-Version verwendest, führe vor dem Aktualisieren von TypeORM ein Upgrade durch.

Buffer durch Uint8Array auf Nicht-Node-Plattformen ersetzt

Die Browser-Buffer-Polyfill wurde entfernt. Auf Nicht-Node-Plattformen (Browser, Deno, Bun) werden Binärdaten nun als Uint8Array dargestellt. Node.js-Nutzer sind nicht betroffen — Node's Buffer erweitert Uint8Array und funktioniert weiterhin wie zuvor.

Treiberänderungen

MySQL / MariaDB

Option connectorPackage entfernt

Die Option connectorPackage wurde entfernt, zusammen mit der Unterstützung für den alten mysql-Client. Der einzige unterstützte Datenbankclient ist nun mysql2, den TypeORM standardmäßig zu laden versucht. Wenn Sie mysql in Ihrem Projekt verwendet haben, ersetzen Sie es einfach durch mysql2.

Standard für legacySpatialSupport auf false geändert

Die Option legacySpatialSupport ist nun standardmäßig false, was bedeutet, dass TypeORM die standardkonformen räumlichen Funktionen ST_GeomFromText und ST_AsText verwendet, die in MySQL 5.7 eingeführt wurden und für MySQL 8.0+ erforderlich sind. Die veralteten Funktionen GeomFromText und AsText wurden in MySQL 8.0 entfernt.

Wenn du MySQL 5.6 oder älter verwendest und auf räumliche Typen angewiesen bist, setze legacySpatialSupport: true explizit:

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

Spaltenoptionen width und zerofill entfernt

MySQL 8.0.17 hat die Anzeigebreite für Integer-Typen und das Attribut ZEROFILL als veraltet markiert, und MySQL 8.4 hat sie vollständig entfernt. TypeORM unterstützt die Spaltenoptionen width und zerofill nicht mehr. Wenn du diese Optionen verwendet hast, entferne sie aus deinen Spaltendefinitionen:

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

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

Wenn du eine mit Nullen aufgefüllte Anzeigeformatierung benötigst, handle dies in deiner Anwendungsschicht mit String.prototype.padStart() oder der MySQL-Funktion LPAD() in einer Raw-Abfrage. Die Option unsigned für Integer-Typen ist nicht von dieser Änderung betroffen.

SQLite

Das Paket sqlite3 wurde entfernt. Verwende stattdessen better-sqlite3:

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

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

Option flags entfernt

Das Paket sqlite3 akzeptierte C-Level-Open-Flags (OPEN_URI, OPEN_SHAREDCACHE usw.). better-sqlite3 unterstützt dies nicht — verwenden Sie stattdessen die spezifischen Optionen:

  • readonly für den Nur-Lese-Modus

  • enableWAL für den WAL-Journalmodus

Option busyTimeout in timeout umbenannt

Das Paket sqlite3 verwendete busyTimeout, um das Busy-Timeout von SQLite zu konfigurieren. better-sqlite3 verwendet stattdessen timeout (Standard: 5000ms):

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

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

MongoDB

TypeORM erfordert nun den mongodb Node.js-Treiber v7 oder höher (^7.0.0). Die Unterstützung für den mongodb-Treiber v5/v6 wurde eingestellt.

Veraltete Verbindungsoptionen entfernt

Die folgenden MongoDB-Verbindungsoptionen wurden entfernt:

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

Methode stats() entfernt

Die Methode stats() wurde aus MongoQueryRunner, MongoEntityManager und MongoRepository entfernt. Der zugrundeliegende Befehl collStats wurde in MongoDB Server 6.2 als veraltet markiert und die Methode Collection.stats() wurde im MongoDB Driver v7 entfernt.

Verwende stattdessen die $collStats-Aggregationsstufe. Beachte, dass die Antwortstruktur anders ist — Eigenschaften wie count, size und storageSize sind nun unter storageStats verschachtelt, anstatt auf oberster Ebene.

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

Globale Funktionen getMongoRepository und getMongoManager entfernt

Die veralteten globalen Funktionen getMongoRepository() und getMongoManager() wurden entfernt. Verwende stattdessen die entsprechenden Instanzmethoden auf DataSource oder EntityManager:

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

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

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

Typen

Die internen MongoDB-Typen (ObjectId, etc.) werden nicht mehr aus typeorm re-exportiert. Importiere sie direkt aus mongodb:

// Before
import { ObjectId } from "typeorm"

// After
import { ObjectId } from "mongodb"

MS SQL Server

Verbindungsoption domain entfernt

Die veraltete Option domain in SqlServerConnectionCredentialsOptions wurde entfernt. Verwende stattdessen die Option authentication mit dem Typ 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",
},
},
// ...
})

SAP HANA

Mehrere veraltete SAP HANA-Verbindungsaliase wurden entfernt.

  • hanaClientDriver wurde entfernt. Verwende driver.

  • pool.max wurde entfernt. Verwende pool.maxConnectedOrPooled.

  • pool.requestTimeout wurde entfernt. Verwende pool.maxWaitTimeoutIfPoolExhausted.

  • pool.idleTimeout wurde entfernt. Verwende pool.maxPooledIdleTime (in Sekunden).

  • pool.min, pool.maxWaitingRequests und pool.checkInterval wurden ohne Ersatz entfernt.

Beachte auch die Änderungen im Standardverhalten der Pool-Konfiguration:

  • pool.maxPooledIdleTime ist nun standardmäßig 30 Sekunden und greift nicht mehr auf pool.idleTimeout zurück.

  • pool.maxWaitTimeoutIfPoolExhausted ist nun standardmäßig 0 und greift nicht mehr auf pool.requestTimeout zurück.

Expo

Die Unterstützung für den veralteten Expo-SQLite-Treiber wurde entfernt. Die veraltete API wurde von Expo in SDK v52 entfernt. Führe ein Upgrade auf Expo SDK v52 oder höher durch und verwende die moderne asynchrone SQLite-API:

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

Die Unterstützung für veraltete (v3) Redis-Clients in RedisQueryResultCache wurde entfernt. Führe ein Upgrade auf Redis-Client v4 oder höher durch (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 hat Connection in v0.3 ersetzt. Der abwärtskompatible Alias wurde nun entfernt:

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

Die folgenden Umbenennungen gelten durchgängig:

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

Eigenschaft name entfernt

Die veraltete Eigenschaft name auf DataSource und BaseDataSourceOptions wurde entfernt. Benannte Verbindungen wurden in v0.3 als veraltet markiert, als ConnectionManager entfernt wurde. Wenn du name zur Identifikation von Verbindungen verwendet hast, verwalte deine DataSource-Instanzen stattdessen direkt.

Hinweis: Code, der dataSource.name ausliest, erhält nun undefined anstelle von "default". Wenn du diesen Wert in Logging- oder Multi-Tenancy-Logik verwendest, aktualisiere entsprechend.

Die .connection-Eigenschaft in verschiedenen Klassen ist nun .dataSource

Die connection-Eigenschaft in den Klassen Driver, QueryRunner, EntityManager, QueryBuilder, EntityMetadata und *Event wurde in dataSource umbenannt. Für EntityManager wurde diese Änderung bereits in Version 0.3 angekündigt, aber nicht implementiert. Zur Vereinfachung der Migration wurde ein veralteter Getter hinzugefügt, der denselben Wert wie dataSource zurückgibt.

Verschiedenes

Die ConnectionManager-Klasse wurde entfernt. Wenn du sie zur Verwaltung mehrerer Verbindungen verwendet hast, erstelle und verwalte deine DataSource-Instanzen direkt.

ConnectionOptionsReader wurde ebenfalls vereinfacht: all() wurde in get() umbenannt (gibt alle Konfigurationen als Array zurück), und die alten Methoden get(name) und has(name) wurden entfernt.

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

Globale Convenience-Funktionen entfernt

Folgende veraltete globale Funktionen wurden entfernt:

  • createConnection / createConnections

  • getConnection

  • getConnectionManager

  • getConnectionOptions

  • getManager

  • getSqljsManager

  • getRepository

  • getTreeRepository

  • createQueryBuilder

Verwende die entsprechenden Methoden auf deiner DataSource-Instanz:

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

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

Konfiguration über Umgebungsvariablen entfernt

Die veraltete Klasse ConnectionOptionsEnvReader und die Möglichkeit, Verbindungen via TYPEORM_CONNECTION, TYPEORM_URL und anderen TYPEORM_*-Umgebungsvariablen zu konfigurieren, wurden entfernt. Das ormconfig.env-Dateiformat wird ebenfalls nicht mehr unterstützt. TypeORM lädt keine .env-Dateien mehr automatisch und hängt nicht mehr von dotenv ab.

Verwende stattdessen eine TypeScript- oder JavaScript-Konfigurationsdatei:

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

Verhaltensänderungen

invalidWhereValuesBehavior-Standard auf throw geändert

Dies ist eine signifikante Verhaltensänderung, die bestehende Anwendungen zur Laufzeit brechen kann.

Das Standardverhalten für null- und undefined-Werte in Where-Bedingungen hat sich geändert. Bisher wurden null- und undefined-Werte stillschweigend ignoriert (die Eigenschaft wurde übersprungen). Jetzt werfen beide standardmäßig einen Fehler.

Diese Änderung verhindert subtile Fehler, bei denen Abfragen wie findBy({ id: undefined }) stillschweigend alle Zeilen zurückgeben würden, anstatt zu scheitern.

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

Um null-Werte abzugleichen, verwende den IsNull()-Operator:

import { IsNull } from "typeorm"

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

Um das vorherige Verhalten wiederherzustellen, setze invalidWhereValuesBehavior in deinen DataSource-Optionen:

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

Diese Einstellung betrifft alle High-Level-APIs – Find-Operationen, Repository-/Manager-Mutationsmethoden und queryBuilder.setFindOptions() (die einzige betroffene QueryBuilder-Methode). Die übrigen QueryBuilder-Methoden (.where(), .andWhere(), .orWhere()) sind nicht betroffen – null- und undefined-Werte werden unverändert durchgereicht. Siehe Null- und undefined-Behandlung für Details.

Hashing

TypeORM verwendete zuvor das npm-Paket sha.js für SHA-1-Hashing (eine nicht standardkonforme Implementierung). Dies wurde durch das eingebaute crypto-Modul von Node.js ersetzt, und das uuid-Paket wurde durch crypto.randomUUID() ersetzt.

Für Browserumgebungen wurde RandomGenerator.sha1 auf die Standardimplementierung korrigiert.

Auswirkung: Wenn du den Abfrage-Ergebnis-Cache von TypeORM verwendest, werden vorhandene Cache-Einträge nach dem Upgrade ungültig, weil die Hash-Funktion eine andere Ausgabe erzeugt. Dies ist harmlos – Caches werden automatisch neu aufgebaut –, aber du könntest einen kurzzeitigen Anstieg von Cache-Misses beobachten.

Glob-Muster

Glob-Muster (verwendet bei der Entdeckung von Entity-/Migrationsdateien) werden nun von tinyglobby anstelle von glob verarbeitet. Dies ist größtenteils ein direkter Ersatz, aber Randfälle mit Brace-Expansion oder plattformspezifischen Pfadtrennern können sich anders verhalten.

Spalten

Option readonly entfernt

Die veraltete Spaltenoption readonly wurde entfernt. Verwende stattdessen die Option update — beachte, dass sie den entgegengesetzten Wert annimmt:

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

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

unsigned in ColumnNumericOptions entfernt

Die veraltete Eigenschaft unsigned in ColumnNumericOptions (verwendet mit Überladungen für Dezimal-/Float-Spaltentypen wie @Column("decimal", { unsigned: true })) wurde entfernt, da MySQL UNSIGNED für nicht ganzzahlige numerische Typen als veraltet markiert hat. Die Option unsigned in ColumnOptions für ganzzahlige Typen ist nicht betroffen.

Beziehungen

nullable: false verwendet jetzt INNER JOIN

Beziehungen mit nullable: false verwenden jetzt INNER JOIN statt LEFT JOIN, wenn sie via relations, Eager Loading oder Find-Optionen geladen werden. Dies gilt nur für Beziehungstypen, die die Join-Spalte besitzen (ManyToOne und owning-side OneToOne).

Dies ist semantisch korrekt, da ein nicht-nullbarer Fremdschlüssel die Existenz der verknüpften Entität garantiert, und ermöglicht Datenbankoptimierern effizientere Abfragepläne.

Potentiell Breaking: Wenn deine Datenbank Zeilen enthält, die die NOT NULL-Constraint verletzen (z.B. verwaiste Fremdschlüssel oder nullable: false gesetzt, aber die Spalte ist in der DB nullbar), werden diese Zeilen aus Abfrageergebnissen ausgeschlossen. Überprüfe deine Datenintegrität oder setze die Beziehung auf nullable: true, falls nötig.

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

OneToMany-, ManyToMany- und inverse OneToOne-Beziehungen verwenden immer LEFT JOIN, unabhängig von der nullable-Einstellung, da diese Beziehungstypen keine Join-Spalte auf der aktuellen Tabelle haben.

Soft-Delete-Ausnahme: Wenn die verknüpfte Entität eine @DeleteDateColumn hat, wird LEFT JOIN selbst für nullable: false-Beziehungen verwendet (außer withDeleted: true ist gesetzt). Dies verhindert, dass soft-gelöschte Entitäten ihre übergeordneten Zeilen ausfiltern.

Repository

findOneById

Die veraltete Methode findOneById wurde aus EntityManager, Repository, BaseEntity, MongoEntityManager und MongoRepository entfernt. Verwende stattdessen 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 })

Für MongoDB-Entitäten mit @ObjectIdColumn() funktioniert findOneBy gleich – TypeORM übersetzt den Eigenschaftsnamen automatisch in _id.

findByIds entfernt

Die veraltete Methode findByIds wurde aus EntityManager, Repository und BaseEntity entfernt. Verwende stattdessen findBy mit dem In-Operator:

// 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 in exists umbenannt

Die veraltete Methode Repository.exist() wurde entfernt. Verwende stattdessen exists() — das Verhalten ist identisch:

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

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

AbstractRepository, @EntityRepository und getCustomRepository entfernt

Die Klasse AbstractRepository, der Dekorator @EntityRepository und die Methode getCustomRepository() wurden entfernt. Diese waren in v0.3 zugunsten von Repository.extend() als veraltet markiert:

// 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 })
},
})

Folgende Fehlerklassen wurden ebenfalls entfernt: CustomRepositoryDoesNotHaveEntityError, CustomRepositoryCannotInheritRepositoryError, CustomRepositoryNotFoundError.

Dekorator @RelationCount und loadRelationCountAndMap entfernt

Der Decorator @RelationCount und die Methode SelectQueryBuilder.loadRelationCountAndMap() wurden entfernt. Verwende stattdessen @VirtualColumn oder eine Sub-Query in deinem 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

Find-Optionen

join-Option entfernt

Die veraltete join-Eigenschaft in FindOneOptions und FindManyOptions wurde entfernt, zusammen mit dem JoinOptions-Interface.

leftJoinAndSelectrelations

Wenn du leftJoinAndSelect verwendet hast, ersetze es durch die relations-Objektsyntax – relations führt immer LEFT JOINs mit Selektion aus, was äquivalent ist:

// 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 },
})

Alle anderen Join-Typen → QueryBuilder

Die relations-Option unterstützt nur LEFT JOINs mit Selektion. Wenn du innerJoinAndSelect, innerJoin oder leftJoin (ohne Select) verwendet hast, wechsel zur QueryBuilder-API:

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

Diese Unterscheidung ist praktisch relevant. Beispielsweise erlauben PostgreSQL und CockroachDB kein FOR UPDATE auf der nullbaren Seite eines Outer Joins, daher benötigen Abfragen mit Locking und verknüpften Beziehungen möglicherweise INNER JOINs:

// 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 mit verschachtelten Beziehungen → QueryBuilder

Die Option relations kann nicht mit pessimistischem Locking für verbundene Tabellen verwendet werden, da relations immer LEFT JOINs verwendet und PostgreSQL/CockroachDB FOR UPDATE auf der nullfähigen Seite von Outer Joins ablehnen. Verwende stattdessen QueryBuilder mit 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()

Beachte, dass das Locking der Haupttabelle weiterhin mit relations funktioniert – nur das Locking von verbundenen Tabellen erfordert QueryBuilder mit inneren Joins.

String-basierte select entfernt

Die veraltete String-Array-Syntax für select-Find-Optionen wurde entfernt. Verwende stattdessen die Objektsyntax:

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

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

Der entfernte Typ ist FindOptionsSelectByString.

String-basierte relations entfernt

Die veraltete String-Array-Syntax für relations-Find-Optionen wurde entfernt. Verwende stattdessen die Objektsyntax:

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

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

Der entfernte Typ ist FindOptionsRelationByString.

QueryBuilder

printSql entfernt

Die printSql()-Methode für Query Builder wurde entfernt. Sie war redundant, da alle ausgeführten Abfragen bei aktiviertem Query-Logging automatisch über den konfigurierten Logger protokolliert werden. Verwende stattdessen getSql() oder getQueryAndParameters(), um die generierte SQL-Abfrage zu prüfen:

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

Um alle ausgeführten Abfragen automatisch zu protokollieren, aktiviere das Query-Logging in deiner DataSource:

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

onConflict entfernt

Die Methode onConflict() in InsertQueryBuilder wurde entfernt. Sie akzeptierte rohe SQL-Strings, die treiberspezifisch und fehleranfällig waren. Verwende stattdessen orIgnore() oder 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()

Objektüberladung für orUpdate entfernt

Die objektbasierte orUpdate()-Überladung, die { columns?, overwrite?, conflict_target? } akzeptiert, wurde entfernt. Verwende stattdessen die array-basierte Signatur:

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

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

setNativeParameters entfernt

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

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

Die interne Eigenschaft QueryExpressionMap.nativeParameters wurde ebenfalls entfernt. Wenn du eine benutzerdefinierte QueryBuilder-Subklasse hast, die auf expressionMap.nativeParameters zugreift, wechsle zu expressionMap.parameters.

Type-Alias WhereExpression entfernt

// Before
import { WhereExpression } from "typeorm"

// After
import { WhereExpressionBuilder } from "typeorm"

replacePropertyNames entfernt

Die veraltete, geschützte Methode replacePropertyNames() wurde entfernt. Sie war eine No-Op, da der Eigenschaftsnamenaustausch in die End-of-Query-Verarbeitung via replacePropertyNamesForTheWholeQuery() verschoben wurde. Wenn du diese Methode in einer benutzerdefinierten QueryBuilder-Subklasse überschrieben hast, wird deine Überschreibung nicht mehr aufgerufen.

Veraltete Sperrmodi entfernt

// Before
.setLock("pessimistic_partial_write")

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

// Before
.setLock("pessimistic_write_or_fail")

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

Dasselbe gilt für Find-Optionen:

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

Migrationen

getAllMigrations entfernt

Die veraltete Methode getAllMigrations() wurde aus MigrationExecutor entfernt. Verwende stattdessen getPendingMigrations() oder getExecutedMigrations(), oder greife direkt auf dataSource.migrations zu, um die Liste der registrierten Migrationsklassen zu erhalten:

// 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 und loadedViews entfernt

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

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

Hinweis: Die Ersatzmethoden sind asynchron, keine synchronen Eigenschaften.

Container-System

Die veraltete IoC-Container-Integration wurde entfernt: useContainer(), getFromContainer(), ContainerInterface, ContainedType und UseContainerOptions.

TypeORM unterstützt nun kein integriertes IoC-Container-System mehr. Die Pakete typeorm-typedi-extensions und typeorm-routing-controllers-extensions sind ebenfalls nicht mehr kompatibel. Die folgenden Abschnitte erklären, wie Du je nach Deinem Setup migrieren kannst.

Subscriber und Migrationen mit Abhängigkeiten

TypeORM instanziiert Subscriber und Migrationen intern immer mit einem parameterlosen Konstruktor, daher können keine vorgefertigten Instanzen übergeben werden. Wenn deine Migrationen Zugriff auf Services benötigen, verwende die DataSource (verfügbar über queryRunner.dataSource) innerhalb der Migration selbst:

// 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)
// ...
}
}

Zugriff auf Repositories und Entity Manager

Wenn Du bisher typeorm-typedi-extensions zur Injektion von EntityManager oder Repositories in Deine Dienste verwendet hast, nutze stattdessen direkt die 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)
}
}

Verwendung mit einem DI-Framework

Bei Verwendung eines DI-Frameworks registriere die DataSource (oder ihre Repositories) als Provider in Deinem 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

NestJS-Nutzer sind nicht betroffen — das @nestjs/typeorm-Paket hat seine eigene Integration, die nicht von TypeORMs entferntem Container-System abhängt. Allerdings versuchen @nestjs/typeorm v10 und die aktuelle v11.0.0, die entfernte Connection-Klasse zu registrieren und stürzen beim Start ab. Stelle sicher, dass du eine Version von @nestjs/typeorm verwendest, die den Fix für TypeORM v1-Kompatibilität enthält.

Andere interne Entfernungen

Die folgenden internen APIs wurden entfernt. Diese betreffen dich nur, wenn du benutzerdefinierte Treiber entwickelt, QueryBuilder erweitert oder Low-Level-Metadaten-APIs verwendet hast:

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