TypeORM中如何实现单个实体属性到多数据库列的映射?
问题:TypeORM EntitySchema 映射多属性值对象到多列
我正在使用TypeScript开发Node.js项目,结合TypeORM与领域驱动设计(DDD)进行开发。目前我通过EntitySchema的transformer属性将实体属性映射到持久化列,但无法找到方法将一个自定义类型属性映射到多列而非单列。
我的模型:
export default class User { public constructor(public id: UuidValueObject, public name: PersonName, public address: Address) {} }
所有属性均为值对象,其中除address包含多个属性(如street、postalCode等)外,其余值对象仅含单个属性。
我的EntitySchema映射代码:
export const UserEntity = new EntitySchema<User>({ name: "User", tableName: "users", target: User, columns: { id: { type: String, primary: true, transformer: { to: (value: UuidValueObject): string => value.id.toString(), from: (value: string): UuidValueObject => UuidValueObject.create(value) } }, name: { type: String, nullable: true, transformer: { to: (value: PersonName): any => value.toString(), from: (value: any): PersonName => PersonName.create(value) } } } });
请问如何将模型中的address属性添加到EntitySchema的columns中,使其映射为多个独立的列?
解决方案
TypeORM的EntitySchema本身不支持直接将单个对象属性映射到多列,但可以通过两种方式实现需求:
方法一:利用实体生命周期钩子处理转换
在EntitySchema中定义Address对应的所有数据库列,然后通过afterLoad、beforeInsert、beforeUpdate钩子完成值对象与列数据的双向转换。
修改后的EntitySchema代码:
export const UserEntity = new EntitySchema<User>({ name: "User", tableName: "users", target: User, columns: { id: { type: String, primary: true, transformer: { to: (value: UuidValueObject): string => value.id.toString(), from: (value: string): UuidValueObject => UuidValueObject.create(value) } }, name: { type: String, nullable: true, transformer: { to: (value: PersonName): any => value.toString(), from: (value: any): PersonName => PersonName.create(value) } }, // 定义Address对应的数据库列 street: { type: String, nullable: true }, postalCode: { type: String, nullable: true }, city: { type: String, nullable: true } }, // 从数据库加载后,将列数据组装为Address值对象 afterLoad: (entity: User) => { entity.address = Address.create({ street: entity['street'], postalCode: entity['postalCode'], city: entity['city'] }); }, // 插入前,拆分Address值对象到对应列 beforeInsert: (entity: User) => { if (entity.address) { entity['street'] = entity.address.street; entity['postalCode'] = entity.address.postalCode; entity['city'] = entity.address.city; } }, // 更新前,同步Address值对象到对应列 beforeUpdate: (entity: User) => { if (entity.address) { entity['street'] = entity.address.street; entity['postalCode'] = entity.address.postalCode; entity['city'] = entity.address.city; } } });
这种方式不需要修改User类的公共接口,外部依然通过user.address操作值对象,钩子会自动处理与数据库列的转换逻辑。
方法二:通过类的getter/setter封装值对象
给User类添加对应数据库列的私有字段,通过getter和setter封装Address值对象的访问,让EntitySchema直接映射这些私有字段。
修改User类:
export default class User { // 对应数据库列的私有字段 private _street?: string; private _postalCode?: string; private _city?: string; public constructor(public id: UuidValueObject, public name: PersonName, address: Address) { // 通过setter初始化address this.address = address; } // 封装address属性的getter,从私有字段组装值对象 get address(): Address { return Address.create({ street: this._street, postalCode: this._postalCode, city: this._city }); } // 封装address属性的setter,拆分值对象到私有字段 set address(address: Address) { this._street = address.street; this._postalCode = address.postalCode; this._city = address.city; } }
修改EntitySchema:
export const UserEntity = new EntitySchema<User>({ name: "User", tableName: "users", target: User, columns: { id: { type: String, primary: true, transformer: { to: (value: UuidValueObject): string => value.id.toString(), from: (value: string): UuidValueObject => UuidValueObject.create(value) } }, name: { type: String, nullable: true, transformer: { to: (value: PersonName): any => value.toString(), from: (value: any): PersonName => PersonName.create(value) } }, // 映射私有字段到数据库列,通过name指定列名 _street: { type: String, nullable: true, name: 'street' }, _postalCode: { type: String, nullable: true, name: 'postalCode' }, _city: { type: String, nullable: true, name: 'city' } } });
这种方式更符合面向对象的封装原则,私有字段直接与数据库列映射,getter/setter自动处理值对象的转换,无需额外钩子逻辑。
内容的提问来源于stack exchange,提问作者Jose Luis
相关产品推荐
相关产品推荐

