如何使用 TypeORM 接入数据库?

3,048 阅读3分钟

简介

本文是《全栈开发 GraphQL + Flutter 最佳实践,文章系列》中的一员。主要介绍如何使用 TypeORM 接入数据库:

  • 安装 TypeORM
  • 创建连接
  • 创建实体类
  • 新增实体
  • 修改实体
  • 删除实体

什么是 TypeORM

TypeORM 是一个对象关系映射,可以在 NodeJS,浏览器,Cordova,PhoneGap,Ionic,React Native,NativeScript,Expo 和 Electron 平台上运行,并且可以与 TypeScript 和 JavaScript(ES5,ES6,ES7,ES8)一起使用。 其目标是始终支持最新的 JavaScript 功能,并提供其他功能,来帮助您开发需要使用数据库的应用程序。可以小到带有少量表的小型应用程序,也可以大到具有多个数据库的大型企业应用程序。

现在我们用 TypeORM 接入 MySQL 数据库。

项目依赖

安装 TypeORM

前往项目根目录,执行以下命令:

npm install --save mysql@2.18.1 \
    typeorm@0.2.25

创建连接

接着在应用内接入数据库,打开文件src/index.ts 作如下修改:

import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
+import { createConnection } from 'typeorm';

...

找到 main 函数,将其改为:

async function main() {

  try {

    // const schema = await buildSchema({
    //   resolvers: [PostResolver],
    //   dateScalarMode: 'timestamp'
    // });


    // const server = new ApolloServer({
    //   schema,
    //   playground: true
    // });

    // const { url } = await server.listen(4444);

    // console.log(`GraphQL Playground available at ${url}`);

    const connection = await createConnection({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'tinylearn',
      password: 'my_password',
      database: 'tinylearn',
      synchronize: true,
    });

    console.log('Create connection success!');

  } catch (error) {
    console.error(error);
  }
}

这里我们用 createConnection 方法创建连接,然后填入连接参数:

  • type - 数据库类型
  • host - 主机
  • port - 端口号
  • username - 用户名
  • password - 密码
  • database- 数据库
  • synchronize - 是否将本地 schema 同步到数据库上去

如需其他配置项可以参考 TypeORM Connection Options

启动服务:

$ npm start

如果运行后显示以下问题:

这是因为不支持授权插件方法,我们可以打开数据库修改授权方法。

目前,我们配置的 MySQL 数据库只有 root 账号有权限做此修改。因此,我们需要在 MySQL Workbench 中,新建一个 root 账号的连接。

可以参考《如何使用 MySQL Workbench 图形化工具来操作数据库?》

账号填 root,密码填写启动 MySQL 时的环境变量 MYSQL_ROOT_PASSWORD:

然后执行以下 SQL 语句,修改授权方法:

ALTER USER 'tinylearn'@'%' IDENTIFIED WITH mysql_native_password BY 'my_password';

再次启动服务:

$ npm start

连接成功!

创建实体类

我们现在创建一个博文的实体类 PostEntity,然后将它同步到数据库里面去。

src/index.ts:

import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { CreateDateColumn, Entity, PrimaryGeneratedColumn, Column, createConnection } from 'typeorm';

@Entity()
class PostEntity {
 
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @CreateDateColumn()
  created: Date;

  @Column('text')
  content: string;
}

@ObjectType()
class Post {

  @Field(type => ID)
  id: string;

  @Field()
  created: Date;

  @Field()
  content: string;
}

...

与数据库同步

src/index.ts:


    const connection = await createConnection({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'tinylearn',
      password: 'my_password',
      database: 'tinylearn',
      synchronize: true,
+      entities: [PostEntity],
    });

    console.log('Create connection success!');
    

激活终端,按下快捷键 ctrl+C 停止当前运行状态,然后使用 npm start 重新启动:

使用 MySQL Workbench 打开数据库,验证表格是否成功创建:

结果显示表格已经成功创建。

新增实体

我们尝试添加一条博文:

