haorm v0.1.0
haorm框架
一个学习向工程,为了学习Node.js和ORM框架原理。Node.js下的ORM框架,帮助用户处理数据库。支持一二级缓存、读写分离。避免SQL注入、快速生成SQL语句,同时允许自定义SQL。
参考学习sequelize框架,但内部实现有差别,同时抛弃了许多功能,只能进行简单的增删改查。
声明:历时一个月完成,生产环境不能保证稳定。本人暂时会用于本人的项目中,但处于随时弃坑状态。
使用教程
- 基础教程
- 1.导入框架
- 2.初始化框架
- 配置
- 导入依赖
- 创建启动类
- 3.注册模型
- QueryTypes查询类型
- 初始化实体类
- 模型管理器(重要)
- 验证 & 约束
- 查询
- 插入/更新
- 删除
- 6.自定义查询
- 其他
- 2.事务
- 3.配置参数
- 5.依赖项
- 6.关于
基础教程
1.导入框架
npm install haorm -S
2.初始化框架
配置
数据库连接配置可以参考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'两个类型。)
}
3.注册模型
QueryTypes查询类型
需要导入haorm包,不使用QueryTypes直接用字符串也是可以的。
读(READ)写(WRITE)或者增(INSERT)删(DELETE)改(UPDATE)查(SELECT)可以用来选择和判断使用主从库。
- SELECT : 'SELECT',
- INSERT : 'INSERT',
- UPDATE : 'UPDATE',
- DELETE : 'DELETE',
- UPSERT : 'UPSERT',
- READ : 'READ',
- WRITE : 'WRITE',
初始化实体类
使用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!');
}
}
}
}
4.数据库操作(基础)
以下接口的参数用[]
包住的,表示可选,即可以不传该参数。
PS:所有方法均为异步,所以一定要使用async-await
查询
findList(conditions,config) :基础查询
- conditions : 【可选】Object或String类型。条件。不选择默认返回所有列。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
findAll(conditions,config) :查询所有(经过项目迭代,已经等同于findList了)
- conditions : 【可选】Object或String类型。条件。不选择默认返回所有列。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
findOne(conditions,config) :仅查询一条/只获取第一条(底层使用了
limit 1
语句)- conditions : 【可选】Object或String类型。条件。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
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
的便捷方法)- conditions : 【可选】Object或String类型。条件。不选择默认返回所有列。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
count(conditions,config) :获取查询的数量。
- conditions : 【可选】object类型。条件。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
max(key,conditions,config) :获取某列的最大值。
- key : string类型。模型的列名称。
- conditions : 【可选】object类型。条件。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
min(key,conditions,config) :获取某列的最小值。
- key : string类型。模型的列名称。
- conditions : 【可选】object类型。条件。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
sum(key,conditions,config) :获取某列的求和。
- key : string类型。模型的列名称。
- conditions : 【可选】object类型。条件。参考:conditions参数文档
- config : 【可选】object类型。局部配置(优先级最高)。
- 返回值:
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, //影响的行数
}
5.表关联查询(进阶)
6.自定义查询
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默认已经添加上了 }
其他
1.Wrapper查询
虽然已经实现部分功能,但未来版本不再维护。仅个人练手而实现。
参考:Wrapper文档
2.事务
导入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数组类型,按照查询的顺序排列。比如例子中第一个为更新结果、第二个为插入结果。
3.配置参数
参数
- 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) } }
4.自定义日志插件
本项目不自带日志,若要启动日志需要自行使用第三方日志。
具体使用方法参考初始化框架-创建启动类和3.配置参数。
在创建启动类的时候,在第三个参数config中加入键logging自定义日志函数,如下:
{
logging: (info,type)=>{
console.log(info)
}
}
会传两个参数。第一个为日志信息,第二个为信息的类型(目前仅支持'query','transaction'两个类型。)
5.依赖项
"mysql2": "^2.2.5",
"sqlstring": "^2.3.2"
6.关于
邮箱(ahaohe@foxmail.com)