TypeORM 简单使用

TypeORM

是一个 ORM (Object Relation Mapping) 框架。

支持 Active Record, 和 Data Mapper 模式。

例子

//  models
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column()
  age: number;
}

// 逻辑操作

const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.age = 25;
await repository.save(user);

const allUsers = await repository.find();
const firstUser = await repository.findOne(1); // find by id
const timber = await repository.findOne({ firstName: "Timber", lastName: "Saw" });

await repository.remove(timber);

// ActiveRecord
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm";

@Entity()
export class User extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  firstName: string;

  @Column()
  lastName: string;

  @Column()
  age: number;
}

// ActiveRecord 逻辑操作
const user = new User();
user.firstName = "Timber";
user.lastName = "Saw";
user.age = 25;
await user.save();

const allUsers = await User.find();
const firstUser = await User.findOne(1);
const timber = await User.findOne({ firstName: "Timber", lastName: "Saw" });

await timber.remove();

安装

$ npm install typeorm --save
$ npm install reflect-metadata --save
# 还可以安装 @types/node 增强类型提示
$ npm install @types/node --save

# 根据喜好安装数据库

# MySQL 或 MariaDB
$ npm install mysql --save # 也可以安装 mysql2

# PostgreSQL
$ npm install pg --save

# SQLite
$ npm install sqlite3 --save

# Microsoft SQL Server
$ npm install mssql --save

# sql.js
$ npm install sql.js --save

# Oracle
$ npm install oracledb --save

# 试验性的 MongoDB
$ npm install mogodb --save
// 同时还需要全局导入 reflect-metadata
// e.g. app.ts
import "reflect-metadata"
{
  // 需要保证再 tsconfig.json 开启配置 并启用 es6
  ...
  "emitDecoratorMetadata": true,
 "experimentalDecorators": true,
  ...
}

快速开始

仅限于 NodeJS 环境,可以使用 CLI 生成项目。

npm install typrorm -g # 全局安装 CLI

转到项目目录。

# ProjectName 为项目名称
# database 为 使用的数据库 可以是 mysql, mariadb, postgres, sqlite, mssql, oracle, mongodb, cordova, react-native, expo, nativescript.
$ typeorm init --name ProjectName --database mysql

会生成以下目录。

# 省略了一些文件
# 如果在现有 NodeJS 项目运行 可能会覆盖已有文件
ProjectName
|- src
|   |- entity        -- 实体(数据库模式)目录
|   |  |- User.ts    -- 示例实体
|   |- migration     -- 存储迁移目录
|   |- index.ts      -- 入口文件
|- ormconfig.json    -- ORM 和数据库连接配置

编辑数据库连接配置。

{
  "type": "mysql",                            // 使用的数据库
  "host": "localhost",                        // 数据库地址
  "port": 3306,                               // 数据库端口
  "username": "test",                         // 数据库用户名
  "password": "test",                         // 数据库用户密码
  "database": "test",                         // 使用的数据库名称
  "synchronize": true,                        // 每次运行时同步
  "logging": false,                           // 日志
  "entities": ["src/entity/**/*.ts"],         // 实体目录
  "migrations": ["src/migration/**/*.ts"],    // 存储迁移目录
  "subscribers": ["src/subscriber/**/*.ts"]   // 订阅器目录
}

使用

import {
  Entity, // 实体装饰器
  Column, // 列装饰器
  PrimaryColumn, // 主键装饰器
  PrimaryGeneratedColumn // 自增主键装饰器
} from "typeorm";

// User 模型
@Entity() // 声明当前类作为一个实体
export class User {
  // 每个实体必须有一个主键
  // @PrimaryColumn()
  // 使用 PrimaryGeneratedColumn 创建一个自增主键
 @PrimaryGeneratedColumn()
  id: number

  // 使用 Column 声明一个字段列
  // 列类型根据声明类型自动推断
  // 也可以手动传入类型
  // 需要注意!列类型是特定于数据库的
  @Column({
    length: 100
  })
  name: string

  @Column("text")
  description: string
}

连接数据库

import "reflect-metadata";
import { createConnection } from "typeorm";
import { User } from "./entity/User";

createConnection({
  type: "mysql",
  host: "localhost",
  port: 3306,
  username: "root",
  password: "admin",
  database: "test",
  entities: [User],
  synchronize: true,
  logging: false
})
  .then(connection => {
    // 这里可以写实体操作相关的代码
  })
  .catch(error => console.log(error));

添加和插入

import { createConnection } from "typeorm";
import { User } from "./entity/User";

createConnection({/** ... */})
 // 也可以使用 async/await 语法
 .then(connection => {
   const user = new User()
    user.name = 'n'
   user.description = 'd'
   return connection
      .manager
      .save(user)
    .then(user => {
       console.log(`userid is ${user.id}`)
    })
 })

实体管理

import { createConnection } from "typeorm";
import { User } from "./entity/User";

createConnection({/** ... */})
  .then(async connection => {
   // 使用 EntityManager 可以操作当前应用任何实体
    let users = await connection.manager.find(User);
    console.log("All User from the db: ", users);
  })
  .catch(error => console.log(error));

