跳至主内容区
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

从0.3升级到1.0

本文档是从 0.3.x 版本升级到 1.0 的指南。

自动升级

@typeorm/codemod 包可自动处理本指南描述的大部分破坏性变更:

npx @typeorm/codemod v1 src/

该工具将直接更新您的代码 —— 使用 --dry 参数可预览变更而不实际写入。此代码转换工具(codemod)能处理导入重命名、API替换、查询选项语法、依赖升级等操作。无法自动化的变更会保留 TODO 注释供手动检查。

完整用法及选项请参阅代码转换工具 README

平台要求

Node.js 20+

最低 JavaScript 编译目标已提升至 ES2023,这要求 Node.js 20 或更高版本。若仍在使用旧版 Node.js,请先升级再更新 TypeORM。

非 Node 平台中 Buffer 替换为 Uint8Array

浏览器端 Buffer polyfill 已被移除。在非 Node 平台(浏览器/Deno/Bun)中,二进制数据现统一用 Uint8Array 表示。Node.js 用户不受影响——Node 的 Buffer 继承自 Uint8Array 且行为保持不变。

驱动变更

MySQL / MariaDB

移除 connectorPackage 配置项

connectorPackage 配置项已被移除,同时停止支持旧版 mysql 客户端。目前唯一支持的数据库客户端是 mysql2,TypeORM 将默认尝试加载该驱动。若您项目中正在使用 mysql,请直接替换为 mysql2

legacySpatialSupport 默认值改为 false

legacySpatialSupport 选项现在默认为 false,这意味着 TypeORM 将使用符合标准的空间函数 ST_GeomFromTextST_AsText(这些函数在 MySQL 5.7 中引入,且为 MySQL 8.0+ 所必需)。旧版的 GeomFromTextAsText 函数已在 MySQL 8.0 中移除。

若你正在运行 MySQL 5.6 或更早版本并依赖空间类型,请显式设置 legacySpatialSupport: true

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

移除 widthzerofill 列选项

MySQL 8.0.17 弃用了整数类型的显示宽度和 ZEROFILL 属性,而 MySQL 8.4 则完全移除了它们。因此 TypeORM 不再支持 widthzerofill 列选项。若你曾使用这些选项,请从列定义中移除:

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

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

如需零填充显示格式,请在应用层使用 String.prototype.padStart() 或在原生查询中用 MySQL 的 LPAD() 函数处理。整数类型的 unsigned 选项不受此变更影响。

SQLite

已停止支持 sqlite3 包,请改用 better-sqlite3

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

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

移除 flags 配置项

sqlite3 包曾接受 C 级别的打开标志(如 OPEN_URIOPEN_SHAREDCACHE 等)。better-sqlite3 不支持此方式,请改用以下专用配置项:

  • readonly:用于只读模式

  • enableWAL:用于启用 WAL 日志模式

busyTimeout 配置项更名为 timeout

sqlite3 包使用 busyTimeout 配置 SQLite 的忙等待超时,而 better-sqlite3 改用 timeout(默认值:5000毫秒):

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

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

MongoDB

TypeORM 现在要求 mongodb Node.js 驱动版本为 v7 或更高(即 ^7.0.0),已停止支持 mongodb 驱动的 v5/v6 版本。

移除已弃用的连接选项

以下 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

移除 stats() 方法

stats() 方法已从 MongoQueryRunnerMongoEntityManagerMongoRepository 中移除。底层的 collStats 命令已在 MongoDB 服务器 6.2 中弃用,而 Collection.stats() 方法则在 MongoDB 驱动 v7 中被移除。

请改用 $collStats 聚合阶段。请注意响应结构已变化——countsizestorageSize 等属性现在嵌套在 storageStats 下,而非顶层。

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

移除全局函数 getMongoRepositorygetMongoManager

已弃用的全局函数 getMongoRepository()getMongoManager() 已被移除。请改用 DataSourceEntityManager 上的对应实例方法:

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

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

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

类型声明

内部 MongoDB 类型(如 ObjectId)不再从 typeorm 重新导出,请直接从 mongodb 导入:

// Before
import { ObjectId } from "typeorm"

// After
import { ObjectId } from "mongodb"

MS SQL Server

移除 domain 连接选项

SqlServerConnectionCredentialsOptions 中已弃用的 domain 配置项已被移除。请改用带有 NTLM 类型的 authentication 配置项:

// 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.isolationoptions.connectionIsolationLevel

SqlServerDataSourceOptions 中的 options.isolation 选项已重命名为 options.isolationLevel(该选项原本命名有误)。另请注意值格式已从 READ_COMMITTED 改为 READ COMMITTED(下划线替换为空格),以匹配 TypeORM 代码库的统一格式。请相应更新 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

