跳到主要内容

repositories - ZenORM 自动生成 Repository 类

本模块由 ZenORM 代码生成器(zenorm-generate)自动生成,为每个数据库表对应的 Model 类提供完整的 CRUD 操作方法。

通过 bindQuery 机制将 MySQL 连接池绑定到 ORM 模型,使得在业务代码中可以直接使用 Model 类的静态方法进行数据库操作,无需手动管理连接。

注意

_repositories.ts 文件为自动生成文件,每次重新生成数据库结构时会被覆盖,请不要手动修改此文件。如需扩展模型方法,请在对应的模型文件中添加。

前置条件

确保已安装 MySQL 模块和 ZenORM 相关依赖:

# 生产依赖
npm install @zenweb/mysql zenorm

# 开发依赖(用于代码生成)
npm install --save-dev @zenorm/generate @zenorm/generate-mysql

bindQuery 机制

bindQuery 是连接 MySQL 连接池与 ZenORM Repository 的桥梁。在 MySQL 模块初始化时,将连接池实例传递给 ORM 生成的 bindQuery 函数,之后所有 Repository 的静态方法都会自动使用该连接池。

单数据库配置

src/index.ts

src/index.ts
import { create } from 'zenweb';
import modMySQL from '@zenweb/mysql';
import { bindQuery } from './model';

create()
.setup(modMySQL({
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '',
database: 'test',
charset: 'utf8mb4',
timezone: '+08:00',
connectionLimit: 100,
// 将连接池绑定到 ORM
bindQuery,
}))
.start();

多租户配置

在使用 @zenweb/tenant 多租户模块时,bindQuery 支持动态切换数据库连接:

src/index.ts
import { create } from 'zenweb';
import modTenant from '@zenweb/tenant';
import { bindQuery } from './model';

create()
.setup(modTenant({
tenantGetter: ctx => {
// 根据请求获取租户信息
return tenantsConfig[ctx.host];
},
pools: {
S1: {
MASTER: {
host: '127.0.0.1',
port: 3306,
user: 'root',
password: '123456',
charset: 'utf8mb4',
timezone: '+08:00',
connectionLimit: 100,
},
},
S2: {
MASTER: {
host: '127.0.0.1',
port: 3307,
user: 'root',
password: '123456',
charset: 'utf8mb4',
timezone: '+08:00',
connectionLimit: 100,
},
},
},
bindQuery, // ORM 查询绑定
}))
.start();

自动生成的 Repository 结构

运行 npm run dbgen 后,会在 model/_repositories.ts 中生成类似以下结构的代码:

src/model/_repositories.ts(自动生成)
import { createRepositoryQuery, QueryParam } from 'zenorm';
import * as _tables from './_tables';
import _User from './user';

let _bindQuery: QueryParam;
function _query() { return typeof _bindQuery === 'function' ? _bindQuery() : _bindQuery }
/** 绑定模型 Query 源 */
export function bindQuery(query: QueryParam) { _bindQuery = query; }

export class User extends _User {
/** 使用指定 Query 对象查询 UserRepository */
static query = createRepositoryQuery<User, _tables.UserTable, number>(User);
/** Query 绑定的 UserRepository */
static repository = User.query(_query);
static find: typeof User.repository.find = User.repository.find.bind(User.repository);
static findByPk: typeof User.repository.findByPk = User.repository.findByPk.bind(User.repository);
static getByPk: typeof User.repository.getByPk = User.repository.getByPk.bind(User.repository);
static count: typeof User.repository.count = User.repository.count.bind(User.repository);
static exists: typeof User.repository.exists = User.repository.exists.bind(User.repository);
static create: typeof User.repository.create = User.repository.create.bind(User.repository);
static createAndGet: typeof User.repository.createAndGet = User.repository.createAndGet.bind(User.repository);
static getOrCreate: typeof User.repository.getOrCreate = User.repository.getOrCreate.bind(User.repository);
static save: typeof User.repository.save = User.repository.save.bind(User.repository);
static update: typeof User.repository.update = User.repository.update.bind(User.repository);
static delete: typeof User.repository.delete = User.repository.delete.bind(User.repository);
/** 保存当前实例数据 */
save() { return User.repository.save(this); }
/** 更新当前实例数据 */
update(data: Partial<User>) { return User.repository.update(this, data); }
/** 删除当前实例数据 */
delete() { return User.repository.delete(this); }
}

Repository 方法说明

查询方法

方法说明返回值
find(conditions?)条件查询,返回 QueryBuilderQueryBuilder
findByPk(pk)按主键查询Promise<Model \| null>
getByPk(pk)按主键查询,不存在则抛出异常Promise<Model>
count(conditions?)统计记录数Promise<number>
exists(conditions)判断记录是否存在Promise<boolean>

写入方法

方法说明返回值
create(data)创建记录Promise<Model>
createAndGet(data)创建并获取完整记录Promise<Model \| null>
getOrCreate(conditions, data)查询或创建记录Promise<Model>
save(instance)保存实例(新增或更新)Promise<Model>
update(instance, data)更新实例指定字段Promise<Model>
delete(instance)删除记录Promise<void>

