一种巧妙的对象映射关系设计--JSON-ORM
项目介绍
这是标准数据库封装的上半部分,智能查询(JSON-ORM)的实现。完整代码:https://github.com/zhoutk/gels
设计思路
我们通用的ORM,基本模式都是想要脱离数据库的,几乎都在编程语言层面建立模型,由程序去与数据库打交道。虽然脱离了数据库的具体操作,但我们要建立各种模型文档,用代码去写表之间的关系等等操作,让初学者一时如坠云雾。我的想法是,将关系数据库拥有的完善设计工具之优势与微服务结合起来,数据设计提供结构信息;前端送到后端的json对象自动映射成为标准的SQL查询语句。只要我们理解了标准的SQL语言,我们就能够完成数据库查询操作。我的这种ORM方式,服务端不需要写一行代码,只需完成关系数据库的设计,就能为前端提供标准服务接口。并且遵循一套统一的接口(已经实践检验,满足百分之九九的查询需求)来实现数据库封装,达到业务层可以随意切换数据库的目的。
数据库查询操作接口。
export default interface IDao { select(tablename: string, params: object, fields?: Array<string>): Promise<any>; //自动生成sql语句 execSql(sql: string, values: Array<any>, params: object, fields?: Array<string>): Promise<any>; //执行手动sql语句 }
智能查询(JSON-ORM)
查询保留字:fields, page, size, sort, search, lks, ins, ors, count, sum, groupfields, 定义查询结果字段,支持数组和逗号分隔字符串两种形式
由前端来确定返回的数据库字段信息,这样后端的设计可以适用面更广泛,而不会造成网络带宽的浪费。
在KOA2的框架下,GET请求要支持输入数组,只能把同一个key多次输入,如:age=11&age=22。这样很不方便,我实现了一个参数转换函数,针对数组提供多种输入形式。
arryParsearryParse(arr): Array<any>|null { //返回值为数据或空值 try { if (Array.isArray(arr) || G.L.isNull(arr)) { //如果输入是数组或空,直接返回 return arr } else if (typeof arr === 'string') { //若是字符串 if (arr.startsWith('[')) { //数组的字符串形式,进行转换 arr = JSON.parse(arr) } else { //逗号拼接的字符串,mysql的驱动同时支持参数以字符串形式或数组形式提供, //所以这里可以不加判断,直接用split函数将字符串转化为数组 arr = arr.split(',') } } } catch (err) { arr = null //数组的字符串形式转换失败,刘明输入参数是错误的 } return arr }
查询示例:
请求URL: /rs/users?username=white&age=22&fields=["username","age"] 生成sql: SELECT username,age FROM users WHERE username = ? and age = ?
page, size, sort, 分页排序
在mysql中这比较好实现,limit来分页是很方便的,排序只需将参数直接拼接到order by后就好了。
查询示例:请求URL: /rs/users?page=1&size=10&sort=age desc 生成sql: SELECT * FROM users ORDER BY age desc LIMIT 0,10
search, 模糊查询切换参数,不提供时为精确匹配
提供字段查询的精确匹配与模糊匹配的切换,实现过程中,注意参数化送入参数时,like匹配,是要在参数两边加%,而不是在占位符两边加%。
另外,同一个字段匹配两次模糊查询,需要特别处理,我提供了一种巧妙的方法://将值用escape编码,数组将转化为逗号连接的字符串,用正则全局替换,变成and连接 value = pool.escape(value).replace(/\', \'/g, "%' and " + key + " like '%") //去掉两头多余的引号 value = value.substring(1, value.length - 1) //补齐条件查询语句,这种方式,比用循环处理来得快捷,它统一了数组与其它形式的处理方式 where += key + " like '%" + value + "%'"
查询示例
请求URL: /rs/users?username=i&password=1&search
ins, lks, ors
这是最重要的三种查询方式,如何找出它们之间的共同点,减少冗余代码是关键。ins, 数据库表单字段in查询,一字段对多个值,例:
查询示例:请求URL: /rs/users?ins=["age",11,22,26] 生成sql: SELECT * FROM users WHERE age in ( ? )
ors, 数据库表多字段精确查询,or连接,多个字段对多个值,支持null值查询,例:
查询示例:请求URL: /rs/users?ors=["age",1,"age",22,"password",null] 生成sql: SELECT * FROM users WHERE ( age = ? or age = ? or password is null )
lks, 数据库表多字段模糊查询,or连接,多个字段对多个值,支持null值查询,例:
查询示例:请求URL: /rs/users?lks=["username","i","password",null] 生成sql: SELECT * FROM users WHERE ( username like ? or password is null )
count, sum
这两个统计求和,处理方式也类似,查询时一般要配合group与fields使用。count, 数据库查询函数count,行统计,例:
查询示例:请求URL: /rs/users?count=["1","total"]&fields=["username"] 生成sql: SELECT username,count(1) as total FROM users
sum, 数据库查询函数sum,字段求和,例:
查询示例:请求URL: /rs/users?sum=["age","ageSum"]&fields=["username"]
group, 数据库分组函数group,例:
查询示例:请求URL: /rs/users?group=age&count=["*","total"]&fields=["age"] 生成sql: SELECT age,count(*) as total FROM users GROUP BY age
支持的不等操作符有:>, >=, <, <=, <>, =;逗号符为分隔符,一个字段支持一或二个操作。
特殊处:使用"="可以使某个字段跳过search影响,让模糊匹配与精确匹配同时出现在一个查询语句中
一个字段一个操作,示例:
查询示例:请求URL: /rs/users?age=>,10 生成sql: SELECT * FROM users WHERE age> ?
一个字段二个操作,示例:
查询示例:请求URL: /rs/users?age=>,10,<=,35 生成sql: SELECT * FROM users WHERE age> ? and age<= ?
使用"="去除字段的search影响,示例:
查询示例:请求URL: /rs/users?age==,22&username=i&search 生成sql: SELECT * FROM users WHERE age= ? and username like ?
相关视频课程
运用typescript进行node.js后端开发精要
nodejs实战之智能微服务快速开发框架
JSON-ORM(对象关系映射)设计与实现
资源地址
凝胶(gels)项目: https://github.com/zhoutk/gels
视频讲座资料: https://github.com/zhoutk/sifou
个人博客: https://segmentfault.com/blog...