typeorm_【译】关系对象模型 typeorm 上

typeorm 有什么用?typeorm 以操作对象方法的方式与数据库交互,而不是像传统库(mysql)那样需要写 sql 语句。

本文主要说什么?Typeorm README.md 写的很长,往下拉可以看到 "Step-by-Step Guide" 标题,本文翻译这部分内容(非直译),帮助新手一步步熟悉并使用 typeorm。(翻译时 typeorm 版本为 0.2.24。)

准备工作

初始化工程

$ mkdir test-typeorm

$ cd test-typeorm

$ npm init -y

安装依赖包

$ npm install typeorm --save

$ npm install reflect-metadata --save

$ npm install mysql --save

$ npm install typescript --save-dev

$ npm install @types/node --save-dev

typeorm、reflect-metadata 是使用 typeorm 必备依赖;

这里使用 mysql 数据库,所以还要安装 mysql 依赖;

要用到装饰器,typescript、@types/node 是使用 typescript 必备依赖。

搭建 ts 环境

创建 tsconfig.json 配置文件

$ npx tsc --init

修改 tsconfig.json 文件中部分属性

"outDir": "./dist",

"rootDir": "./src",

"strictPropertyInitialization": false,

"emitDecoratorMetadata": true,

"experimentalDecorators": true,

创建 src 目录,源码都写在该目录中,之后目录结构如下:

|-- test-typeorm

|-- src

|-- package.json

|-- tsconfig.json

修改 package.json 文件:

"scripts": {

"dev": "tsc -w"

}

测试

创建文件 src/test.ts,内容如下:

const greet: string = 'hello typeorm'

console.log(greet)

打开终端,执行 $ npm run dev,可以看到根目录下生成了 dist 目录,里面包含编译后的 js 文件。

另打开一个终端,执行 $ node dist/test.js,可以看到控制台打印出问候 "hello typeorm"。

至此,依赖包和环境搭建完成,下面开始进入正题,先来学习如何使用 typeorm 创建数据库表。

Typeorm 生成数据库表

创建 model

与数据库打交道的第一步是需要创建一张数据库表,TypeORM 怎么知道你要创建什么样的表呢?答案是通过 model 类,你的程序代码中的 model 类就是你的数据库表。

创建 src/entity/Photo.ts 文件,内容如下:

export class Photo {

id: number;

name: string;

description: string;

filename: string;

views: number;

isPublished: boolean;

}

你想往数据库里存一些照片,为了做到这个,你首先需要建一张数据库表,typeorm 会根据你的 model 类自动创建表,但并非所有的 models 类都可以创建表,只有那些实现了 entity 的 model 类才有这个特性。

此时您的目录结构如下:

|-- test-typeorm

|-- src

|-- entity

|-- Photo.ts

|-- package.json

|-- tsconfig.json

创建 entity

为你的 model 类添加 @Entity 装饰器,只有符合这种规则的 model 类,typeorm 才会帮助你建表。

将我们的 Photo model 改造成 Photo entity。

import { Entity } from "typeorm";

@Entity()

export class Photo {

id: number;

name: string;

description: string;

filename: string;

views: number;

isPublished: boolean;

}

现在,表已经创立了,但是表中还没有字段,让我们为数据库表创建一些字段。

创建表字段

要为表添加字段,您只需要给 entity 类的属性值添加 @Column 装饰器。

import { Entity, Column } from "typeorm";

@Entity()

export class Photo {

@Column()

id: number;

@Column()

name: string;

@Column()

description: string;

@Column()

filename: string;

@Column()

views: number;

@Column()

isPublished: boolean;

}

现在,id、name、description、filename、views 和 isPublished 列已经被添加到 photo 表中了。 数据库里的列类型会根据 entity 类中字段类型自动转换,例如:类中的 number 类型会转换为数据库列类型为 integer,string 转换为 varchar,boolean 转换为 bool 等等。您还可以设置 @Column() 装饰器的参数明确设置数据库列类型。

@Column('varchar2')

filename: string;

我们已经为数据库表添加了字段,但还有件事没有做,每张表应该有一个主键字段。

创建主键列

每个 Entity 类至少有一个主键字段,这是 typeorm 规定的,并且是不可避免的。使用 @PrimaryColumn 装饰器创建主键列。

import { Entity, Column, PrimaryColumn } from "typeorm";

@Entity()

export class Photo {

@PrimaryColumn()

id: number;

@Column()

name: string;

@Column()

description: string;

@Column()

filename: string;

@Column()

views: number;

@Column()

isPublished: boolean;

}

主键列只能保证 id 的值不能重复。

创建主键自生成字段

