zenorm
v4.5.1
Published
Easy ORM, easy query. easy typing! Auto generate typescript declaration.
Maintainers
Readme
ZenORM
Node.js 数据库 ORM 框架
ZenWeb 衍生的核心项目,此项目可以独立使用
本框架不主动创建数据库结构,而是根据已有数据库结构来生成操作代码,这么做的原因:
- 在数据库设计层面直接定义表结构比框架生成的通用结构更细致
- 对于已有项目想要使用 ORM 支持更加友好
本框架并不是真正的 ORM 系统,而是类 ORM 的数据库操作层,几乎任何复杂查询都可实现(试试强大的 AB 工具类)
本框架诞生之因就是为了解决 SAAS 系统的单实例多租户问题,所以所有设计上都是从如何在一个系统中使用多个数据库服务器以及多个数据库而导向,
当然也支持传统的单体应用方式(配置 @zenorm/generate 的 bindQuery 即可)。
以下样例代码即是单体应用的使用方式
安装
# 生产依赖
npm install zenorm mysql-easy-query
# 开发依赖
npm install @zenorm/generate @zenorm/generate-mysql --save-dev配置
在 package.json 的 scripts 中增加如下代码,用于执行 dbgen 命令
{
"scripts": {
"dbgen": "zenorm-generate .dbgen.js"
}
}创建文件 .dbgen.js 用于生成数据库结构代码时连接到指定数据库
提示:运行时并不使用此配置
/** @type {import("@zenorm/generate").GenerateConfig} */
export default {
host: "localhost",
port: 3306,
user: "root",
password: "",
bindQuery: "pool@../db",
database: "test"
};演示
以下数据库结构为演示用,在数据中创建表结构
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `profile` (
`id` int(11) NOT NULL,
`edu` varchar(255) DEFAULT NULL,
`work` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`content` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;运行命令开始生成数据库结构代码
npm run dbgen编辑模型关系
编辑生成的模型文件 src/model/user.ts
import { Model, Join, Many } from 'zenorm';
import { UserTable } from './_tables.js';
import { type Profile } from './profile.js';
import * as ProfileRef from './profile.js'; // 互相依赖引入方式
import { Message } from './message.js';
@Model({
pk: 'id',
table: 'user',
})
export default class User extends UserTable {
@Join(ProfileRef, { type: 'OneToMany', asList: false })
profile?: Profile;
@Join(Message)
messages?: Message[];
@Many(Message)
messageList?: Message[];
set age(v) {
if (v === undefined) throw new Error('age is undefined');
if (v === 99) return false;
const date = new Date();
date.setFullYear(date.getFullYear() - v, 1, 1);
this.birthday = date;
}
get age() {
return this.birthday ? ((new Date().getFullYear()) - this.birthday.getFullYear()) : undefined;
}
}编辑生成的模型文件 src/model/profile.ts
import { Model, Join } from 'zenorm';
import { ProfileTable } from './_tables';
import User from './user';
@Model({
pk: 'id',
table: 'profile',
})
export default class Profile extends ProfileTable {
@Join(User)
user?: User;
}初始化数据库访问层
创建代码 src/db.ts
import { createPoolCompatible } from 'mysql-easy-query';
import { Repositories } from './model';
// 创建数据库连接池
export const pool = createPoolCompatible({
pools: {
// 主库
MASTER: {
host: '10.0.0.1',
user: 'root',
database: 'test',
password: '',
},
// 如果需要读写分离,创建命令规则为 SLAVE* 的只读配置
/*
SLAVE1: {
host: '10.0.0.2'
},
*/
}
});开始使用
常规使用
import { User, Message } from './model';
async function test() {
// create
const id = await User.create({ name: 'yf' });
console.log(id); // 1
// get and update
const user = await User.findByPk(id);
user.name = 'yefei';
user.age = 20;
await User.save(user);
// find all
const users = await User.find().all();
// find limit
const users = await User.find().limit(10).all();
// find by where
const users = await User.find({ name: { $like: `%y%` } }).all();
// get all count
const count = await User.find().count();
// page
const page = await User.find().page();
// exists
const exists = await User.find({ name: 'yf' }).exists();
// update
const updatedCount = await User.find({ id: 1 }).update({ name: 'yf', age: 11 });
// delete
const user = await User.findByPk(1);
const deletedCount = await user.delete();
// sql delete
await User.find({ name: 'aaa' }).delete();
// join 预定义
const user = await User.find().join("messages").get();
// join 模型(未定义的)
const user = await Message.find().join(User).all();
// many 独立查询功能
const userList = await User.find().many("messageList").all();
// 指定使用主从库
await User.find().of('MASTER').all();
await User.find().of('SLAVE*').all();
}事物支持
import { pool } from './db';
import { User, Message } from './model';
async function test() {
await pool.transaction(async tx => {
await User.query(tx).find().update({ some: 'data' });
await Message.query(tx).find().update({ some: 'data' });
});
}更多用法
以上仅为常用操作演示,ZenORM 还支持:
- Join 链式查询(
join('user->messages')) - Many 并行查询
- 聚合查询(
group+having+AB.count) - 索引提示(
useIndex/forceIndex/ignoreIndex) - 分页(
page)、取单值(value/values) getOrCreate、createAndGet- Finder
clone()复用 - 非自增主键模型
- 手动定义模型(不依赖代码生成)
- AB 工具类 SQL 表达式
更多用法请参阅 使用指南。 AI 编程工具的项目参考手册
