介绍
mongo-context是团队根据自身产品定位定制的一个操作mongodb的插件模块,提供支持连接多个数据库、数据模型基类(SuperModel)、自动收集数据模型(@TypegooseModel装饰器)
连接数据库
一般来说,连接数据库是在生命周期中服务启动时要做的。为了满足服务端可以连接多个数据库,所以就没有做自动连接数据库的功能,而是手动在服务启动的时候提供多个数据库连接配置,连接数据库
## 安装mongo-context模块,因为typegoose的原因,mongoose最高支持5.10.18
npm install @lzy-plugin/mongo-context @typegoose/typegoose mongoose@5.10.18
//src/configuration.ts
import { 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.ts
import { 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); //支持传递ModelName
console.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.ts
import { 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.ts
import { 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();
}
}