TypeORM中能否用同一类同时标记Entity与ViewEntity?
关于TypeORM中Entity与ViewEntity的疑问解答
一、为什么要分开定义Entity和ViewEntity?能否同时标记在同一个类上?
Entity和ViewEntity在TypeORM中对应数据库的两种完全不同的对象,定位和用途差异极大:
- Entity:对应数据库的物理表,是原始数据的持久化载体,支持完整的CRUD(增、删、改、查)操作,用于存储和修改数据;
- ViewEntity:对应数据库的视图,是基于一个或多个表的查询结果集,通常作为只读的虚拟表使用,主要用于封装复杂查询逻辑,多数场景下不支持写入(可更新视图有严格限制)。
TypeORM不支持在同一个类上同时使用@Entity和@ViewEntity装饰器,二者是互斥的。因为TypeORM的元数据系统会分别为实体和视图维护不同的元信息,同时标记会导致元数据冲突,引发各种不可预期的错误(比如你遇到的类型不支持报错)。
二、合并代码报错的原因分析
你遇到的DataTypeNotSupportedError: Data type "" in "Person.age" is not supported by "mysql" database.报错,根源在于:
同时使用@Entity装饰器时,TypeORM会将这个类当作数据库表的实体来处理,要求每个字段都必须通过@Column注解定义数据库存储类型。但你的age字段只有@ViewColumn标记,没有@Column注解,TypeORM无法识别它的数据库类型,因此抛出数据类型为空的错误。而视图中的age是SQL计算生成的字段,不需要实体表的列定义,但@Entity的存在强制要求了这一点。
三、合并后查询的行为(假设能成功)
如果强行合并(忽略报错),使用dataSource.getRepository(Person)查询时,TypeORM会优先按照Entity的定义处理:
- 它会去查询
person物理表,而不是PersonView视图; - 返回的结果只会包含实体表中存在的字段(id、name、birthDate),不会包含
age字段,因为实体表中没有这个列; - 实际行为会因为元数据混乱变得不可预测,大概率会持续报错。
正确的实现方式
分开定义实体和视图,各司其职:
1. Person实体类(对应物理表)
@Entity() export class Person { @PrimaryGeneratedColumn({ type: 'int', unsigned: true }) id: number; @Column({ length: 50 }) name: string; @Column({ type: 'date' }) birthDate: string; }
2. PersonView视图实体类(对应视图)
@ViewEntity({ name: 'PersonView', expression: `SELECT id, name, birthDate, TIMESTAMPDIFF(YEAR,birthDate,CURDATE()) AS age FROM person;` }) export class PersonView { @ViewColumn() id: number; @ViewColumn() name: string; @ViewColumn() birthDate: string; @ViewColumn() age: number; }
3. 查询方式
- 操作物理表(增删改查):
dataSource.getRepository(Person) - 查询视图:
dataSource.getRepository(PersonView).find()或dataSource.manager.find(PersonView)
内容的提问来源于stack exchange,提问作者Ahmad Alkaf
相关产品推荐
相关产品推荐