您已经创建了主键列,每次存数据时需要手动添加 id 字段的值,如果每次插值时想要自动生成主键值,使用 @PrimaryGeneratedColumn 装饰器代替 @PrimaryColumn 装饰器。

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

@Entity()

export class Photo {

@PrimaryGeneratedColumn('increment') // 主键自增长

// @PrimaryGeneratedColumn('uuid') // 自动生成 uuid 作为主键:'0ae5e5c8-36f3-4b7b-84e4-c0b79cdfb1d1'

id: number;

@Column()

name: string;

@Column()

description: string;

@Column()

filename: string;

@Column()

views: number;

@Column()

isPublished: boolean;

}

完善类类型

接下来,让我们完善数据库类类型。默认情况,string 被转换为 varchar 类型,长度自动设置为 255,number 被转换为 integer,我们不想要所有的列类型都被限制的太死板,让我们按实际情况来设置正确的列类型。

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

@Entity()

export class Photo {

@PrimaryGeneratedColumn()

id: number;

@Column({

length: 100

})

name: string;

@Column("text")

description: string;

@Column()

filename: string;

@Column("double")

views: number;

@Column()

isPublished: boolean;

}

创建数据库连接

准备就绪,让我们尝试连接数据库。

创建 src/testConnect.ts 文件,内容:

import "reflect-metadata";

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection({

type: "mysql",

host: "localhost",

port: 3306,

username: "root",

password: "admin",

database: "test",

entities: [

Photo // <==== 将需要操作的 Entity 导入进来

],

synchronize: true,

logging: false

}).then(connection => {

// here you can start to work with your entities

console.log('数据库连接成功,做点什么吧~')

}).catch(error => console.log(error));

将数据库连接信息改成您自己数据库的相关值:host、port、username、password、database

在这个例子中我们使用 MySQL 数据库,您也可以自由选择使用其它数据库。要用其它数据库,只需要简单的修改 type 属性,值可以是:mysql、mariadb、sqlite、mssql、oracle 或 mongodb。

我们将 Photo Entity 类添加到此次连接中,您需要将使用的所有 Entity 都列出来。

设置 synchronize 属性为 true,确保每次运行应用时,您的 Entity 实体类将与数据库同步。

(译者注:Entity 中新增了一个字段或修改了字段的数据类型,每次启动应用,数据库表也会同步这些修改)

加载所有的 Entities

当我们创建更多 Entity 实体类时,需要我们每次都手动将 Entity 文件添加到配置中的 entities 属性,这很不方便,您可以直接导入整个 entity 目录。

import { createConnection } from "typeorm";

createConnection({

type: "mysql",

host: "localhost",

port: 3306,

username: "root",

password: "admin",

database: "test",

entities: [

__dirname + "/entity/*.js" // <=== 将 entity 目录下所有文件全导进来

],

synchronize: true,

}).then(connection => {

// here you can start to work with your entities

console.log('数据库连接成功,做点什么吧~')

}).catch(error => console.log(error));

(译者注:如果 Vscode 报错 __dirname 未定义,安装 @types/node 包即可解决。 )

运行应用

执行 $ node dist/testConnect.js,数据库连接会被初始化,去查看您的数据库,photo 表格已经被创建了。

+-------------+--------------+----------------------------+

| photo |

+-------------+--------------+----------------------------+

| id | int(11) | PRIMARY KEY AUTO_INCREMENT |

| name | varchar(100) | |

| description | text | |

| filename | varchar(255) | |

| views | int(11) | |

| isPublished | boolean | |

+-------------+--------------+----------------------------+

Typeorm 增删改查

新增数据

让我们创建一张图片并保存到数据库中,创建 src/testPhotoCurd.ts 文件,内容如下:

(createConnection() 方法的连接数据库的参数和上面是一样样的,这里用 /.../ 替代)**

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(connection => {

let photo = new Photo();

photo.name = "Me and Bears";

photo.description = "I am near polar bears";

photo.filename = "photo-with-bears.jpg";

photo.views = 1;

photo.isPublished = true;

return connection.manager

.save(photo)

.then(photo => {

console.log("Photo has been saved. Photo id is", photo.id);

});

}).catch(error => console.log(error));

执行 $ node dist/testPhotoCurd.js,查看数据库,已经生成一张照片数据了。

新增操作会自动生成 id,save() 方法返回的对象和你传给它的对象是同一个对象,唯一的区别是为 photo 对象设置了 id 字段值并返回。

使用 async/await 语法

使用 es8(es2017)的新语法 async/await 可以让代码更易读。

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

let photo = new Photo();

photo.name = "Me and Bears";

