# ORM

ORM 提供了一系列抽象方法,可以对象化操作数据库

# 模型定义

模型可以理解为数据抽象层,模型提供了许多便捷的数据操作接口

# 定义

定义一个模型类可以用来描述一个模型, 通过 @Table() 来定义一个模型,下面是一个 User 模型:

import { Table, Column, AutoIncrementPrimaryColumn } from '@tiger/common';

@Table('users')
export class User {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;
}

通过 Table 的参数来指定映射的表名

# 字段

通过字段定义可以描述模型对应的数据库字段

import { Table, Column, AutoIncrementPrimaryColumn } from '@tiger/common';

@Table('users')
export class User extends Entity {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  age: number;

  @Column()
  gender: 'M' | 'F';
}

# 主键

每个模型都至少需要配置一个主键

普通主键

import { Table, Column, PrimaryColumn } from '@tiger/common';

@Table('users')
export class User extends Entity {

  @PrimaryColumn()
  id: number;

  @Column()
  name: string;
}

自增主键

import { Table, Column, AutoIncrementPrimaryColumn } from '@tiger/common';

@Table('users')
export class User {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;
}

# 模型实例

控制器中可以使用 model 方法来创建

import { Controller, Get, model } from '@tiger/common'
import { UserModel } from '../models/user'

@Controller()
export default class {
  @Get()
  index() {
    const model = model(UserModel)
  }
}

或者通过继承 BaseModel 来直接实例化

import { BaseModel, Table, Column, AutoIncrementPrimaryColumn } from '@tiger/common';

@Table('users')
export class UserModel extends BaseModel {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;
}

直接实例化

import { Controller, Get } from '@tiger/common'
import { UserModel } from '../models/user'

@Controller()
export default class {
  @Get()
  index() {
    const model = new UserModel();
  }
}

# 查询模型

# 查询单个模型

根据主键值获取单条记录

const user = new UserModel();
const res = await user.get(1);
console.log(res.age)

根据查询构建器获取单条记录

const user = new UserModel();
const res = await user.createQueryBuilder().where('id', 1).first();
console.log(res.age)

# 查询多个模型

遍历主键获取

const user = new UserModel();
for (const id of [1, 2, 3]) {
  const res = await user.get(id);
  console.log(res.age)
}

使用查询构建器查询

const user = new UserModel();
const res = await user.createQueryBuilder().whereIn('id', [1, 2, 3]).find();

for (const item of res) {
  console.log(item.age)
}

# 新增模型

模型数据的新增和数据库的新增数据有所区别,数据库的新增只是单纯的写入数据,而模型的新增则会包含自动时间戳,软删除等功能

# 新增单个模型

const user = new UserModel();
user.name = 'tiger';
user.age = 10';
await user.save();

也可以使用 create 方法传入数据对象

const user = new UserModel();
await user.create({
  name: 'tiger',
  age: 10
});

# 更新模型

更新数据前我们需要先取出数据,然后进行更新

const user = new UserModel();
const res = await user.get(1);
res.name = 'tiger'
res.age = 10
await res.save();

save 方法会自动判断是更新还是新增操作

不要调用 save 方法进行多次数据写入。


# 删除模型

# 删除当前模型

使用 delete 方法删除当前模型, 删除前需要先取出模型

const user = new UserModel();
const res = await user.get(1);
await res.delete();

# 根据主键删除

使用 destroy 根据主键删除模型

const user = new UserModel();
user.destroy(1, 2, 3)

# 自动时间戳

模型支持配置自动插入/更新时间戳字段的能力

# 自动插入时间戳

该模型在每次创建数据的时候,都会自动插入 created_at 为当前时间戳,默认 int 类型:

import { BaseModel, Table, Column, AutoIncrementPrimaryColumn, CreateTimestampColumn } from '@tiger/common';

@Table('users')
export class UserModel extends BaseModel {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @CreateTimestampColumn()
  created_at: number;
}

如果需要修改为其他类型的时间戳字段,可以通过 CreateTimestampColumn 的参数修改

支持 inttimestampdatetimeyeardatetime

import { BaseModel, Table, Column, AutoIncrementPrimaryColumn, CreateTimestampColumn } from '@tiger/common';

@Table('users')
export class UserModel extends BaseModel {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @CreateTimestampColumn('timestamp')
  created_at: string;
}

# 自动更新时间戳

该模型在每次更新数据的时候,都会自动修改 updated_at 为当前时间戳,默认 int 类型:

import { BaseModel, Table, Column, AutoIncrementPrimaryColumn, UpdateTimestampColumn } from '@tiger/common';

@Table('users')
export class UserModel extends BaseModel {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @UpdateTimestampColumn()
  updated_at: number;
}

# 软删除

