介绍
mongo-context是团队根据自身产品定位定制的一个操作mongodb的插件模块,提供支持连接多个数据库、数据模型基类(SuperModel)、自动收集数据模型(@TypegooseModel装饰器)
连接数据库
一般来说,连接数据库是在生命周期中服务启动时要做的。为了满足服务端可以连接多个数据库,所以就没有做自动连接数据库的功能,而是手动在服务启动的时候提供多个数据库连接配置,连接数据库
## 安装mongo-context模块,因为typegoose的原因,mongoose最高支持5.10.18npm install @lzy-plugin/mongo-context @typegoose/typegoose mongoose@5.10.18
//src/configuration.tsimport { IMongoConfig, MongoContext } from '@lzy-plugin/mongo-context';import * as mongoContext from '@lzy-plugin/mongo-context';@Configuration({imports: [mongoContext], // 不可缺少,需要引入mongo-context模块中所有实例对象importConfigs: [join(__dirname, 'config')]})export class AutoConfiguration {async onReady(container: IMidwayContainer) {const array = new Array<IMongoConfig>();array.push({key: 'admin',user: 'lzy',pass: 'lzy123456',host: '',port: '',replicaSet: {name: 'mgset-17098013',members: [{host: '127.0.0.1',port: 3717},{host: '127.0.0.1',port: 3717}]},db: 'test-admin'})array.push({key: 'one',user: 'lzy',pass: 'lzy123456',host: '',port: '',replicaSet: {name: 'mgset-17098013',members: [{host: '127.0.0.1',port: 3717},{host: '127.0.0.1',port: 3717}]},db: 'test-one'})for(const item of array){const connection = await MongoContext.createConnection(item);connection.on('connected', () => {console.log(`${item.key}数据库连接成功!`)});}}}
定义Model
// src/models/user.tsimport { prop } from '@typegoose/typegoose';import { SuperModel, TypegooseModel } from '@lzy-plugin/mongo-context';import { Expose } from 'class-transformer';@TypegooseModel({modelName:'User',collectionName:'users'}) //不可缺少,声明为数据模型,入参可以定义模型名称和表名export class UserModel extends SuperModel {//不可缺少,必须继承SuperModel@prop() //不可缺少,声明为数据模型中的列@Expose()username: string;@prop()@Expose()password: string;}
CRUD示例
// src/service/test.ts// 获取mongoContxt示例import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';import { MongoContext, MongoManager } from '@lzy-plugin/mongo-context';import { UserModel } from '../models/user';@Provide()@Scope(ScopeEnum.Singleton)export class TestService {@Inject()mongoManager: MongoManager;context: MongoContext;@Init()async init(): Promise<void> {this.context = await this.mongoManager.getContext('admin'); 获取admin数据库连接实例对象下的mongo上下文}//新增用户async createUser(username: string, password: string): Promise<void> {const user = new UserModel();user.password = password;user.username = username;await this.context.switchModel(UserModel).save(user); //支持传递Model类// await this.context.switchModel('User').save(user); //支持传递ModelNameconsole.log('新增用户成功');}//修改用户async editUser(_id:string,username: string, password: string): Promise<void> {const user = await this.context.findById(_id);user.password = password;user.username = username;await this.context.switchModel(UserModel).save(user);console.log('新增用户成功');}//批量查询用户async listUser(): Promise<void> {const result = await this.context.switchModel(UserModel).find({});console.log(result);}//查询单个用户async listUser(): Promise<void> {const result = await this.context.switchModel(UserModel).findOne({});console.log(result);}//移除指定用户async removeUser(_id:string): Promise<void> {const result = await this.context.switchModel(UserModel).remove({_id:_id});console.log(result);}}
数据关联&&自动填充
// src/models/cart-item.tsimport { prop } from '@typegoose/typegoose';import { SuperModel, TypegooseModel } from '@lzy-plugin/mongo-context';import { Expose } from 'class-transformer';@TypegooseModel({modelName:'CartItem',collectionName:'cart-item'})export class CartItem extends SuperModel {@prop() //不可缺少,声明为数据模型中的列@Expose()goodsName: string;@prop()@Expose()price: number;@prop()@Expose()count: number;constructor(goodsName:string,price:number,count:number){this.goodsName = goodsName;this.price = price;this.count = count;}}// src/models/cart.tsimport { prop } from '@typegoose/typegoose';import { SuperModel, TypegooseModel } from '@lzy-plugin/mongo-context';import { Expose } from 'class-transformer';import { CartItem } from './cart-item';@TypegooseModel({modelName:'CartItem',collectionName:'cart-item'})export class Cart extends SuperModel {@prop() //不可缺少,声明为数据模型中的列@Expose()userId: string;@prop({ autopopulate: true,ref: 'CartItem' })@Expose()@Type(() => CartItem)public items?: Ref<CartItem>[];}// src/service/test.ts// src/service/test.ts// 获取mongoContxt示例import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';import { MongoContext, MongoManager } from '@lzy-plugin/mongo-context';import { Cart } from '../models/cart';import { CartItem } from '../models/cart-item';@Provide()@Scope(ScopeEnum.Singleton)export class TestService {@Inject()mongoManager: MongoManager;context: MongoContext;@Init()async init(): Promise<void> {this.context = await this.mongoManager.getContext('admin'); 获取admin数据库连接实例对象下的mongo上下文}async addCartItem(){const cart = new Cart();cart.userId = '1234';cart.items = new Array<CartItem>();cart.items.push(new CartItem('可口可乐',3,1));cart.items.push(new CartItem('百事可乐',3,10));await this.context.switchModel(Cart).save(cart);for(const item of cart.items){await this.context.switchModel(CartItem).save(item)}//cart数据的items只保存CartItem数据的_id;}async getCart(){const result = await this.context.findOne({});console.log(result);//result.items 自动根据_id查询cart-item表的数据,填充到其中}}
事务处理
以上更新中存在多条执行写入语句,如果有一条写入失败,整个操作都得回滚。
mongodb 4.0以上的版本提供了多文档的事务,那我们来看封装mongo-context模块如何处理事务
async addCartItem(){const db: Connection = MongoContext.getConnection('admin');if (!db) {throw new Error('获取数据库连接对象失败');}const session = await db.startSession();session.startTransaction();try{const cart = new Cart();cart.userId = '1234';cart.items = new Array<CartItem>();cart.items.push(new CartItem('可口可乐',3,1));cart.items.push(new CartItem('百事可乐',3,10));await this.context.switchModel(Cart).save(cart,{session});for(const item of cart.items){await this.context.switchModel(CartItem).save(item,{session})}await session.commitTransaction();}catch(e){await session.abortTransaction();}finally{session.endSession();}}