存储库

每个实体都有自己的存储库,可以处理实体的所有操作。

当经常处理实体时, 存储库会比实体管理更方便。

import { createConnection } from "typeorm";
import { User } from "./entity/User";

createConnection({/** ... */})
 .then(async connection => {
   const u = new User()

    u.name = 'n'
   u.description = 'd'

    const repository = connection.getRepository(User)

    await repository.save(u)
   const allUser = await repository.find()
 })

查询操作

// ...

const repository = connection.getRepository(User)

repository.find() // find all
repository.findOne(1) // find first
repository.findOne({name: 'n'}) // find first name eq n
repository.find({name: 'n'}) // find all name eq n

// find all user and count user quantity
const [allUser, userCount] = repository.findAndCount()

// ...

更新操作

// ...
const first = repository.findOne(1)
first.name = 'name'
await repository.save(first)
// ...

删除操作

// ...
const first = repository.findOne(1)
await repository.remove(first)
// ...

关系

一对一

import {
  Entity, // 实体装饰器
  Column, // 列装饰器
  PrimaryColumn, // 主键装饰器
  PrimaryGeneratedColumn, // 自增主键装饰器
  OneToOne, // 一对一关系装饰器
  JoinColumn // 关系外键装饰器
} from "typeorm";

// 一个用户有一个对应的行为记录
@Entity()
export class User {
 @PrimaryGeneratedColumn()
  id: number

  @Column({
    length: 100
  })
  name: string

  @Column("text")
  description: string
}

// 一个行为记录对应一位用户
@Entity()
export class UserRecord {
  @PrimaryGeneratedColumn()
  id: number

  @Column()
  time: Date

  @Column()
  exec: number

  // type 不含内容仅为了可读
  // OneToOne 建立了 UserRecord 和 User 之间的一对一关系
  @OneToOne(type => User)
  // JoinColumn() 表明了实体键的对应关系 关系可单双向,但只有一方为拥有者
  // 在此关系的所有者方面需要此装饰器
  @JoinColumn()
  user: User
}

保存一对一

// ...
const u = User()
u.name = 'n'
u.description = 'd'

const r = UserRecord()
r.time = new Date().toString()
r.exec = 2
// 连接两者
r.user = u

// 获取实体存储库
const uR = connection.getRepository(User)
const uRR = connection.getRepository(UserRecord)

// 首先保存 User
await uR.save(u)
await uRR.save(r)

// ...

反向关系

关系是可单双向的。

目前,UserRecord 可以单方向访问 User,而 User 则不能。因此转换为双向关系。

// user.entity.ts

// ...
export class User {
  @OneToOne(
    type => UserRecord,
    // 用于指定反向关系名称
    userRecord => userRecord.user
  )
 record: UserRecord
}

// user-record.entity.ts

// ...
export class UserRecord {
  @OneToOne(
    type => User,
    user => user.record
  )
  @JoinColumn()
  user: User
}

取出关系对象数据

在一个查询中同时获取 User 和 UserRecord 的数据有两个方法。

find*QueryBuilder

// ...
const r = connection.getRepository(User)
// u 为来自数据库的 User 数据列表
// 每项元素都会带有各自的 UserRecord
const u = await r.find({relations: 'record'})
// ...

使用更复杂的查询时,应该使用 QueryBuilder

// ...
connection
 // QueryBuilder 允许创建和执行几乎任何复杂性的 SQL
 // user 和 record 分别为所选的 user 别名
 // 可使用别名访问数据列和属性
 .getRepository(User)
 .createQueryBuilder('user')
 .innerJoinAndSelect('user.record', 'record')
 .getMany()
// ...

自动保存

在关系设置中设置对应选项,即可在保存其他对象的同时自动保存相关对象。

export class User {
  // ...

  @OneToOne(
    type => UserRecord,
    record => record.user,
    {
      // 开启自动保存
      cascade: true
    }
  )
  record: UserRecord
}

多对一

import { Entity, Column, PrimaryGeneratedColumn, OneToMany, ManyToOne, JoinColumn } from "typeorm";

// 拥有者
// 一个用户可以发布多篇文章
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number

  // 一对多
  @OneToMany(
   type => Article,
    article => article.author
  )
  articles: Article[]
}

// 被拥有者
// 一篇文章只能有一个作者
@Entity()
export class Article {
  @PrimaryGeneratedColumn()
  id: number;

  // 多对一
  @ManyToOne(
   type => User,
    user => user.articles
  )
  author: User
}

多对多

import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable } from "typeorm";

// 一篇文章可以有多个标签
@Entity()
export class Article {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToMany(
   type => Tag,
    tag => tag.articles
  )
  @JoinTable() // 指定此处为关系的所有者
  tag: Tag[]
}

// 一个标签可以被打给多个文章
@Entity()
export class Tag {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToMany(
   type => Article,
    article => article.tag
  )
  articles: Article[]
}

TypeORM 简单使用
http://localhost:8080/archives/d07681b2-28a8-44fa-aede-77d110a15eaf
作者
inksha
发布于
2024年09月21日
许可协议