使用node+typescript搭建mvc后台(三)--使用typeorm连接数据库

1,230 阅读5分钟

使用typeorm连接数据库

TypeORM是一个ORM框架,它可以运行在NodeJS、浏览器、Cordova、PhoneGap、Ionic、React Native、Expo和Electron平台上,可以与TypeScript和JavaScript (ES5, ES6, ES7)一起使用。
它的目标是始终支持最新的JavaScript特性并提供额外的特性以帮助你开发任何使用数据库的应用程序 —— 不管是只有几张表的小型应用还是拥有多数据库的大型企业应用。像java也有他的hibernate ,mybatis这类东西。
话不多说。直接开撸。

搭建IOC依赖注入loader

首先我们要先安装TypeORM的模块。这里就不多介绍了。
然后最重要的就是一个ioc的引入。这也是spring最大的特色之一吧。使得代码模块极大的解耦了。对于IOC是什么的介绍。这边有一篇很好的文章。浅谈IOC--说清楚IOC是什么。如果不知道的同学可以看看这篇文章了解一下。

创建iocLoader

在loader文件家里创建

import { MicroframeworkLoader, MicroframeworkSettings } from 'microframework-w3tec';
import { useContainer as routingUseContainer } from 'routing-controllers';
import { Container } from 'typedi';
import { useContainer as ormUseContainer } from 'typeorm';

export const iocLoader: MicroframeworkLoader = (settings: MicroframeworkSettings | undefined) => {
    routingUseContainer(Container);
    ormUseContainer(Container);
};

这里要加入依赖注入。把项目需要的实例注入到container中。在后面我们才能直接使用注解获取。而 routing-controllers 和 typeorm 都为我们提供了依赖注入的封装方法,在此我们直接使用即可。
这样之后我们就可以使用注解的方式来获取一些实例。

初始化数据库连接

在loader下新建typeormLoader。

import { MicroframeworkLoader, MicroframeworkSettings } from 'microframework-w3tec';
import { createConnection, getConnectionOptions } from 'typeorm';

import { env } from '../env';

export const typeormLoader: MicroframeworkLoader = async (settings: MicroframeworkSettings | undefined) => {
    // 获取连接配置
    const loadedConnectionOptions = await getConnectionOptions();
    // 从环境变量中获取配置并覆盖。
    const connectionOptions = Object.assign(loadedConnectionOptions, {
        type: env.db.type as any, 
        host: env.db.host,
        port: env.db.port,
        username: env.db.username,
        password: env.db.password,
        database: env.db.database,
        synchronize: env.db.synchronize,
        logging: env.db.logging,
        entities: env.app.dirs.entities
    });
    // 创建新的数据库连接
    const connection = await createConnection(connectionOptions);
    // 如果启动服务已经有了seeting。那么把数据库连接报错到全局仓库中。
    // 同时设置关闭时端开数据库连接
    if (settings) {
        settings.setData('connection', connection);
        settings.onShutdown(() => connection.close());
    }
};

在服务结束后也同时关闭了连接。这大概就是使用这个框架的好处吧。
这些配置都在环境变量中配置

## app基础配置
# 路由base路径
APP_ROUTE_PREFIX=/api

## 路径配置
#配置controller路径
CONTROLLERS=src/api/controllers/**/*Controller.ts
#配置中间件路径
MIDDLEWARES=src/api/middlewares/**/*Middleware.ts
#配置拦截器路径
INTERCEPTORS=src/api/interceptors/**/*Interceptor.ts
# 配置数据库相关地址。MIGRATIONS 的操作项待遇对代码的一个映射,可以通过js代码自动生成数据库代码。
# 这里我们先手动创建。后期再来学习这个MIGRATIONS怎么操作
# TYPEORM_MIGRATIONS=src/database/migrations/**/*.ts
# TYPEORM_MIGRATIONS_DIR=src/database/migrations
TYPEORM_ENTITIES=src/api/models/**/*.ts
TYPEORM_ENTITIES_DIR=src/api/models


## 数据库配置
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=root
TYPEORM_DATABASE=feg
# 这个配置将会在数据连接的时候对数据库的视图Schema进行自动的Migration。
# 在生产环境的时候应该关掉
TYPEORM_SYNCHRONIZE=true
# 设置打印日志的等级
TYPEORM_LOGGING=error
# 设置日志打印方式
# 可设置的类型有 advanced-console,simple-console,file, debug, 在开启 logging 的情况下,
# logger默认使用类型是 advanced-console, 这个模式会高亮字体和标示sql语句。
TYPEORM_LOGGER=advanced-console

创建service,dao,model文件夹

可能因为之前写java习惯了。这三个就有点照着java的模式来。不过这也是使用这些框架的目的嘛。
然后我们新建一个user的三层结构

entity

model/user.ts 之前做小程序新建了一个对象,所以现在直接复制过来了
在做这个地方的时候踩了一个坑,先前ts文件名称是User,然后扫描的时候无法扫描出来。后来改为user又可以了。后来再改回来又可以了真的是奇怪了。至今没想通。可能不小心改其他地方吧。
~~
这里的eneity可以不填。默认为class的名称。如果找不到就会根据column新建一个table

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

@Entity('user')
export class User {
    @PrimaryColumn()
    public id!: string;

    @Column({
        default: null
    })
    public wx_name!: string;

    @Column({
        default: null
    })
    public avatar!: string;

    @Column({
        default: null
    })
    public name!: string;

    @Column({
        default: null
    })
    public role!: string;

    @Column({
        default: null
    })
    public init!: string;

}

dao层

这里继承了typeorm的Repository,他已经根据你传入的泛型为对象绑定了一些列简单的增删改查方法

import {User} from "../models/User";
import {EntityRepository, Repository} from "typeorm";
@EntityRepository(User)
export class UserRepository extends Repository<User> {

}

最后是service层

import { Service } from 'typedi';
import { OrmRepository } from 'typeorm-typedi-extensions';
import uuid from 'uuid';

import { User } from '../models/User';
import { UserRepository } from '../repositories/UserRepository';

@Service()
export class UserService {

    constructor(
        @OrmRepository() private userRepository: UserRepository,
    ) { }

    public find(): Promise<User[]> {
        return this.userRepository.find();
    }

    public findOne(id: string): Promise<User | undefined> {
        return this.userRepository.findOne({ id });
    }

    public async create(user: User): Promise<User> {
        user.id = uuid.v1();
        const newUser = await this.userRepository.save(user);
        return newUser;
    }

    public update(id: string, user: User): Promise<User> {
        user.id = id;
        return this.userRepository.save(user);
    }

    public async delete(id: string): Promise<void> {
        await this.userRepository.delete(id);
        return;
    }

}

增删改查的方法都大同小异。这里通过uuid来生成唯一码,注意这里要加入注解@Service(),和 @OrmRepository(),这样才能从contain里面拿到我们要的实例。

最后是controller层

最后要修改我们的controller层才能完整的搭建一个mvc的结构。不过前后端分离了。views应该就不在这个后台了。
新建一个userController

import { Get, JsonController } from "routing-controllers";
import { UserService } from '../services/UserService';
import { User } from "../models/User";

@JsonController('/user')
export default class UserController {
    constructor(
        private userService: UserService
    ) {
    }

    @Get()
    public find(): Promise<User[]>  {
        return this.userService.find();
    }

}

这里也把UserService注入到构造函数中。这样在函数中就可以直接使用this来使用然后就通过一个方法this.userService.find();返回用户列表。

查看效果

接下来我们就可以重启服务器。查看效果了。

image.png

代码仓库