多个已弃用的 SAP HANA 连接别名已被移除。

  • hanaClientDriver 已被移除,请改用 driver

  • pool.max 已被移除,请改用 pool.maxConnectedOrPooled

  • pool.requestTimeout 已被移除,请改用 pool.maxWaitTimeoutIfPoolExhausted

  • pool.idleTimeout 已被移除,请改用 pool.maxPooledIdleTime(单位为秒)

  • pool.minpool.maxWaitingRequestspool.checkInterval 已被移除,且无替代项

另请注意连接池配置的默认行为变更:

  • pool.maxPooledIdleTime 现在默认为 30 秒,且不再回退至 pool.idleTimeout

  • pool.maxWaitTimeoutIfPoolExhausted 现在默认为 0,且不再回退至 pool.requestTimeout

Expo

已移除对旧版 Expo SQLite 驱动的支持。Expo 在 SDK v52 中移除了旧版 API,请升级至 Expo SDK v52 或更高版本并使用现代异步 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(缓存)

RedisQueryResultCache 中已移除对旧版(v3)Redis 客户端的支持,请升级至 Redis 客户端 v4 或更高版本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()

数据源(DataSource)

ConnectionDataSource

DataSource 在 v0.3 已取代 Connection,现完全移除向后兼容别名:

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

以下全局重命名生效:

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

移除 name 属性

DataSourceBaseDataSourceOptions 中已弃用的 name 属性已被移除。命名连接在 v0.3 版本移除 ConnectionManager 时已被弃用。若需使用 name 来标识连接,请直接管理 DataSource 实例。

注意:读取 dataSource.name 的代码现在会得到 undefined(原为 "default")。若在日志或多租户逻辑中使用该值,请相应调整。

多个类中的 .connection 属性现已更名为 .dataSource

DriverQueryRunnerEntityManagerQueryBuilderEntityMetadata*Event 类中的 connection 属性已统一更名为 dataSource。对于 EntityManager,此变更在 0.3 版本已预告但未实际实施。为方便迁移,已添加返回相同值的弃用 getter(其返回值与 dataSource 相同)。

杂项变更

ConnectionManager 类已被移除。若您曾用它管理多个连接,请直接创建并管理 DataSource 实例。

ConnectionOptionsReader 也已简化:all() 重命名为 get()(以数组形式返回所有配置),旧版 get(name)has(name) 方法被移除。

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

移除全局快捷函数

以下已弃用的全局函数已被移除:

  • createConnection / createConnections

  • getConnection

  • getConnectionManager

  • getConnectionOptions

  • getManager

  • getSqljsManager

  • getRepository

  • getTreeRepository

  • createQueryBuilder

请改用 DataSource 实例的等效方法:

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

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

环境变量配置功能已移除

已弃用的 ConnectionOptionsEnvReader 类及通过 TYPEORM_CONNECTIONTYPEORM_URLTYPEORM_* 环境变量配置连接的功能已被移除。同时不再支持 ormconfig.env 文件格式。TypeORM 不再自动加载 .env 文件或依赖 dotenv

请改用 TypeScript 或 JavaScript 配置文件:

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

行为变更

invalidWhereValuesBehavior 默认值更改为 throw

这是一项重大行为变更,可能导致现有应用在运行时中断。

WHERE 条件中 null 和 undefined 值的默认处理行为已变更。此前这些值会被静默忽略(跳过对应属性),现在默认情况下两者都会抛出错误

此项变更修复了 findBy({ id: undefined }) 类查询会静默返回所有行而非报错的潜在缺陷。

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

若需匹配 null 值,请使用 IsNull() 操作符:

import { IsNull } from "typeorm"

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

要恢复旧版行为,请在数据源配置中设置 invalidWhereValuesBehavior

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