photo.description = "I am near polar bears";

photo.filename = "photo-with-bears.jpg";

photo.views = 1;

photo.isPublished = true;

await connection.manager.save(photo);

console.log("Photo has been saved");

}).catch(error => console.log(error));

执行 $ node dist/testPhotoCurd.js,查看数据库又生成一条数据。

(下面如没有特别说明,代码都是写在 src/testPhotoCurd.ts 文件中)

使用 Entity Manager

前面使用 EntityManager 我们已经创建了两张图片并保存到数据库中。

使用 EntityManager,您可以操作应用中的任何 Entity 实体类,如下示例:

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

/*...*/

let savedPhotos = await connection.manager.find(Photo);

console.log("All photos from the db: ", savedPhotos);

}).catch(error => console.log(error));

savedPhotos 是从数据库取出来的 photo 对象组成的数组。

使用 Repositories

Repository 也可以做到 EntityManager 做的事,让我们使用 Repository 重构上面的代码。

当你要对同一个 Entity 做多次处理时,Repositories 会更加方便些。

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

let photo = new Photo();

photo.name = "Me and Bears";

photo.description = "I am near polar bears";

photo.filename = "photo-with-bears.jpg";

photo.views = 1;

photo.isPublished = true;

let photoRepository = connection.getRepository(Photo);

await photoRepository.save(photo); // <=== 操作1:保存数据

console.log("Photo has been saved");

let savedPhotos = await photoRepository.find(); // <=== 操作2:查询数据

console.log("All photos from the db: ", savedPhotos);

}).catch(error => console.log(error));

更新数据

首先从数据库把数据查出来,然后修改它的值。

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

/*...*/

let photoRepository = connection.getRepository(Photo);

let photoToUpdate = await photoRepository.findOne(1); // <=== 操作1:查询数据

if (photoToUpdate !== undefined) {

photoToUpdate.name = "Me, my friends and polar bears";

await photoRepository.save(photoToUpdate); // <=== 操作2:修改数据

}

}).catch(error => console.log(error));

现在,id = 1 的数据被修改了。

删除数据

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

/*...*/

let photoRepository = connection.getRepository(Photo);

let photoToRemove = await photoRepository.findOne(1); // <=== 操作1:查询数据

if (photoToRemove !== undefined) {

await photoRepository.remove(photoToRemove); // <=== 操作2:修改数据

}

}).catch(error => console.log(error));

现在,id = 1 的数据被删除了。

查询数据

import { createConnection } from "typeorm";

import { Photo } from "./entity/Photo";

createConnection(/*...*/).then(async connection => {

/* 查询所有照片 */

let allPhotos = await photoRepository.find();

console.log("All photos from the db: ", allPhotos);

/* 查询 id = 1 的第一条数据 */

let firstPhoto = await photoRepository.findOne(1);

console.log("First photo from the db: ", firstPhoto);

/* 查询 name = 'Me and Bears' 的第一条数据 */

let meAndBearsPhoto = await photoRepository.findOne({ name: "Me and Bears" });

console.log("Me and Bears photo from the db: ", meAndBearsPhoto);

/* 查询 views = 1 的所有数据 */

let allViewedPhotos = await photoRepository.find({ views: 1 });

console.log("All viewed photos: ", allViewedPhotos);

/* 查询 isPublished = true 的所有数据 */

let allPublishedPhotos = await photoRepository.find({ isPublished: true });

console.log("All published photos: ", allPublishedPhotos);

/* 查询数据和数目 */

let [allPhotos, photosCount] = await photoRepository.findAndCount();

console.log("All photos: ", allPhotos);

console.log("Photos count: ", photosCount);

}).catch(error => console.log(error));

更复杂的查询数据

您可以使用 QueryBuilder 构建复杂的 sql 查询:

let photos = await connection

.getRepository(Photo)

.createQueryBuilder("photo") // first argument is an alias. Alias is what you are selecting - photos. You must specify it.

.innerJoinAndSelect("photo.metadata", "metadata")

.leftJoinAndSelect("photo.albums", "album")

.where("photo.isPublished = true")

.andWhere("(photo.name = :photoName OR photo.name = :bearName)")

.orderBy("photo.id", "DESC")

.skip(5)

.take(10)

.setParameters({ photoName: "My", bearName: "Mishka" })

.getMany();

这条查询语句会检索已经发布的,照片名字是 "My" 或 "Mishka" 的照片。从数据库中下标为 5 的数据开始取(pagination offset),只取 10 条数据(pagination limit),检索到的数据按照 photo.id 倒序排列,photo 表左连接 albums 表,内连接 metadata 表查询。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值