try {

    ...

    console.log('Create connection success!');

    const postRepository = connection.getRepository(PostEntity);

    const result = await postRepository.insert({ content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...' });

    console.log(`insert post success: ${JSON.stringify(result, null, 2)}`);
    
  } catch (error) {
    console.error(error);
  }

重启服务 ctrl+C, npm start

验证结果:

新增的博文已在最下方显示,我们先记下它的 id,后面修改博文时会用到,这个 id 是自动生成的。

修改实体

现在来编辑一下博文内容:

try {

    ...

    console.log('Create connection success!');

    const postRepository = connection.getRepository(PostEntity);

    const result = await postRepository
      .update('fefc7d7c-4d33-45e2-b437-88db8e920f5d',  { content: 'Hello GraphQL' });

    console.log(`update post success: ${JSON.stringify(result, null, 2)}`);
    
  } catch (error) {
    console.error(error);
  }

这里通过博文 id 来修改它的内容。

重启服务 ctrl+C, npm start

验证结果:

内容已经修改成功!

删除实体

最后,我们尝试删除这篇博文:

try {

    ...

    console.log('Create connection success!');

    const postRepository = connection.getRepository(PostEntity);

    const result = await postRepository
      .delete('fefc7d7c-4d33-45e2-b437-88db8e920f5d');

    console.log(`delete post success: ${JSON.stringify(result, null, 2)}`);    

  } catch (error) {
    console.error(error);
  }

重启服务 ctrl+C, npm start

验证结果:

博文已经被删除了。

下一步

想了解更多 TypeORM 的使用方法请访问: typeorm.io/#/

下一篇 《GraphQL + TypeORM 黄金搭档》,会把当前模拟数据源替换成 MySQL 数据源。这样我们就可以通过“接口”实现数据库的增删改查了。

源码

代码地址

src/index.ts

import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { CreateDateColumn, Entity, PrimaryGeneratedColumn, Column, createConnection } from 'typeorm';

@Entity()
class PostEntity {

  @PrimaryGeneratedColumn('uuid')
  id: string;

  @CreateDateColumn()
  created: Date;

  @Column('text')
  content: string;
}

@ObjectType()
class Post {

  @Field(type => ID)
  id: string;

  @Field()
  created: Date;

  @Field()
  content: string;
}

@Resolver(Post)
class PostResolver {

  @Query(returns => [Post])
  async posts(): Promise<Post[]> {
    return [
      {
        id: "0",
        created: new Date(),
        content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...'
      },
      {
        id: "1",
        created: new Date(),
        content: '为什么GraphQL是API的未来'
      },
      {
        id: "2",
        created: new Date(),
        content: 'Netflix:我们为什么要将 GraphQL 引入前端架构?'
      },
    ]
  }
}

async function main() {

  try {

    // const schema = await buildSchema({
    //   resolvers: [PostResolver],
    //   dateScalarMode: 'timestamp'
    // });


    // const server = new ApolloServer({
    //   schema,
    //   playground: true
    // });

    // const { url } = await server.listen(4444);

    // console.log(`GraphQL Playground available at ${url}`);

    const connection = await createConnection({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'tinylearn',
      password: 'my_password',
      database: 'tinylearn',
      synchronize: true,
      entities: [PostEntity],
    });

    console.log('Create connection success!');

    const postRepository = connection.getRepository(PostEntity);

    // 新增
    // const result = await postRepository.insert({ content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...' });
    // console.log(`insert post success: ${JSON.stringify(result, null, 2)}`);

    // 修改
    // const result = await postRepository
    //   .update('fefc7d7c-4d33-45e2-b437-88db8e920f5d', { content: 'Hello GraphQL' });
    // console.log(`update post success: ${JSON.stringify(result, null, 2)}`);

    // 删除
    const result = await postRepository
      .delete('fefc7d7c-4d33-45e2-b437-88db8e920f5d');
    console.log(`delete post success: ${JSON.stringify(result, null, 2)}`);
    
  } catch (error) {
    console.error(error);
  }
}


main();

参考

TypeORM

《全栈开发 GraphQL + Flutter 最佳实践,文章系列》

《GraphQL + TypeORM 黄金搭档》

《10 分钟搭建一个 GraphQL 后端项目》

《Docker 启动 MySQL 容器》

MySQL Workbench