TypeOrm的介绍与实例

1,188 阅读4分钟

TypeOrm的介绍与实例

支持ts的node orm框架

优势

  1. 自定义实体的形式实现自定义存储库,清晰的对象关系模型,支持单向的,双向的和自引用的关系
  2. 支持多重继承模式、级联、索引、事务、迁移和自动迁移
  3. 灵活而强大的 QueryBuilder,使用联查查询的适当分页
  4. json/xml/yml/env格式的连接配置、支持MySQL/MariaDB/Postgres/SQLite/Microsoft SQL Server/Oracle/SAP Hana/sql.js
  5. 可在NodeJS/浏览器/Ionic/Cordova/React Native/Expo/Electron等平台上使用、支持TypeScript和JavaScript

初始化

首先全局安装 TypeORM:

npm install typeorm -g

然后转到要创建新项目的目录并运行命令:

typeorm init --name MyProject --database mysql

此命令将在MyProject目录中生成一个包含以下文件的新项目:

MyProject
├── src              // TypeScript 代码
│   ├── entity       // 存储实体(数据库模型)的位置
│   │   └── User.ts  // 示例 entity
│   ├── migration    // 存储迁移的目录
│   └── index.ts     // 程序执行主文件
├── .gitignore       // gitignore文件
├── ormconfig.json   // ORM和数据库连接配置
├── package.json     // node module 依赖
├── README.md        // 简单的 readme 文件
└── tsconfig.json    // TypeScript 编译选项

在安装过程中,编辑ormconfig.json文件并在其中放置您自己的数据库连接配置选项:

{
    "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"]
}

搭配Koa(实例)

通过createConnection创建一个新的连接(多连接使用createConnections),在成功的回调内启动Koa,并挂载KoaBody与KoaRouter等中间件

import "reflect-metadata";
import "module-alias/register";
import { createConnection } from "typeorm";
import Koa from "koa";
import KoaBody from "koa-body";
import Router from "koa-router";
import websockify from "koa-websocket";

import { setupWS } from "./ws";
import ctxResponse from "./middleware/ctxResponse";
import config from "./modules/config";
import AppRoutes from "./routes";

createConnection()
    .then(async (connection) => {
        const app = websockify(new Koa());
        const router = new Router();

        // register all application routes
        AppRoutes.forEach((route) =>
            router[route.method](route.path, route.action)
        );

        setupWS(app);
        app.use(KoaBody());
        app.use(ctxResponse());

        // run app
        app.use(router.routes()).use(router.allowedMethods());

        app.listen(config.port, () => {
            console.log(`server is running at http://localhost:${config.port}`);
        });
    })
    .catch((error) => console.log("TypeORM connection error: ", error));

实体

实体是一个映射到数据库表(或使用 MongoDB 时的集合)的类,可以通过定义一个新类来创建一个实体,并用@Entity()来标记:

创建一个实体

import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } 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;
    
    // 特殊列,自动为实体插入日期。无需设置此列,该值将自动设置。
    @CreateDateColumn({
        comment: "创建时间",
    })
    created_time: Date;

    // 特殊列,在每次调用实体管理器或存储库的`save`时,自动更新实体日期。无需设置此列,该值将自动设置。
    @UpdateDateColumn({
        comment: "更新时间",
    })
    updated_time: Date;
}

使用实体

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

createConnection(/*...*/)
    .then(async (connection) => {
        let photoRepository = connection.getRepository(Photo);
   
        /* loading */
        let allPhotos = await photoRepository.find();
        console.log("All photos from the db: ", allPhotos);

        let firstPhoto = await photoRepository.findOne(1);
        console.log("First photo from the db: ", firstPhoto);

        let meAndBearsPhoto = await photoRepository.findOne({ name: "Me and Bears" });
        console.log("Me and Bears photo from the db: ", meAndBearsPhoto);

        let allViewedPhotos = await photoRepository.find({ views: 1 });
        console.log("All viewed photos: ", allViewedPhotos);

        let [allPhotos, photosCount] = await photoRepository.findAndCount();
        console.log("All photos: ", allPhotos);
        console.log("Photos count: ", photosCount);
        
        /* save */
        let photoToUpdate = await photoRepository.findOne(1);
        photoToUpdate.name = "Me, my friends and polar bears";
        await photoRepository.save(photoToUpdate);
        
        /* remove */
        let photoToRemove = await photoRepository.findOne(1);
        await photoRepository.remove(photoToRemove);
    })
    .catch((error) => console.log(error));

插件推荐 - 完善API体系

routing-controllers

使用装饰器的形式定义接口,完美契合ts,贴近后端框架写法

import { Controller, Get, Param } from 'routing-controllers';
//路由器 router 相当于const router = express.Router();用 
@Controller()//书写,括号可传url,不传则相当于/,传/api 就是每次访问里面的路由要加上/api
class UserController{
//用类代替一个router,里面每个加上POST,GET,PUT,DELETE,PATCH...的方法都是一个路由
	@Get('/user/:id')//定义一个get请求
	public getUser(@Param('id') id:string){//想要获取参数,定义和参数一致的key,然后使用变量接收
		console.log(id);//当输入localhost:5000/user/12312321 打印12312321
		//需要返回给前端的数据直接return回去,这里建议使用async 和await来进行数据库的交互,
		//可以返回promise和字符串、对象等
		return id;
	}
}

typedi

支持ts的依赖注入工具,可以基于属性注入,基于构造函数注入,支持多个DI容器

import { Service, Container, Inject } from "typedi";
@Service()
class BeanFactory {
    create() {
        console.log('BeanFactory,create');
    }
}
@Service()
class SomeClass {
    @Inject()
    private beanFactory!: BeanFactory;
    someMethod() {
        console.log('someMethod');
        this.beanFactory.create();
    }
}
//或者
@Service('bean.factory')
class BeanFactory {
    create() {
        console.log('BeanFactory,create');
    }
}
@Service()
class SomeClass {
    @Inject('bean.factory')//指定使用
    private beanFactory!: BeanFactory;
    someMethod() {
        console.log('someMethod');
        this.beanFactory.create();
    }
}
//实例化
let someClass = Container.get(SomeClass);//是一个单例的结果,每次get都是一样的,实现单例模式,例如数据库的连接,单例的创建
someClass.someMethod();//someMethod  BeanFactory,create