此设置保护所有高级 API——包括查询操作、仓库/管理器的变更方法以及 queryBuilder.setFindOptions()(唯一受影响的 QueryBuilder 方法)。其他 QueryBuilder 方法(.where().andWhere().orWhere()不受影响——null 和 undefined 值会原样传递。完整细节请参阅空值与未定义值处理

哈希算法

内部哈希实现已替换为 Node.js 内置的 crypto 模块。若您使用 TypeORM 的查询结果缓存,升级后现有缓存条目将失效(因为哈希函数产生不同输出)。缓存将自动重建——您可能会观察到缓存未命中的短暂增加。

通配符模式

Glob 模式(用于实体/迁移文件发现)现由 tinyglobby 处理(替代 glob)。这对大多数项目是无缝替换。

外键非空时 orphanedRowAction: "nullify" 的处理

orphanedRowAction 设为 "nullify"(默认值)且外键列为非空时,孤儿子记录现会被删除(而非抛出数据库约束违反错误)。此前 TypeORM 会尝试将外键设为 null,这在非空列上会失败。

若您依赖此错误来防止意外删除子记录,请在关系上设置 orphanedRowAction: "disable" 以保留旧行为。

多对多关联表行与软删除实体

对具有多对多关系的软删除实体调用 recover() 时,不再抛出重复键违反错误。关联表行不受 softRemove 影响,但此前 TypeORM 在加载实体以恢复时无法看到它们,导致尝试重复插入。

副作用是,save() 期间的多对多关联比较现在包含软删除的相关实体。若您显式设置的关系数组排除了某个软删除实体,其关联行将被移除:

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

此情况仅发生在关系属性被显式设置时。若其为 undefined,则不进行比较且关联行保持不变。

列(Columns)

readonly 选项已移除

已弃用的 readonly 列选项已被移除。请改用 update 选项——注意它接受相反的值:

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

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

ColumnNumericOptions 中的 unsigned 已移除

ColumnNumericOptions 中已弃用的 unsigned 属性(用于 @Column("decimal", { unsigned: true }) 等小数/浮点列类型重载)已被移除,因 MySQL 已弃用非整数数值类型的 UNSIGNED。整数类型 ColumnOptions 中的 unsigned 选项 不受 影响。

关联关系

nullable: false 现在使用 INNER JOIN

标记为 nullable: false 的关联关系现在通过 relations、贪婪加载或查询选项加载时,会使用 INNER JOIN 而非 LEFT JOIN。此变更仅适用于拥有连接列的关联类型(ManyToOne 和拥有方的 OneToOne)。

这在语义上是正确的,因为非空外键保证了相关实体必然存在,且允许数据库优化器生成更高效的查询计划。

潜在破坏性变更: 若数据库中存在违反 NOT NULL 约束的行(例如孤儿外键,或设置了 nullable: false 但数据库列实际可为空),这些行将被排除在查询结果之外。请验证数据完整性,或根据需要将关联关系改为 nullable: true

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

OneToManyManyToMany 和反向 OneToOne 关系始终使用 LEFT JOIN(无论 nullable 设置如何),因为这些关联类型在当前表上没有连接列。

软删除例外: 若关联实体包含 @DeleteDateColumn,即使对于 nullable: false 的关系也会使用 LEFT JOIN(除非显式设置 withDeleted: true)。这能防止软删除的关联实体过滤掉其父行。

仓储(Repository)

findOneById

已弃用的 findOneById 方法已从 EntityManagerRepositoryBaseEntityMongoEntityManagerMongoRepository 中移除。请改用 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 })

对于使用 @ObjectIdColumn() 的 MongoDB 实体,findOneBy 工作方式相同——TypeORM 会自动将属性名转换为 _id

findByIds 已移除

EntityManagerRepositoryBaseEntity 中已弃用的 findByIds 方法已被移除。请改用 findBy 配合 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 更名为 exists

已弃用的 Repository.exist() 方法已被移除。请改用 exists()——两者行为完全一致:

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

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

AbstractRepository@EntityRepositorygetCustomRepository 已移除

AbstractRepository 类、@EntityRepository 装饰器和 getCustomRepository() 方法已被移除。这些功能在 v0.3 中已被弃用,推荐使用 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 })
},
})

以下错误类也被移除:CustomRepositoryDoesNotHaveEntityErrorCustomRepositoryCannotInheritRepositoryErrorCustomRepositoryNotFoundError

@RelationCount 装饰器和 loadRelationCountAndMap 已移除

@RelationCount 装饰器和 SelectQueryBuilder.loadRelationCountAndMap() 方法已被移除。请改用 @VirtualColumn 或在查询构建器中使用子查询:

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

查询选项

移除 join 选项

已弃用的 FindOneOptionsFindManyOptions 中的 join 属性及 JoinOptions 接口已被移除。

leftJoinAndSelectrelations

若曾使用 leftJoinAndSelect,请改用 relations 的对象语法——relations 始终执行带结果选择的 LEFT JOIN,效果等同:

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

其他 JOIN 类型 → 改用 QueryBuilder

relations 选项仅支持带结果选择的 LEFT JOIN。若曾使用 innerJoinAndSelectinnerJoinleftJoin(不带选择),请切换到 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()

此区别在实际中很重要。例如 PostgreSQL 和 CockroachDB 不允许在外部连接的可空侧使用 FOR UPDATE,因此结合锁操作与关联加载的查询可能需要 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()

