视图实体

什么是视图实体?

视图实体是一个映射到数据库视图的类。 你可以通过定义一个新类来创建一个视图实体,并用@ViewEntity()来标记:

@ViewEntity() 接收以下参数:

  • name - 视图名称。 如果未指定,则从实体类名生成视图名称。
  • database - 所选DB服务器中的数据库名称。
  • schema - 架构名称。
  • expression - 视图定义。 必需参数

expression可以是带有正确转义的列和表的字符串,取决于所使用的数据库(示例中为postgres):

  1. @ViewEntity({
  2. expression: `
  3. SELECT "post"."id" "id", "post"."name" AS "name", "category"."name" AS "categoryName"
  4. FROM "post" "post"
  5. LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
  6. `
  7. })

或者是QueryBuilder的一个实例

  1. @ViewEntity({
  2. expression: (connection: Connection) => connection.createQueryBuilder()
  3. .select("post.id", "id")
  4. .addSelect("post.name", "name")
  5. .addSelect("category.name", "categoryName")
  6. .from(Post, "post")
  7. .leftJoin(Category, "category", "category.id = post.categoryId")
  8. })

注意: 由于驱动程序的限制,不支持参数绑定。请改用文字参数。

  1. @ViewEntity({
  2. expression: (connection: Connection) => connection.createQueryBuilder()
  3. .select("post.id", "id")
  4. .addSelect("post.name", "name")
  5. .addSelect("category.name", "categoryName")
  6. .from(Post, "post")
  7. .leftJoin(Category, "category", "category.id = post.categoryId")
  8. .where("category.name = :name", { name: "Cars" }) // <-- 这是错的
  9. .where("category.name = 'Cars'") // <-- 这是对的
  10. })

每个视图实体都必须在连接选项中注册:

  1. import {createConnection, Connection} from "typeorm";
  2. import {UserView} from "./entity/UserView";
  3. const connection: Connection = await createConnection({
  4. type: "mysql",
  5. host: "localhost",
  6. port: 3306,
  7. username: "test",
  8. password: "test",
  9. database: "test",
  10. entities: [UserView]
  11. });

或者你可以指定包含所有实体的整个目录 - 所有实体都将被加载:

  1. import {createConnection, Connection} from "typeorm";
  2. const connection: Connection = await createConnection({
  3. type: "mysql",
  4. host: "localhost",
  5. port: 3306,
  6. username: "test",
  7. password: "test",
  8. database: "test",
  9. entities: ["entity/*.js"]
  10. });

视图实体列

要将视图中的数据映射到正确的实体列,必须使用@ViewColumn()装饰器标记实体列,并将这些列指定为select语句别名。

字符串表达式定义的示例:

  1. import {ViewEntity, ViewColumn} from "typeorm";
  2. @ViewEntity({
  3. expression: `
  4. SELECT "post"."id" AS "id", "post"."name" AS "name", "category"."name" AS "categoryName"
  5. FROM "post" "post"
  6. LEFT JOIN "category" "category" ON "post"."categoryId" = "category"."id"
  7. `
  8. })
  9. export class PostCategory {
  10. @ViewColumn()
  11. id: number;
  12. @ViewColumn()
  13. name: string;
  14. @ViewColumn()
  15. categoryName: string;
  16. }

使用QueryBuilder的示例:

  1. import {ViewEntity, ViewColumn} from "typeorm";
  2. @ViewEntity({
  3. expression: (connection: Connection) => connection.createQueryBuilder()
  4. .select("post.id", "id")
  5. .addSelect("post.name", "name")
  6. .addSelect("category.name", "categoryName")
  7. .from(Post, "post")
  8. .leftJoin(Category, "category", "category.id = post.categoryId")
  9. })
  10. export class PostCategory {
  11. @ViewColumn()
  12. id: number;
  13. @ViewColumn()
  14. name: string;
  15. @ViewColumn()
  16. categoryName: string;
  17. }

完整示例

让我们创建两个实体和一个包含来自这些实体的聚合数据的视图:

  1. import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
  2. @Entity()
  3. export class Category {
  4. @PrimaryGeneratedColumn()
  5. id: number;
  6. @Column()
  7. name: string;
  8. }
  1. import {Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn} from "typeorm";
  2. import {Category} from "./Category";
  3. @Entity()
  4. export class Post {
  5. @PrimaryGeneratedColumn()
  6. id: number;
  7. @Column()
  8. name: string;
  9. @Column()
  10. categoryId: number;
  11. @ManyToOne(() => Category)
  12. @JoinColumn({ name: "categoryId" })
  13. category: Category;
  14. }
  1. import {ViewEntity, ViewColumn} from "typeorm";
  2. @ViewEntity({
  3. expression: (connection: Connection) => connection.createQueryBuilder()
  4. .select("post.id", "id")
  5. .addSelect("post.name", "name")
  6. .addSelect("category.name", "categoryName")
  7. .from(Post, "post")
  8. .leftJoin(Category, "category", "category.id = post.categoryId")
  9. })
  10. export class PostCategory {
  11. @ViewColumn()
  12. id: number;
  13. @ViewColumn()
  14. name: string;
  15. @ViewColumn()
  16. categoryName: string;
  17. }

然后用数据填充这些表并从PostCategory视图请求所有数据:

  1. import {getManager} from "typeorm";
  2. import {Category} from "./entity/Category";
  3. import {Post} from "./entity/Post";
  4. import {PostCategory} from "./entity/PostCategory";
  5. const entityManager = getManager();
  6. const category1 = new Category();
  7. category1.name = "Cars";
  8. await entityManager.save(category1);
  9. const category2 = new Category();
  10. category2.name = "Airplanes";
  11. await entityManager.save(category2);
  12. const post1 = new Post();
  13. post1.name = "About BMW";
  14. post1.categoryId = category1.id;
  15. await entityManager.save(post1);
  16. const post2 = new Post();
  17. post2.name = "About Boeing";
  18. post2.categoryId = category2.id;
  19. await entityManager.save(post2);
  20. const postCategories = await entityManager.find(PostCategory);
  21. const postCategory = await entityManager.findOne(PostCategory, { id: 1 });

postCategories的结果将是:

  1. [ PostCategory { id: 1, name: 'About BMW', categoryName: 'Cars' },
  2. PostCategory { id: 2, name: 'About Boeing', categoryName: 'Airplanes' } ]

postCategory中:

  1. PostCategory { id: 1, name: 'About BMW', categoryName: 'Cars' }