使用示例

在 Service 中使用

src/service/chat-room.ts
import { Context, Inject } from 'zenweb';
import { ChatRoom, ChatRoomMember, ChatRoomMessage, User } from '../model';

export class ChatRoomService {
@Inject ctx!: Context;

/**
* 创建聊天室
*/
async create(name: string) {
const userId = this.ctx.user!.id!;

// 使用 createAndGet 创建并获取完整记录
const room = await ChatRoom.createAndGet({
name,
creator_id: userId,
});
if (!room) {
throw new Error('创建聊天室失败');
}

// 使用 create 创建成员记录
await ChatRoomMember.create({
room_id: room.id!,
user_id: userId,
role: 'owner',
});

return room;
}

/**
* 加入聊天室
*/
async join(roomId: number) {
const userId = this.ctx.user!.id!;

// 使用 getByPk 查询,不存在会抛出异常
const room = await ChatRoom.getByPk(roomId);

// 使用 find 条件查询,get() 获取单条记录
const exists = await ChatRoomMember.find({ room_id: roomId, user_id: userId }).get();
if (exists) {
throw new Error('已在聊天室中');
}

await ChatRoomMember.create({
room_id: roomId,
user_id: userId,
role: 'member',
});

return room;
}

/**
* 获取聊天室成员列表
*/
async getMembers(roomId: number) {
// 使用 find 条件查询,all() 获取所有记录
const members = await ChatRoomMember.find({ room_id: roomId }).all();
const userIds = members.map(m => m.user_id!);
// 使用 find 进行批量查询
const users = userIds.length > 0 ? await User.find({ id: userIds }).all() : [];
return members.map(m => ({
id: m.id,
room_id: m.room_id,
user_id: m.user_id,
role: m.role,
user: users.find(u => u.id === m.user_id),
}));
}

/**
* 获取聊天记录
*/
async getMessages(roomId: number, limit: number = 50) {
// 使用 find 链式调用,支持 order、limit 等方法
const messages = await ChatRoomMessage.find({ room_id: roomId })
.order('created_at', 'DESC')
.limit(limit)
.all();
return messages.reverse();
}
}

在 Controller 中使用

src/controller/chat-room.ts
import { Get, Post, Context, Injectable } from 'zenweb';
import { ChatRoomService } from '../service/chat-room';

@Injectable
export class ChatRoomController {
constructor(
private ctx: Context,
private service: ChatRoomService,
) {}

@Post()
async create() {
const { name } = this.ctx.request.body;
return await this.service.create(name);
}

@Post()
async join() {
const { roomId } = this.ctx.request.body;
return await this.service.join(roomId);
}

@Get()
async members() {
const roomId = Number(this.ctx.query.room_id);
return await this.service.getMembers(roomId);
}
}

实例方法 - 修改并保存

Repository 同时为模型实例提供 save()update()delete() 方法:

src/service/chat-room.ts
// 获取实例后修改属性,调用 save() 保存
async recallMessage(messageId: number) {
const message = await ChatRoomMessage.getByPk(messageId);
if (!message) {
throw new Error('消息不存在');
}

message.recalled = true;
await message.save();
return message;
}

// 使用 update() 仅更新指定字段
async muteMember(roomId: number, userId: number) {
const member = await ChatRoomMember.find({ room_id: roomId, user_id: userId }).get();
if (!member) {
throw new Error('成员不存在');
}

await member.update({ muted: true });
return member;
}

// 使用 delete() 删除记录
async kickMember(roomId: number, userId: number) {
const member = await ChatRoomMember.find({ room_id: roomId, user_id: userId }).get();
if (!member) {
throw new Error('成员不存在');
}

await member.delete();
}

静态方法 - 直接更新和删除

除实例方法外,也可以通过静态方法直接操作:

// 按主键删除
await ChatRoom.delete(room);

// 直接更新指定字段(无需先查询)
await ChatRoom.update(room, { name: '新名称' });

// 按条件查询是否存在
const exists = await ChatRoomMember.exists({ room_id: 1, user_id: 2 });

// 统计数量
const count = await ChatRoomMessage.count({ room_id: 1 });

使用事务

通过 @zenweb/mysql$mysql 全局对象执行事务操作:

src/service/order.ts
import { $mysql } from '@zenweb/mysql';
import { Order, OrderItem } from '../model';

export class OrderService {
async createOrder(userId: number, items: { productId: number; quantity: number }[]) {
return await $mysql.transaction(async tx => {
// 在事务中创建订单
const order = await Order.createAndGet({
user_id: userId,
status: 'pending',
});

// 在事务中批量创建订单项
for (const item of items) {
await OrderItem.create({
order_id: order!.id,
product_id: item.productId,
quantity: item.quantity,
});
}

return order;
});
}
}
补充说明

Repository 类通过 bindQuery 绑定全局连接池,在事务场景中需要配合 $mysql.transaction() 使用,确保所有操作在同一个数据库连接上执行。