一个学习向工程,为了学习Node.js和ORM框架原理。Node.js下的ORM框架,帮助用户处理数据库。支持一二级缓存、读写分离。避免SQL注入、快速生成SQL语句,同时允许自定义SQL。
参考学习sequelize框架,但内部实现有差别,同时抛弃了许多功能,只能进行简单的增删改查。
声明:历时一个月完成,生产环境不能保证稳定。本人暂时会用于本人的项目中,但处于随时弃坑状态。
- 基础教程
- 1.导入框架
- 2.初始化框架
- 配置
- 导入依赖
- 创建启动类
- 3.注册模型
- QueryTypes查询类型
- 初始化实体类
- 模型管理器(重要)
- 验证 & 约束
- 查询
- 插入/更新
- 删除
- 6.自定义查询
- 其他
- 2.事务
- 3.配置参数
- 5.依赖项
- 6.关于
npm install haorm -S
数据库连接配置可以参考mysql2的配置
const MYSQL_CONFIG = {
host : 'localhost', //数据库地址
port : '3306', //端口
user : 'root', //用户名
password : '123456',//数据库密码
database : 'test', //数据库名
connectionLimit: 1000 //连接池数量
};
const MYSQL_CONFIG = {
LoadBalance: 'Default', //负载均衡策略(具体在下面)
Expiration: 120 * 1000, //单点故障(ms)
read:[{ //读库列表(从)
host : 'localhost', //数据库地址
port : '3306', //端口
user : 'root', //数据库用户名
password : '123456', //数据库密码
database : 'test' //数据库库名
weight : 2, //权重(负载均衡策略为加权时)默认1
},{
host : 'localhost',
port : '3306',
user : 'root',
password : '123456',
database : 'test',
weight : 4,
connectionLimit: 4000 //连接池最大连接数量
}],
write:[{ //写库列表(主)
host : 'localhost',
port : '3306',
user : 'root',
password : '123456',
database : 'test'
},{
host : 'localhost',
port : '3306',
user : 'root',
password : '123456',
database : 'test',
weight : 4,
connectionLimit: 4000
}],
common:{ //所有数据库配置通用设置,单个设置优先
connectionLimit: 2000
}
}
其中LoadBalance为字符串,目前含:
ROUNDROBIN //轮询
RANDOM //随机
WEIGHTEDROUNDROBIN //加权轮询
WEIGHTEDRANDOM //加权随机
const { Haorm } = require('haorm')
或const Haorm = require('haorm')
const haorm= new Haorm('mysql',MYSQL_CONFIG,{timeout:4000});
Haorm构造器含三个参数: 1.第一个参数——指定数据库类型【必填】。 字符串,目前仅含mysql 2.第二个参数——数据库连接配置【必填】。 - 参考数据库配置 3.第三个参数——其他全局配置【选填】。
- 可以才能考配置参数
{
timeout: 4000 //数据库请求最大时间(ms),默认4000ms
logging: (info,type)=>{
console.log(info)
}
//自定义日志函数,框架会传2个参数。
//第一个为日志信息,第二个为信息的类型(目前仅支持'query','transaction'两个类型。)
}
需要导入haorm包,不使用QueryTypes直接用字符串也是可以的。
读(READ)写(WRITE)或者增(INSERT)删(DELETE)改(UPDATE)查(SELECT)可以用来选择和判断使用主从库。
使用Haorm类中的define方法注册模型(参考如下代码)
define方法含2个参数: 1.第一个参数——标签,用于从模型管理器中获取模型【必填】。 自定义字符串。唯一标签、不可重复。 2.第二个参数——数据库连接配置【必填】。 - 参考如下代码的注释
const { Haorm,QueryTypes } = require('haorm')
const haorm= new Haorm('mysql',MYSQL_CONFIG,{timeout:4000});
const user = haorm.define('user'/*标签,用于模型管理*/,{
tablename:'b_user',//表名
struct : {
//数据库结构,key为自定义名称(开头不能为$)
id : {
fieldName : 'id',//表字段名称
value : 'number',//暂时没用
primarykey: true //是否是主键
},
name : {
fieldName : 'u_login',
value : 'string',
validate:{//【可选】验证,参考验证 & 约束的章节
not:'ttt',
//自定义方法
notTTT(val){
if(val == 'ttt'){
throw new Error('validate error!');
}
}
}
},
password : {
fieldName : 'u_passwd',
value : 'string',
ignore:[QueryTypes.UPDATE] //忽略的操作,例子为修改时忽略该字段
},
mail : {
fieldName : 'u_mail',
value : 'string'
}
},
cache: {
enable: true, //是否启动二级缓存,若开启必填
type: 'FIFO', //选择二级缓存算法,类型参考下方
//不填默认LIRS算法
size: 200 //该模型下二级缓存池的大小
//不填默认100
} //该模型是否使用二级缓存,不存在cache不启动二级缓存
})
**二级缓存算法:**FIFO、LRU、LIRS
先导入haorm框架包
const Haorm = require('haorm')
获取模型
示例中user为标签
//'user'为标签
const userModel = Harom.getModel('user'); //参数为注册模型的标签
获取接口
1.基础查询接口,不存在缓存,不建议使用
const interface = userModel.getInterface(); //基础查询接口,不存在缓存,不建议使用
2.session里包含了查询接口,同时使用session则自动开启了一级缓存
const session = Harom.openSession('user'); //session里包含了查询接口,同时使用session则自动开启了一级缓存
或者
const session = Harom.openSession('user',{timeout:1000}); //第二个参数表示session的全局配置
可以更换session里的模型(更换其他表查询)
session.changeModel('group'); //'group'为注册模型时的标签
或者
const groupModel = Harom.getModel('group');
session.changeModel(groupModel); //直接使用模型更换
开启事务(具体参考其他-事务章节)
session.beginTransaction();
session.push(sql/*自定义sql语句*/)或者session.insert({name:'abc'});
cosnt result = session.commit();//结果按照顺序排列
在创建模型的时候,使用validate
参数即可使用。
mail:{
fieldName : 'u_mail',
validate:{
is: /^[a-z]+$/i, // 匹配这个 RegExp
is: ["^[a-z]+$",'i'], // 与上面相同,但是以字符串构造 RegExp
not: /^[a-z]+$/i, // 不匹配 RegExp
not: ["^[a-z]+$",'i'], // 与上面相同,但是以字符串构造 RegExp
isInt: true, // 检查有效的整数
isFloat: true, // 检查有效的浮点数
isLowercase: true, // 检查小写
isUppercase: true, // 检查大写
notNull: true, // 不允许为空
isNull: true, // 只允许为空
notEmpty: true, // 不允许空字符串
contains: 'foo', // 强制特定子字符串
notIn: ['foo', 'bar'], // 检查值不是这些之一
isIn: ['foo', 'bar'], // 检查值是其中之一
notContains: 'bar', // 不允许特定的子字符串
len: [2,10], // 仅允许长度在2到10之间的值,第二个小于0时表示最大长度无限。
isDate: true, // 只允许日期字符串
max: 23, // 仅允许值 <= 23
min: 23, // 仅允许值 >= 23
// 自定义验证器的示例:
nottt(val){
if(val == 'ttt'){
throw new Error('test is success!');
}
}
DivideBy2(value) {
if (parseInt(value) % 2 !== 0) {
throw new Error('can't divide by two!');
}
}
}
}
以下接口的参数用[]
包住的,表示可选,即可以不传该参数。
PS:所有方法均为异步,所以一定要使用async-await
findList([conditions,config]) :基础查询
findAll([conditions,config]) :查询所有(经过项目迭代,已经等同于findList了)
findOne([conditions,config]) :仅查询一条/只获取第一条(底层使用了limit 1
语句)
findById(id,[fields,config]) :根据id查询(底层采用WHERE id = 'id'
的sql语句)
id : string或number类型。查询的主键条件的值。
当id为Array时,表示主键条件符合等于多个值
fields : 【可选】object或者array类型。需要查询的列。不选则返回所有列。
config : 【可选】object类型。局部配置(优先级最高)。
findAndCountAll([conditions,config]) : 查询结果和不带limit
和offset
的条件结果数量(结合了 findAll
和 count
的便捷方法)
count([conditions,config]) :获取查询的数量。
max(key,[conditions,config]) :获取某列的最大值。
min(key,[conditions,config]) :获取某列的最小值。
sum(key,[conditions,config]) :获取某列的求和。
- 返回值:
findXXX之类的方法:
{
results:[
{每行的结果(key键为自定义的列名)}
{每行的结果,$join:[{},{}]}//如果有表连接,则会出现$join的键key,为数组Array格式,表示第几个表连接。
]
}
count、sum、max、min等统计函数的方法
直接返回统计结果,比如 100 (Number类型)。
insert(params,[config]) :插入数据
params : object类型。插入的数据,使用键值对,键表示模型的列名称,值表示插入的值。如下:
{
id:1,
name:'name'
}
config : 【可选】object类型。局部配置(优先级最高)。
update(params,conditions,[config]) : 更新数据
params : object类型。修改的数据,使用键值对,键表示模型的列名称,值表示更新的值。
**PS:**如果存在id(主键)的键值,自动在条件中使用WHERE id = 值
。如下:
{
name:'name',
mail:'123@qq.com'
} ==> UPDATE name,mail
或者
{
id: 1,//主键
name:'name',
mail:'123@qq.com'
} ==> UPDATE name,mail WHERE id = 1
conditions : 【可选,如果params存在主键】object类型。条件参考:conditions参数文档(仅使用where和limit条件)。
update更新有一个特殊的配置参数noWhere。默认为false。为ture时,才可以不带WHERE;
{
noWhere: true //默认为false。为ture时,才可以不带WHERE。
}
config : 【可选】object类型。局部配置(优先级最高)。
saveOrUpdate(params,[config]) : 更新或插入(更新无效后插入数据)
params : object类型。修改的数据,使用键值对,键表示模型的列名称,值表示更新/插入的值。
PS:必须包含主键(id),如下:
{
id: 1,
name: 'TEST'
}
config : 【可选】object类型。局部配置(优先级最高)。
- 返回值:
插入insert(params)的方法:
{
results:[10,11], //插入后自动递增生成的主键,根据params的顺序排列。
affectedRows: 2, //影响的行数
}
更新的方法
{
affectedRows: 2, //影响的行数
}
delete(conditions,[config]) : 删除数据
conditions: object类型。条件参考:conditions参数文档(仅使用where和limit条件)。
delete删除有一个特殊的参数noWhere。默认为false。为ture时,才可以不带WHERE;
{
noWhere: true //默认为false。为ture时,才可以不带WHERE。
}
conditions例子如下:
{
where:{
id: 1, //默认使用=
name:{value:'name',symbol:'!='} //需要其他符号判断,可以使用这种格式
}
}
或者
[{key:'id',value:1,symbol:'='},{key:'name',value:'name',symbol:'!='}]
==> DELET WHERE id = 1 AND name != 'name'
config : 【可选】object类型。局部配置(优先级最高)。
deleteById(id,config) : 根据(id)主键删除数据
id : string或number或Array类型。查询的主键条件的值。
当id为Array时,表示主键条件符合等于多个值
config : 【可选】object类型。局部配置(优先级最高)。
- 返回值
删除的方法
{
affectedRows: 2, //影响的行数
}
1.使用基础查询接口(不使用缓存)【不推荐】
const result = Harom.query(sql,config);
sql : string或array类型。
config : 【可选】object类型。局部配置(优先级最高)。此时建议使用type参数,可以参考QueryType
{
//设置你查询的sql语句的类型,QueryType需要导入haorm框架包
type:'SELECT'或者 QueryType.SELECT
model: 需要查询的model //如果没有,就不使用二级缓存
}
2.使用openSession()的查询接口(使用缓存)【推荐】
导入haorm包,const Haorm = require('haorm')
生成Haom类,const sqlOperation = new Haorm('mysql',MYSQL_CONFIG,{timeout:4000});
开启session,let session = sqlOperation.openSession('user');
使用自定义查询,let result = session.query(sql,config)
sql : string或array类型。
config : 【可选】object类型。局部配置(优先级最高)。此时建议使用type参数,可以参考QueryType
{
//设置你查询的sql语句的类型,QueryType需要导入haorm框架包
type:'SELECT'或者 QueryType.SELECT
//model默认已经添加上了
}
虽然已经实现部分功能,但未来版本不再维护。仅个人练手而实现。
参考:Wrapper文档
导入haorm包const Haorm = require('haorm')
生成Haom类const sqlOperation = new Haorm('mysql',MYSQL_CONFIG,{timeout:4000});
开启session,同时开启事务
let session = sqlOperation.openSession('user');
session.beginTransaction();
进行查询(期间不输出结果),比如
let resultT = await session.update({id:1,name:'name'});
resultT = await session.insert({name:'name2'});
//resultT is undefined
最后提交事务,commit()方法参数可以为空或者config(参考3.配置参数)
let result = session.commit();
或者let result = await session.commit({timeout:4000});
此时,result结果输出,为Array数组类型,按照查询的顺序排列。比如例子中第一个为更新结果、第二个为插入结果。
参数
- timeout : number类型。连接请求超时时间,单位ms。默认4000ms。
- logging: 只支持全局,自定义日志函数,会传两个参数。第一个为日志信息,第二个为信息的类型(目前仅支持'query','transaction'两个类型。)
- type : string类型。查询类型,参考QueryType。READ和WRITE等可以用于选择主从数据库。不建议使用,不填写使用默认即可。
**-tag: ** 模型标签(优先于model参数),关乎二级缓存。如果使用session默认会添加tag标签对应的model模型。如果使用原始接口但没添加tag,则不使用缓存。
- model: 模型类,关乎二级缓存。如果使用session默认会添加对应的model模型。如果使用原始接口但没添加model,则不使用缓存。
结构如下:
{
timeout: 4000,
logging: (info,type)=>{
console.log(info)
}
}
本项目不自带日志,若要启动日志需要自行使用第三方日志。
具体使用方法参考初始化框架-创建启动类和3.配置参数。
在创建启动类的时候,在第三个参数config中加入键logging自定义日志函数,如下:
{
logging: (info,type)=>{
console.log(info)
}
}
会传两个参数。第一个为日志信息,第二个为信息的类型(目前仅支持'query','transaction'两个类型。)
"mysql2": "^2.2.5",
"sqlstring": "^2.3.2"
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。