嵌套关联的锁操作 → 改用 QueryBuilder

relations 选项无法在连接表上使用悲观锁,因为 relations 始终使用 LEFT JOIN,而 PostgreSQL/CockroachDB 会拒绝在外部连接的可空侧使用 FOR UPDATE。请改用带 innerJoinAndSelect 的 QueryBuilder:

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

注意锁定_主表_仍可通过 relations 实现——仅锁定_关联表_时才需使用带内连接的 QueryBuilder。

基于字符串的 select 已移除

已弃用的 select 查询选项字符串数组语法已被移除,请改用对象语法:

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

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

被移除的类型是 FindOptionsSelectByString

基于字符串的 relations 已移除

已弃用的 relations 查询选项字符串数组语法已被移除,请改用对象语法:

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

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

被移除的类型是 FindOptionsRelationByString

查询构建器(QueryBuilder)

原始 SQL 表达式方法中分号被拒绝

所有查询构建器(SelectQueryBuilderUpdateQueryBuilderSoftDeleteQueryBuilder 及基础 QueryBuilder)上的 select()addSelect()groupBy()addGroupBy()orderBy()addOrderBy() 方法现在会在运行时拒绝包含分号的输入,以防止 SQL 语句堆叠攻击。orderBy() 方法还会验证排序方向值必须为 "ASC""DESC",空值处理值必须为 "NULLS FIRST""NULLS LAST"。若您的合法 SQL 表达式中包含分号(例如在字符串字面量内),请改用参数绑定:

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

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

移除 printSql

查询构建器上的 printSql() 方法已被移除。该方法已冗余——启用查询日志后,所有执行语句都会通过配置的日志记录器自动输出。请改用 getSql()getQueryAndParameters() 检查生成的 SQL:

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

要自动记录所有执行语句,请在 DataSource 中启用查询日志:

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

onConflict 已移除

InsertQueryBuilder 上的 onConflict() 方法已被移除。该方法接受原始 SQL 字符串,存在驱动依赖性强且易出错的问题。请改用 orIgnore()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()

orUpdate 对象重载已移除

接受对象参数 { columns?, overwrite?, conflict_target? }orUpdate() 重载已被移除。请改用数组形式的签名:

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

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

setNativeParameters 已移除

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

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

WhereExpression 类型别名已移除

// Before
import { WhereExpression } from "typeorm"

// After
import { WhereExpressionBuilder } from "typeorm"

replacePropertyNames 已移除

QueryBuilder 上已弃用的受保护方法 replacePropertyNames() 已被移除。若您在自定义子类中重写了此方法,该重写将不再被调用。

已弃用的锁模式已移除

// Before
.setLock("pessimistic_partial_write")

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

// Before
.setLock("pessimistic_write_or_fail")

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

查找选项同理:

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

迁移功能变更

getAllMigrations 已移除

MigrationExecutor 中已弃用的 getAllMigrations() 方法已被移除。请改用 getPendingMigrations()getExecutedMigrations(),或直接访问 dataSource.migrations 获取注册的迁移类列表:

// 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.loadedTablesloadedViews 已移除

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

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

注意:替代方案为异步方法,而非同步属性。

容器系统

已弃用的 IoC 容器集成已被移除,包括:useContainer()getFromContainer()ContainerInterfaceContainedTypeUseContainerOptions

TypeORM 不再内置支持 IoC 容器。typeorm-typedi-extensionstypeorm-routing-controllers-extensions 包也不再兼容。以下部分将根据你的配置介绍迁移方法。

带依赖项的订阅器和迁移

TypeORM 始终通过无参构造函数在内部实例化订阅者和迁移,因此无法传递预构建实例。若迁移需要访问服务,请在迁移内部通过 queryRunner.dataSource 获取 DataSource

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

访问存储库和实体管理器

如果你之前使用 typeorm-typedi-extensionsEntityManager 或存储库注入到服务中,现在请直接使用 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)
}
}

与 DI 框架配合使用

如果使用 DI 框架,请将 DataSource(或其存储库)注册为容器中的提供者:

// 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 用户不受影响——@nestjs/typeorm 包拥有独立集成方案,不依赖 TypeORM 已移除的容器系统。但请注意:@nestjs/typeorm v10 及当前 v11.0.0 会尝试注册已移除的 Connection 类,导致启动崩溃。请确保您使用的 @nestjs/typeorm 版本包含 TypeORM v1 兼容性修复。

其他内部移除项

以下内部 API 已被移除。这些变更仅影响构建自定义驱动、扩展 QueryBuilder 或使用底层元数据 API 的用户:

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