如何利用TypeORM内部转换器将原生查询结果转换为实体?
TypeORM原生查询结果转实体的问题解决
问题背景
TypeORM未提供官方API将EntityManager.query或QueryBuilder.execute返回的原生结果转换为实体,尝试使用内部类RawSqlResultsToEntityTransformer和PlainObjectToNewEntityTransformer时遇到以下问题:
- 使用
RawSqlResultsToEntityTransformer:原生结果有数据,但转换后实体数组为空; - 使用
PlainObjectToNewEntityTransformer:转换后的User实体仅包含id和email字段,其余字段缺失。
代码参考信息
User实体定义
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() email: string; @Column() username: string; @Column() password: string; @Column({ default: false }) isActive: boolean; }
原尝试转换代码
import { getManager, RawSqlResultsToEntityTransformer, PlainObjectToNewEntityTransformer } from "typeorm"; import { User } from "./entities/User"; async function testRawQuery() { const manager = getManager(); // 执行更新并返回所有字段 const rawResult = await manager.query( "UPDATE user SET username = ? WHERE id = ? RETURNING *", ["new_username", 1] ); // 尝试用RawSqlResultsToEntityTransformer转换 const transformer1 = new RawSqlResultsToEntityTransformer(manager.connection, User); const entities1 = transformer1.transform(rawResult); console.log("RawSqlResultsToEntityTransformer转换结果:", entities1); // 输出为空数组 // 尝试用PlainObjectToNewEntityTransformer转换 const transformer2 = new PlainObjectToNewEntityTransformer(); const entities2 = rawResult.map(item => transformer2.transform(item, User)); console.log("PlainObjectToNewEntityTransformer转换结果:", entities2); // 仅id和email存在 } testRawQuery();
控制台输出示例
原生查询返回结果: [ { id: 1, email: 'test@example.com', username: 'new_username', password: 'hashed_pw', is_active: true } ] RawSqlResultsToEntityTransformer转换结果: [] PlainObjectToNewEntityTransformer转换结果: [ User { id: 1, email: 'test@example.com' } ]
正确转换方法
1. 正确使用RawSqlResultsToEntityTransformer
该转换器必须传入实体元数据而非实体类本身,同时要保证原生结果的字段名与实体元数据中定义的数据库列名完全匹配(注意大小写、蛇形/驼峰命名差异)。
修正代码:
import { getManager, RawSqlResultsToEntityTransformer } from "typeorm"; import { User } from "./entities/User"; async function correctRawTransformer() { const manager = getManager(); // 获取实体元数据 const userMetadata = manager.connection.getMetadata(User); const rawResult = await manager.query( "UPDATE user SET username = $1 WHERE id = $2 RETURNING *", ["new_username", 1] ); const transformer = new RawSqlResultsToEntityTransformer(manager.connection, userMetadata); // 传入元数据完成转换 const entities = transformer.transform(rawResult, userMetadata); console.log("正确转换结果:", entities); }
2. 正确使用PlainObjectToNewEntityTransformer
同样需要传入实体元数据,且要确保原生对象的键名与实体的属性名(驼峰格式)完全匹配,若数据库返回蛇形命名字段,需手动转换为驼峰。
修正代码:
import { getManager, PlainObjectToNewEntityTransformer } from "typeorm"; import { User } from "./entities/User"; async function correctPlainTransformer() { const manager = getManager(); const userMetadata = manager.connection.getMetadata(User); const rawResult = await manager.query( "UPDATE user SET username = ? WHERE id = ? RETURNING *", ["new_username", 1] ); // 蛇形字段转实体驼峰属性 const normalizedResult = rawResult.map(item => ({ id: item.id, email: item.email, username: item.username, password: item.password, isActive: item.is_active })); const transformer = new PlainObjectToNewEntityTransformer(); const entities = normalizedResult.map(item => transformer.transform(item, userMetadata)); console.log("正确转换结果:", entities); }
3. 替代方案:手动映射(更稳定)
如果内部API使用不稳定,直接手动映射原生结果到实体是更可靠的选择:
import { getManager } from "typeorm"; import { User } from "./entities/User"; async function manualMapping() { const manager = getManager(); const rawResult = await manager .createQueryBuilder() .update(User) .set({ username: "new_username" }) .where("id = :id", { id: 1 }) .returning("*") .execute(); // 手动将原生对象转换为实体实例 const users = rawResult.raw.map(item => { const user = new User(); user.id = item.id; user.email = item.email; user.username = item.username; user.password = item.password; user.isActive = item.is_active; return user; }); console.log("手动映射结果:", users); }
注意事项
- 数据库兼容性:MySQL不支持
UPDATE ... RETURNING语法,需通过其他方式获取更新后数据;PostgreSQL、SQLite等支持该语法; - 实体列命名:若实体列通过
@Column({ name: "db_column_name" })指定了数据库列名,原生查询返回的字段名必须与该值一致; - 内部API风险:
RawSqlResultsToEntityTransformer和PlainObjectToNewEntityTransformer属于TypeORM内部类,未提供官方维护,版本升级时可能出现兼容问题。
内容的提问来源于stack exchange,提问作者Jonas Grønbek
相关产品推荐
相关产品推荐

