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
用于生成数据库结构代码时连接到指定数据库
提示:运行时并不使用此配置
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, propset } from 'zenorm';
import { UserTable } from './_tables';
import { Profile } from './profile';
import { Message } from './message';
@model({
pk: 'id',
table: 'user',
})
export default class User extends UserTable {
@join(__dirname + '/profile', { type: 'OneToMany', asList: false })
profile?: Profile;
@join(Message)
messages?: Message[];
@many(Message)
messageList?: Message[];
@propset(function (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;
return true;
})
age = 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: '',
},
}
});
开始使用
常规使用
import { User, Message } from './model';
async function test() {
const id = await User.create({ name: 'yf' });
console.log(id);
const user = await User.findByPk(id);
user.name = 'yefei';
user.age = 20;
await User.save(user);
const users = await User.find().all();
const users = await User.find().limit(10).all();
const users = await User.find({ name: { $like: `%y%` } }).all();
const count = await User.find().count();
const page = await User.find().page();
const exists = await User.find({ name: 'yf' }).exists();
const updatedCount = await User.find({ id: 1 }).update({ name: 'yf', age: 11 });
const user = await User.findByPk(1);
const deletedCount = await user.delete();
await User.find({ name: 'aaa' }).delete();
const user = await User.find().join("messages").get();
const user = await Message.find().join(User).all();
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' });
});
}
Related projects
mysql-easy-query
sql-easy-builder