介绍

mongo-context是团队根据自身产品定位定制的一个操作mongodb的插件模块,提供支持连接多个数据库、数据模型基类(SuperModel)、自动收集数据模型(@TypegooseModel装饰器)

连接数据库

一般来说,连接数据库是在生命周期中服务启动时要做的。为了满足服务端可以连接多个数据库,所以就没有做自动连接数据库的功能,而是手动在服务启动的时候提供多个数据库连接配置,连接数据库

  1. ## 安装mongo-context模块,因为typegoose的原因,mongoose最高支持5.10.18
  2. npm install @lzy-plugin/mongo-context @typegoose/typegoose mongoose@5.10.18
  1. //src/configuration.ts
  2. import { IMongoConfig, MongoContext } from '@lzy-plugin/mongo-context';
  3. import * as mongoContext from '@lzy-plugin/mongo-context';
  4. @Configuration({
  5. imports: [mongoContext], // 不可缺少,需要引入mongo-context模块中所有实例对象
  6. importConfigs: [join(__dirname, 'config')]
  7. })
  8. export class AutoConfiguration {
  9. async onReady(container: IMidwayContainer) {
  10. const array = new Array<IMongoConfig>();
  11. array.push({
  12. key: 'admin',
  13. user: 'lzy',
  14. pass: 'lzy123456',
  15. host: '',
  16. port: '',
  17. replicaSet: {
  18. name: 'mgset-17098013',
  19. members: [
  20. {
  21. host: '127.0.0.1',
  22. port: 3717
  23. },
  24. {
  25. host: '127.0.0.1',
  26. port: 3717
  27. }
  28. ]
  29. },
  30. db: 'test-admin'
  31. })
  32. array.push({
  33. key: 'one',
  34. user: 'lzy',
  35. pass: 'lzy123456',
  36. host: '',
  37. port: '',
  38. replicaSet: {
  39. name: 'mgset-17098013',
  40. members: [
  41. {
  42. host: '127.0.0.1',
  43. port: 3717
  44. },
  45. {
  46. host: '127.0.0.1',
  47. port: 3717
  48. }
  49. ]
  50. },
  51. db: 'test-one'
  52. })
  53. for(const item of array){
  54. const connection = await MongoContext.createConnection(item);
  55. connection.on('connected', () => {
  56. console.log(`${item.key}数据库连接成功!`)
  57. });
  58. }
  59. }
  60. }

定义Model

  1. // src/models/user.ts
  2. import { prop } from '@typegoose/typegoose';
  3. import { SuperModel, TypegooseModel } from '@lzy-plugin/mongo-context';
  4. import { Expose } from 'class-transformer';
  5. @TypegooseModel({modelName:'User',collectionName:'users'}) //不可缺少,声明为数据模型,入参可以定义模型名称和表名
  6. export class UserModel extends SuperModel {//不可缺少,必须继承SuperModel
  7. @prop() //不可缺少,声明为数据模型中的列
  8. @Expose()
  9. username: string;
  10. @prop()
  11. @Expose()
  12. password: string;
  13. }

CRUD示例

  1. // src/service/test.ts
  2. // 获取mongoContxt示例
  3. import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
  4. import { MongoContext, MongoManager } from '@lzy-plugin/mongo-context';
  5. import { UserModel } from '../models/user';
  6. @Provide()
  7. @Scope(ScopeEnum.Singleton)
  8. export class TestService {
  9. @Inject()
  10. mongoManager: MongoManager;
  11. context: MongoContext;
  12. @Init()
  13. async init(): Promise<void> {
  14. this.context = await this.mongoManager.getContext('admin'); 获取admin数据库连接实例对象下的mongo上下文
  15. }
  16. //新增用户
  17. async createUser(username: string, password: string): Promise<void> {
  18. const user = new UserModel();
  19. user.password = password;
  20. user.username = username;
  21. await this.context.switchModel(UserModel).save(user); //支持传递Model类
  22. // await this.context.switchModel('User').save(user); //支持传递ModelName
  23. console.log('新增用户成功');
  24. }
  25. //修改用户
  26. async editUser(_id:string,username: string, password: string): Promise<void> {
  27. const user = await this.context.findById(_id);
  28. user.password = password;
  29. user.username = username;
  30. await this.context.switchModel(UserModel).save(user);
  31. console.log('新增用户成功');
  32. }
  33. //批量查询用户
  34. async listUser(): Promise<void> {
  35. const result = await this.context.switchModel(UserModel).find({});
  36. console.log(result);
  37. }
  38. //查询单个用户
  39. async listUser(): Promise<void> {
  40. const result = await this.context.switchModel(UserModel).findOne({});
  41. console.log(result);
  42. }
  43. //移除指定用户
  44. async removeUser(_id:string): Promise<void> {
  45. const result = await this.context.switchModel(UserModel).remove({_id:_id});
  46. console.log(result);
  47. }
  48. }

