Migration zu v1
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:
-
readonlyfür den Nur-Lese-Modus -
enableWALfü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 option | Action |
|---|---|
appname | Use appName (camelCase) instead |
fsync | Use writeConcern: { journal: true } instead |
j | Use writeConcern: { journal: true } instead |
keepAlive | Remove — always enabled since MongoDB Driver v6.0 |
keepAliveInitialDelay | Remove — not configurable since MongoDB Driver v6.0 |
ssl | Use tls instead |
sslCA | Use tlsCAFile instead |
sslCRL | Remove — no replacement in modern driver |
sslCert | Use tlsCertificateKeyFile instead |
sslKey | Use tlsCertificateKeyFile instead |
sslPass | Use tlsCertificateKeyFilePassword instead |
sslValidate | Use tlsAllowInvalidCertificates (inverted) instead |
useNewUrlParser | Remove — no-op since MongoDB Driver v4.0 |
useUnifiedTopology | Remove — no-op since MongoDB Driver v4.0 |
w | Use writeConcern: { w: 1 } instead |
wtimeout | Use writeConcern: { wtimeoutMS: 2500 } instead |
wtimeoutMS | Use 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.
-
hanaClientDriverwurde entfernt. Verwendedriver. -
pool.maxwurde entfernt. Verwendepool.maxConnectedOrPooled. -
pool.requestTimeoutwurde entfernt. Verwendepool.maxWaitTimeoutIfPoolExhausted. -
pool.idleTimeoutwurde entfernt. Verwendepool.maxPooledIdleTime(in Sekunden). -
pool.min,pool.maxWaitingRequestsundpool.checkIntervalwurden ohne Ersatz entfernt.
Beachte auch die Änderungen im Standardverhalten der Pool-Konfiguration:
-
pool.maxPooledIdleTimeist nun standardmäßig30Sekunden und greift nicht mehr aufpool.idleTimeoutzurück. -
pool.maxWaitTimeoutIfPoolExhaustedist nun standardmäßig0und greift nicht mehr aufpool.requestTimeoutzurü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
Connection → DataSource
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:
| Before | After |
|---|---|
Connection | DataSource |
ConnectionOptions | DataSourceOptions |
BaseConnectionOptions | BaseDataSourceOptions |
MysqlConnectionOptions | MysqlDataSourceOptions |
| (same pattern for all drivers) | |
connection.connect() | dataSource.initialize() |
connection.close() | dataSource.destroy() |
connection.isConnected | dataSource.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.
leftJoinAndSelect → relations
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:
| Removed | Replacement |
|---|---|
EntityMetadata.createPropertyPath() (static) | Removed with no public replacement |
DriverUtils.buildColumnAlias() | Use DriverUtils.buildAlias() |
Broadcaster.broadcastLoadEventsForAll() | No replacement — use individual event subscribers |
QueryExpressionMap.nativeParameters | Use QueryExpressionMap.parameters |