TypeORM

运行在 NodeJS 环境的ORM框架

Posted by page on July 30, 2023

TypeORM

中文文档 https://typeorm.bootcss.com/ (部分api落后)

官方文档 https://typeorm.io/

安装

npm install typeorm --save

npm i reflect-metadata --save

全局引入

require("reflect-metadata");

数据库驱动

npm install mysql2 --save

模型

TypeORM 通过模型创建数据库表

typeorm-model-generator

模型生成插件,通过命令行读取数据库结构逆向出模型

typeorm-model-generator -h localhost -d database1 -u 用户名 -x 密码 -e mysql -o ./entity

实体

一个映射到数据库表(或MongoDB 的集合)的类

Entity 实体由@Entity装饰器装饰的模型,声明主列、表列、自动生成的列,声明列数据类型

具体类型与参数声明详见 https://typeorm.io/entities

// src/entity/TagsGroup.js
const { EntitySchema } = require("typeorm");

module.exports = new EntitySchema({
  name: "TagsGroup",
  tableName: "t_tags_group",
  columns: {
    // 主列
    id: {
      primary: true,
      type: "int",
      generated: true,
    },
    // 表列
    group_name: {
      type: "varchar",
      nullable: true, // 是否可为空,默认false
    },
    // 表列
    create_time: {
      type: "timestamp",
      default: () => "CURRENT_TIMESTAMP", // 默认值
    },
    update_time: {
      type: "timestamp",
      default: () => "CURRENT_TIMESTAMP",
      onUpdate: "CURRENT_TIMESTAMP",
    },
  },
});
// src/entity/TagsGroup.ts
const {
  Entity,
  Column,
  PrimaryColumn,
  PrimaryGeneratedColumn,
} = require("typeorm");

@Entity()
export class TagsGroup {
  // 主列
  @PrimaryGeneratedColumn
  id: number;

  // 表列
  @Column
  group_name: string;

  // 表列
  @CreateDateColumn 
  create_time: string;
}

注: 实体的列相关参数,必须与数据库统一才会生效,可以通过 synchronize 同步实体与数据库(副作用:原来的列数据被清除)或 手动保证统一

数据源

require("reflect-metadata");
const { DataSource } = require("typeorm");

// 连接数据库
const AppDataSource = new DataSource({
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "root",
    password: "root",
    database: "database",
    entities: [`${__dirname}/entity/*.js`], // 加载所有实体
    synchronize: true, // 运行时数据库同步
    logging: false,
  })

AppDataSource.initialize()
  .then(() => console.log("数据库连接并加载所有实体成功"))
  .catch(error => console.log("数据库连接失败", error))
);

Repository

操作权限仅限于指定实体

获取repository

const TagsGroup = require("./entity/TagsGroup");

const AppDataSource = new DataSource(/*...*/)
const tagsGroupRepository = AppDataSource.getRepository(TagsGroup);

新增记录

// 或
const tagsGroup = { group_name: "group1", create_time: new Date() };
// 或
const tagsGroup = tagsGroupRepository.create();
tagsGroup.group_name = "group1";
//......
// 或
const tagsGroup = tagsGroupRepository.create({ 
  group_name: "group1",
  create_time: new Date()
});

tagsGroupRepository.save(tagsGroup); // 不存在则插入,已存在则更新;返回新纪录
tagsGroupRepository.insert(tagsGroup); // 都支持数组

查询记录

const tagsGroup = await tagsGroupRepository.find();
const tagsGroup = await tagsGroupRepository.findBy({ id: 136 });
const tagsGroup = await tagsGroupRepository.findOneBy(136);
// 空值条件
const { IsNull } = require("typeorm");
const tagsGroup = await tagsGroupRepository.findOneBy({ type: IsNull() });

更新记录

const tagsGroup = await tagsGroupRepository.findOneBy(136);
tagsGroup.group_name = "group2";
tagsGroupRepository.save(tagsGroup);

// 或
await repository.update({ group_name: "typeorm" }, { create_time: new Date()})
await repository.update(1, { create_time: new Date() })

删除记录

const tagsGroup = await tagsGroupRepository.findOneBy(136);
tagsGroupRepository.remove(tagsGroup);

Repository APIs: https://typeorm.io/repository-api

EntityManager

实体管理器,实体存储库的集合,对任意实体操作

获取manager

const TagsGroup = require("./entity/TagsGroup");

const AppDataSource = new DataSource(/*...*/);
const manager = AppDataSource.manager;

操作API与 Repository 一致,只是第一个参数指定实体

如:manager.findOneBy(tagsGroup, 136);

Find(options)

查找方法支持 Like Between Any In 等,详见 Find Options

分页

// 用户列表 总记录count
const [userList, total] = await userRepository.findAndCount({
  del: false,
  skip: (pageNum - 1) * pageSize, // 起始offset
  take: pageSize,
  order: { create_time: "INSC" }, // 排序依据
});

关系(Relations)

module.exports = {
  columns: {
    id: { 
      type: "int"
      primary: true,
      generated: true,        
    },
    author_id: { type: "varchar" },
    ......
  },
  relations: {
    author: {  // 虚拟关系列,代表后续通过article.author访问
      type: "many-to-one",
      target: "Author", // 关系实体
      joinColumn: {
        name: "author_id",  // article关系列
        referencedColumnName: "aid",  // 引用列(可选)。默认为关系实体主键  
      },
    },
  },
}
{
  ......
  @ManyToOne(() => Author)
  @JoinColumn()  // 仅关系的拥有方(即拥有另一实体的关系键)需声明JoinColumn
  author: Author;
}
AppDataSource.getRepository(Article)
    .createQueryBuilder("article")
    .innerJoinAndSelect("article.author", "author")
    .select(["article.id", "article.title", "author.name"])
    .where("article.id = :id", { id })
    .getMany();

QueryBuilder

使用优雅便捷的语法构建 SQL 查询,执行并获得自动转换的实体

基础的查询实例

const user = await userRepository
  .createQueryBuilder("user")  // 创建查询构造器,参数为自定义实体别名
  .where("user.id = :id", { id: 1 })  // where等sql关键字
  .getOne();  // getMany、getOne等查询方法

创建

connection.createQueryBuilder().select("user").from(User, "user")

userRepository.createQueryBuilder("user")

manager.createQueryBuilder(User, "user")

操作类型

queryBuilder.select().where().getMany()

queryBuilder.insert().values(values).execute();

queryBuilder.update().set(values).where().execute();

queryBuilder.delete().where().execute();

取值

queryBuilder.getOne/getRawOne()

queryBuilder.getMany/getRawMany()

参数转义

:var 语法

.where("user.name = :name")  
.setParameter("name", "Page");

简写:

.where("user.name = :name", { name: "Page" })

表达式

联查

innerJoin leftJoin