数据关联&&自动填充

  1. // src/models/cart-item.ts
  2. import { prop } from '@typegoose/typegoose';
  3. import { SuperModel, TypegooseModel } from '@lzy-plugin/mongo-context';
  4. import { Expose } from 'class-transformer';
  5. @TypegooseModel({modelName:'CartItem',collectionName:'cart-item'})
  6. export class CartItem extends SuperModel {
  7. @prop() //不可缺少,声明为数据模型中的列
  8. @Expose()
  9. goodsName: string;
  10. @prop()
  11. @Expose()
  12. price: number;
  13. @prop()
  14. @Expose()
  15. count: number;
  16. constructor(goodsName:string,price:number,count:number){
  17. this.goodsName = goodsName;
  18. this.price = price;
  19. this.count = count;
  20. }
  21. }
  22. // src/models/cart.ts
  23. import { prop } from '@typegoose/typegoose';
  24. import { SuperModel, TypegooseModel } from '@lzy-plugin/mongo-context';
  25. import { Expose } from 'class-transformer';
  26. import { CartItem } from './cart-item';
  27. @TypegooseModel({modelName:'CartItem',collectionName:'cart-item'})
  28. export class Cart extends SuperModel {
  29. @prop() //不可缺少,声明为数据模型中的列
  30. @Expose()
  31. userId: string;
  32. @prop({ autopopulate: true,ref: 'CartItem' })
  33. @Expose()
  34. @Type(() => CartItem)
  35. public items?: Ref<CartItem>[];
  36. }
  37. // src/service/test.ts
  38. // src/service/test.ts
  39. // 获取mongoContxt示例
  40. import { Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
  41. import { MongoContext, MongoManager } from '@lzy-plugin/mongo-context';
  42. import { Cart } from '../models/cart';
  43. import { CartItem } from '../models/cart-item';
  44. @Provide()
  45. @Scope(ScopeEnum.Singleton)
  46. export class TestService {
  47. @Inject()
  48. mongoManager: MongoManager;
  49. context: MongoContext;
  50. @Init()
  51. async init(): Promise<void> {
  52. this.context = await this.mongoManager.getContext('admin'); 获取admin数据库连接实例对象下的mongo上下文
  53. }
  54. async addCartItem(){
  55. const cart = new Cart();
  56. cart.userId = '1234';
  57. cart.items = new Array<CartItem>();
  58. cart.items.push(new CartItem('可口可乐',3,1));
  59. cart.items.push(new CartItem('百事可乐',3,10));
  60. await this.context.switchModel(Cart).save(cart);
  61. for(const item of cart.items){
  62. await this.context.switchModel(CartItem).save(item)
  63. }
  64. //cart数据的items只保存CartItem数据的_id;
  65. }
  66. async getCart(){
  67. const result = await this.context.findOne({});
  68. console.log(result);
  69. //result.items 自动根据_id查询cart-item表的数据,填充到其中
  70. }
  71. }

事务处理

以上更新中存在多条执行写入语句,如果有一条写入失败,整个操作都得回滚。
mongodb 4.0以上的版本提供了多文档的事务,那我们来看封装mongo-context模块如何处理事务

  1. async addCartItem(){
  2. const db: Connection = MongoContext.getConnection('admin');
  3. if (!db) {
  4. throw new Error('获取数据库连接对象失败');
  5. }
  6. const session = await db.startSession();
  7. session.startTransaction();
  8. try{
  9. const cart = new Cart();
  10. cart.userId = '1234';
  11. cart.items = new Array<CartItem>();
  12. cart.items.push(new CartItem('可口可乐',3,1));
  13. cart.items.push(new CartItem('百事可乐',3,10));
  14. await this.context.switchModel(Cart).save(cart,{session});
  15. for(const item of cart.items){
  16. await this.context.switchModel(CartItem).save(item,{session})
  17. }
  18. await session.commitTransaction();
  19. }catch(e){
  20. await session.abortTransaction();
  21. }finally{
  22. session.endSession();
  23. }
  24. }