zenorm
v4.1.0
Published
Easy ORM, easy query. easy typing! Auto generate typescript declaration.
Downloads
1,170
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, 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 描述支持使用文件名,解决互相依赖问题
@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: '',
},
// 如果需要读写分离,创建命令规则为 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' });
});
}