在实际项目中,对数据频繁使用删除操作会导致性能问题,软删除的作用就是把数据加上删除标记,而不是真正的删除,同时也便于需要的时候进行数据的恢复

通过配置软删除字段来开启软删除功能, 示例模型在删除操作的时候会自动更新 deleted_at 字段为当前时间戳,默认 int 类型

import { BaseModel, Table, Column, AutoIncrementPrimaryColumn, SoftDeleteColumn } from '@tiger/common';

@Table('users')
export class UserModel extends BaseModel {

  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @SoftDeleteColumn()
  deleted_at: number;
}

定义好模型后,我们就可以通过模型的删除方法来进行删除操作, 数据并不会在数据库中真实删除:

const user = new UserModel();
const res = await user.get(1);
await res.delete();

并且无法直接查询该数据

const user = new UserModel();
await user.destory(1);
const res = await user.get(1);
console.log(res)

# 模型输出

一般通过模型查询到的结果否是模型实例, 我们可以通过模型方法来获取模型的真实数据

getAttribute(name)

根据字段名获取字段值

const user = new UserModel();
const res = await user.get(1);
console.log(res.getAttribute('age'))

getAttributes()

获取模型数据

const user = new UserModel();
const res = await user.get(1);
console.log(res.getAttributes())

# 关联关系

通过模型关联操作把数据表的关联关系对象化,关联操作比起常规的数据库联表操作更加直观

# 一对一关联

# 定义关联关系

使用 hasOne(() => Model, { foreignKey: string, localKey: string }) 来定义一对一的模型关联,第一个参数接收一个返回关联模型类的匿名函数 (为了解决循环依赖的问题), foreignKey 用来定义自定义外键名, 默认使用 关联模型表名的单数形式_id , localKey 用来定义主键名, 默认当前模型主键名

定义 User 模型

import { BaseModel, Table, AutoIncrementPrimaryColumn, Column, HasOne } from '@tiger/common';
import { ProfileModel } from './profile';

@Table('users')
export class UserModel extends BaseModel {
  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @HasOne(() => Profile)
  profile: ProfileModel;
}

定义 Profile 模型, 使用 belongsTo() 定义一对一的反向模型关联

import { BaseModel, Table, AutoIncrementPrimaryColumn, Column, BelongsTo } from '@tiger/common';
import { UserModel } from './user';

@Table('users')
export class ProfileModel extends BaseModel {
  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @BelongsTo(() => User)
  user: UserModel;
}

# 一对多关联

使用 hasMany(() => Model, { foreignKey: string, localKey: string }) 来定义一对多的模型关联,第一个参数接收一个返回关联模型类的匿名函数, foreignKey 用来定义自定义外键名, 默认使用 关联模型表名的单数形式_id , localKey 用来定义主键名, 默认当前模型主键名

定义 User 模型

import { BaseModel, Table, AutoIncrementPrimaryColumn, Column, HasMany } from '@tiger/common';
import { ProfileModel } from './profile';

@Table('users')
export class UserModel extends BaseModel {
  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @hasMany(() => ProfileModel)
  profiles: ProfileModel[];
}

定义 Profile 模型, 使用 BelongsTo() 定义一对一的反向模型关联

import { BaseModel, Table, AutoIncrementPrimaryColumn, Column, BelongsTo } from '@tiger/common';
import { UserModel } from './user';

@Table('users')
export class ProfileModel extends BaseModel {
  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @BelongsTo(() => UserModel)
  user: UserModel;
}

# 多对多关联

使用 hasMany(() => Model, { foreignKey: string, localKey: string }) 来定义多对多的模型关联,第一个参数接收一个返回关联模型类的匿名函数, foreignPivotKey 用来定义中间表外键名, 默认使用 主表名的单数形式_id , relatedPivotKey 用来定义中间表关联外键名, 默认使用 关联表名的单数形式_id

定义 User 模型

import { BaseModel, Table, AutoIncrementPrimaryColumn, Column, BelongsToMany } from '@tiger/common';
import { ProfileModel } from './profile';

@Table('users')
export class UserModel extends BaseModel {
  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @BelongsToMany(() => ProfileModel)
  profiles: ProfileModel[];
}

定义 Profile 模型, 使用 belongsToMany() 定义多对多的反向模型关联

import { BaseModel, Table, AutoIncrementPrimaryColumn, Column, BelongsToMany } from '@tiger/common';
import { UserModel } from './user';

@Table('users')
export class ProfileModel extends BaseModel {
  @AutoIncrementPrimaryColumn()
  id: number;

  @Column()
  name: string;

  @BelongsToMany(() => User)
  users: UserModel[];
}

# 关联查询

使用 with 方法查询关联数据

const user = new UserModel();
const res = await user.with('profile').get(1);
console.log(res)