TypeOrm的介绍与实例
支持ts的node orm框架
优势
- 自定义实体的形式实现自定义存储库,清晰的对象关系模型,支持单向的,双向的和自引用的关系
- 支持多重继承模式、级联、索引、事务、迁移和自动迁移
- 灵活而强大的 QueryBuilder,使用联查查询的适当分页
- json/xml/yml/env格式的连接配置、支持MySQL/MariaDB/Postgres/SQLite/Microsoft SQL Server/Oracle/SAP Hana/sql.js
- 可在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