本页面由 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_GeomFromText 和 ST_AsText(这些函数在 MySQL 5.7 中引入,且为 MySQL 8.0+ 所必需)。旧版的 GeomFromText 和 AsText 函数已在 MySQL 8.0 中移除。
若你正在运行 MySQL 5.6 或更早版本并依赖空间类型,请显式设置 legacySpatialSupport: true:
new DataSource({
type: "mysql",
legacySpatialSupport: true,
// ...
})
移除 width 和 zerofill 列选项
MySQL 8.0.17 弃用了整数类型的显示宽度和 ZEROFILL 属性,而 MySQL 8.4 则完全移除了它们。因此 TypeORM 不再支持 width 和 zerofill 列选项。若你曾使用这些选项,请从列定义中移除:
// 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_URI、OPEN_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 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 |
移除 stats() 方法
stats() 方法已从 MongoQueryRunner、MongoEntityManager 和 MongoRepository 中移除。底层的 collStats 命令已在 MongoDB 服务器 6.2 中弃用,而 Collection.stats() 方法则在 MongoDB 驱动 v7 中被移除。
请改用 $collStats 聚合阶段。请注意响应结构已变化——count、size 和 storageSize 等属性现在嵌套在 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)
移除全局函数 getMongoRepository 和 getMongoManager
已弃用的全局函数 getMongoRepository() 和 getMongoManager() 已被移除。请改用 DataSource 或 EntityManager 上的对应实例方法:
// 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.isolation 与 options.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",
// ...
},
// ...
})
Oracle
The LegacyOracleNamingStrategy is no longer exported in the main TypeORM package. You can import it from @typeorm/legacy-naming-strategies if you still need it.
// Before
import { LegacyOracleNamingStrategy } from "typeorm"
// After
import { LegacyOracleNamingStrategy } from "@typeorm/legacy-naming-strategies"
SAP HANA
多个已弃用的 SAP HANA 连接别名已被移除。
-
hanaClientDriver已被移除,请改用driver -
pool.max已被移除,请改用pool.maxConnectedOrPooled -
pool.requestTimeout已被移除,请改用pool.maxWaitTimeoutIfPoolExhausted -
pool.idleTimeout已被移除,请改用pool.maxPooledIdleTime(单位为秒) -
pool.min、pool.maxWaitingRequests和pool.checkInterval已被移除,且无替代项
另请注意连接池配置的默认行为变更:
-
pool.maxPooledIdleTime现在默认为30秒,且不再回退至pool.idleTimeout -
pool.maxWaitTimeoutIfPoolExhausted现在默认为0,且不再回退至pool.requestTimeout
Expo
最低支持的 Expo SDK 版本为 52,该版本提供了现代化的异步 SQLite API。TypeORM 现在会自动加载 expo-sqlite,因此不再需要 driver 选项:
// Before
new DataSource({
type: "expo",
driver: require("expo-sqlite"),
database: "db.sqlite",
})
// After
new DataSource({
type: "expo",
database: "db.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)
Connection → DataSource
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()
以下全局重命名生效:
| 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 |
移除 name 属性
DataSource 和 BaseDataSourceOptions 中已弃用的 name 属性已被移除。命名连接在 v0.3 版本移除 ConnectionManager 时已被弃用。若需使用 name 来标识连接,请直接管理 DataSource 实例。
注意:读取 dataSource.name 的代码现在会得到 undefined(原为 "default")。若在日志或多租户逻辑中使用该值,请相应调整。
多个类中的 .connection 属性现已更名为 .dataSource
Driver、QueryRunner、EntityManager、QueryBuilder、EntityMetadata 及 *Event 类中的 connection 属性已统一更名为 dataSource。对于 EntityManager,此变更在 0.3 版本已预告但未实际实施。为方便迁移,已添加返回相同值的弃用 getter(其返回值与 dataSource 相同)。
杂项变更
ConnectionManager 类已被移除。若您曾用它管理多个连接,请直接创建并管理 DataSource 实例。
ConnectionOptionsReader 同样被简化:all() 重命名为 get()(返回所有配置的数组),旧的 get(name) 和 has(name) 方法已被移除。现在将在 process.cwd() 而非应用路径中搜索 ormconfig 文件。可通过 root 选项更改搜索位置。
const reader = new ConnectionOptionsReader({ root: "/path/to/config/" })
// 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_CONNECTION、TYPEORM_URL 等 TYPEORM_* 环境变量配置连接的功能已被移除。同时不再支持 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 值会原样传递。完整细节请参阅空值与未定义处理。
哈希算法
In TypeORM v1, the SHA1 hashing algorithm used for hashing is applied directly to the input. In the previous versions (v0.3), the input was first encoded using encodeURIComponent.
If you use TypeORM's query result cache, existing cached entries will be invalidated after upgrading because the hash function produces different output. Caches will be rebuilt automatically — you may see a brief increase in cache misses.
If you have table and columns names containing special characters, you can use @typeorm/legacy-naming-strategies to avoid changes in the database:
import { NamingStrategyV03 } from "@typeorm/legacy-naming-strategies"
const dataSource = new DataSource({
...
namingStrategy: new NamingStrategyV03(),
...
})
通配符模式
全局模式(用于实体/迁移文件发现)现由 tinyglobby 替代 glob 处理。对多数项目而言这是无缝替换。
外键非空时的 orphanedRowAction: "nullify"
当 orphanedRowAction 设置为 "nullify"(默认值)且外键列为非空约束时,孤儿记录现在会被删除而非抛出数据库约束违反错误。此前 TypeORM 会尝试将外键设为 null,这在非空列上必然失败。
此规则仅当关系已在实体实例上加载时才适用。TypeORM 不会自动加载关系 —— 它只会遍历对象上已填充的关系值。为确保处理孤立子记录,请在调用 remove 或 save 前加载关系:
const parent = await manager.findOne(Parent, {
where: { id: 1 },
relations: { children: true },
})
await manager.remove(parent)
若关系未加载(即属性为 undefined),TypeORM 将无法检测或删除孤立子记录,可能导致外键约束冲突。
若您依赖此错误防止意外删除子记录,请在关系上设置 orphanedRowAction: "disable" 以保留旧行为。
级联删除现支持一对多关系
现在,在一对多关系上调用 manager.remove(entity) 并设置 cascade: true 或 cascade: ["remove"] 时,会先正确删除子实体再删除父实体。此前子实体不会被级联删除,导致 DELETE 操作因外键约束冲突而失败。此外,级联操作现在会限定于匹配的操作类型 —— 例如,save() 仅遵循 insert/update 级联,不再遍历仅 remove 的关系。
多对多关联行与软删除实体
对具有多对多关系的软删除实体调用 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,则不会执行关联比较,关联行将保持不变。
日志记录器
FileLogger 现在由底层平台(如 NodeJS)处理路径,而非相对于应用根目录解析路径。若应用未从根目录启动,可提供绝对路径(或相对于 process.cwd() 的路径):
const dataSource = new DataSource({
logger: new FileLogger("all", { logPath: "/path/to/file.log" }),
})
列(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
OneToMany、ManyToMany 和反向 OneToOne 关系始终使用 LEFT JOIN(无论 nullable 设置如何),因为这些关联类型在当前表上没有连接列。
软删除例外: 若关联实体包含 @DeleteDateColumn,即使对于 nullable: false 的关系也会使用 LEFT JOIN(除非显式设置 withDeleted: true)。这能防止软删除的关联实体过滤掉其父行。
仓储(Repository)
findOneById
已弃用的 findOneById 方法已从 EntityManager、Repository、BaseEntity、MongoEntityManager 和 MongoRepository 中移除。请改用 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 已移除
EntityManager、Repository 和 BaseEntity 中已弃用的 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、@EntityRepository 和 getCustomRepository 已移除
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 })
},
})
以下错误类也被移除:CustomRepositoryDoesNotHaveEntityError、CustomRepositoryCannotInheritRepositoryError、CustomRepositoryNotFoundError。
@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 选项
已弃用的 FindOneOptions 和 FindManyOptions 中的 join 属性及 JoinOptions 接口已被移除。
leftJoinAndSelect → relations
若曾使用 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。若曾使用 innerJoinAndSelect、innerJoin 或 leftJoin(不带选择),请切换到 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)
移除 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.loadedTables 和 loadedViews 已移除
// Before
const tables = queryRunner.loadedTables
const views = queryRunner.loadedViews
// After
const tables = await queryRunner.getTables()
const views = await queryRunner.getViews()
注意:替代方案为异步方法,而非同步属性。
容器系统
已弃用的 IoC 容器集成已被移除,包括:useContainer()、getFromContainer()、ContainerInterface、ContainedType 和 UseContainerOptions。
TypeORM 不再内置支持 IoC 容器。typeorm-typedi-extensions 和 typeorm-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-extensions 将 EntityManager 或存储库注入到服务中,现在请直接使用 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 的用户:
| Removed | Replacement |
|---|---|
EntityMetadata.createPropertyPath() (static) | Removed with no public replacement |
EntityMetadata.getValueMap() options param | Remove the third argument — it was never functional |
DriverUtils.buildColumnAlias() | Use DriverUtils.buildAlias() |
Broadcaster.broadcastLoadEventsForAll() | No replacement — use individual event subscribers |
QueryExpressionMap.nativeParameters | Use QueryExpressionMap.parameters |
RdbmsSchemaBuilder.renameTables() | Removed |