本页面由 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",
// ...
},
// ...
})
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 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)
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) 方法被移除。
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_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 值会原样传递。完整细节请参阅空值与未定义值处理。
哈希算法
内部哈希实现已替换为 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
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)
原始 SQL 表达式方法中分号被拒绝
所有查询构建器(SelectQueryBuilder、UpdateQueryBuilder、SoftDeleteQueryBuilder 及基础 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.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 |
|---|---|
Broadcaster.broadcastLoadEventsForAll() | No replacement — use individual event subscribers |
DriverUtils.buildColumnAlias() | Use DriverUtils.buildAlias() |
EntityMetadata.createPropertyPath() (static) | Removed with no public replacement |
QueryExpressionMap.nativeParameters | Use QueryExpressionMap.parameters |
RdbmsSchemaBuilder.renameTables() | Removed |