【前言】
前面讲过ORM的前世今生,对ORM框架不了解的朋友可以参考博文:https://www.cnblogs.com/7tiny/p/9551754.html
今天,我们主要通过设计一款轻量级的ORM框架来介绍:"如何实现一个ORM框架"
文末给出了GitHub源码地址~
【基本要素】
既然是ORM框架,那么必不可或缺的三点:
1.Sql语句的自动生成
2.数据结果集自动映射到类型实体
3.多数据库的支持
甚至可以在此三点的基础上扩展出更多的:
1.缓存处理
2.Api的封装
3.日志系统
基于以上几点,那么我们逐步开始我们的设计:
为了功能抽象和细化的职责划分,我们将各个功能点拆分成为各个组件,灵活进行装配。
数据存储核心:调用底层数据库驱动执行Sql语句,将数据持久化
表映射描述器:描述表和实体的映射关系
Sql语句转化器:将封装的数据操作Api转化成对应数据库的Sql语句
数据操作上下文:用户数据操作信息传递,包装,数据库连接管理等,缓存核心配置信息的承载
缓存核心:用户ORM框架的缓存支持(一级缓存/二级缓存)
【实现细节】
我们抽象出核心功能组件后,对各个功能组件进行详细设计:
数据存储核心:
数据存储核心主要包括对多种数据库驱动的封装调用,读写分离的简单策略,查询数据集合与强类型实体的映射(性能优化点,目前采用Expression 表达式树缓存委托方式)。
这里以封装的支持多种关系型数据库的DbHelper形式呈现
DbHelper
表映射描述器:
表映射描述器定义了一系列对实体的标签,以描述该实体和数据库以及数据库表之间的映射关系。除此之外还扩展了对数据库表缓存的描述。
Sql语句转化器:
实体类+条件 Sql语句转化过程:
Sql语句转化器的功能为将友好查询Api传递的Lambda表达式语句转化成对应功能的Sql条件语句,以及对应不同数据库生成针对数据库的Sql语句。
数据操作上下文:
数据库操作上下文作为全部数据操作的载体,在DbContext的基础上分离出SqlDbContext和NoSqlDbContext,分别支持关系型数据库和非关系型数据库。在关系型数据库上下文基础上又可以衍生出各种类型的关系型数据库上下文。该设计保证了组件的水平扩容的能力。
上下文除了维系各种数据库操作的支持以外,还扩展出了缓存组件的强力支持,可以在上下文中设置当前会话的缓存配置项。
缓存核心:
缓存核心拓扑:
缓存核心处理流程:
详细缓存策略:
缓存的处理逻辑比较细化,组件的缓存统一由缓存管理核心处理,缓存核心分别调用一级缓存和二级缓存处理对象缓存。缓存处理的步骤如下:
1.判断是否开启了二级缓存,如果未开启,跳过。
2.如果开启了二级缓存,检查是否存在二级缓存,如果不存在,则判断是否对实体开启表缓存,如果开启,则开启后台线程扫描表,存储表数据为二级缓存。
3.判断是否存在一级缓存,如果存在,直接返回一级缓存的结果集。
4.如果一级缓存不存在,则执行查询命令,并写入一级缓存。
当二级缓存存在时:
1.拿出二级缓存并对二级缓存执行增删改查操作,并执行对应的增删改操作持久化过程。
2.后续所有查询优先从二级缓存中获取。
如果二级缓存开启状态,执行增删改命令的同时,会同步维护持久化数据和二级缓存数据。
备注:
二级缓存是针对某表进行的策略,不是针对所有数据库表的,如果数据库表数量太大,则不建议对该表开启二级缓存,以免耗费大量的内存资源。
【SevenTiny.Bantina.Bankinate ORM框架的使用】
Nuget包源搜索 SevenTiny.Bantina.Bankinate 安装
新建一个数据库上下文类(对应数据库名称,类似EntityFramework的上下文类)
如果和库名不一致,则使用DataBase标签进行特殊映射。并在上下文类传递链接字符串和一级缓存和二级缓存的开启配置(默认都关闭)。
这里测试使用SqlServer数据库,因此继承了SqlServerDbContext,如果是其他数据库,则继承对应的数据库。
根据数据库表创建实体类(实体类可以使用代码生成器自动生成,生成器迭代升级中,有需求可以联系博主)。
这里提供了一个Student类(表),并使用 TableCaching 标签指定了该表二级缓存的开启(重载可以配置该表二级缓存时间)。
Id为主键,并且是自增列。
Api列表:
SevenTiny.Bantina.Bankinate ORM框架提供了一系列标准的非标准的数据查询api,api基于Lambda Expression写法,以便习惯了.Net平台Linq的人群很快上手,无学习成本。
复制代码
1 /*********************************************************
2 * CopyRight: 7TINY CODE BUILDER.
3 * Version: 5.0.0
4 * Author: 7tiny
5 * Address: Earth
6 * Create: 2018-04-19 23:58:08
7 * Modify: 2018-04-19 23:58:08
8 * E-mail: dong@7tiny.com | sevenTiny@foxmail.com
9 * GitHub: https://github.com/sevenTiny
10 * Personal web site: http://www.7tiny.com
11 * Technical WebSit: http://www.cnblogs.com/7tiny/
12 * Description:
13 * Thx , Best Regards ~
14 *********************************************************/
15 using System;
16 using System.Collections.Generic;
17 using System.Data;
18 using System.Linq.Expressions;
19
20 namespace SevenTiny.Bantina.Bankinate
21 {
22 ///
23 /// 通用的Api接口,具备基础的操作,缓存
24 ///
25 public interface IDbContext : IDisposable, IBaseOerate, ICacheable
26 {
27 }
28
29 ///
30 /// 基础操作Api
31 ///
32 public interface IBaseOerate
33 {
34 void Add(TEntity entity) where TEntity : class;
35 void AddAsync(TEntity entity) where TEntity : class;
36 void Add(IEnumerable entities) where TEntity : class;
37 void AddAsync(IEnumerable entities) where TEntity : class;
38
39 void Update(Expression> filter, TEntity entity) where TEntity : class;
40 void UpdateAsync(Expression> filter, TEntity entity) where TEntity : class;
41
42 void Delete(Expression> filter) where TEntity : class;
43 void DeleteAsync(Expression> filter) where TEntity : class;
44
45 bool QueryExist(Expression> filter) where TEntity : class;
46 int QueryCount(Expression> filter) where TEntity : class;
47 TEntity QueryOne(Expression> filter) where TEntity : class;
48 List QueryList(Expression> filter) where TEntity : class;
49 }
50
51 ///
52 /// 执行sql语句扩展Api
53 ///
54 public interface IExecuteSqlOperate
55 {
56 void ExecuteSql(string sqlStatement, IDictionary parms = null);
57 void ExecuteSqlAsync(string sqlStatement, IDictionary parms = null);
58 DataSet ExecuteQueryDataSetSql(string sqlStatement, IDictionary parms = null);
59 object ExecuteQueryOneDataSql(string sqlStatement, IDictionary parms = null);
60 TEntity ExecuteQueryOneSql(string sqlStatement, IDictionary parms = null) where TEntity : class;
61 List ExecuteQueryListSql(string sqlStatement, IDictionary parms = null) where TEntity : class;
62 }
63
64 ///
65 /// 分页查询扩展Api
66 ///
67 public interface IQueryPagingOperate
68 {
69 List QueryListPaging(int pageIndex, int pageSize, Expression> orderBy, Expression> filter, bool isDESC = false) where TEntity : class;
70 List QueryListPaging(int pageIndex, int pageSize, Expression> orderBy, Expression> filter, out int count, bool isDESC = false) where TEntity : class;
71 }
72
73 ///
74 /// 缓存接口,实现该接口的类必须具备ORM缓存
75 ///
76 public interface ICacheable
77 {
78 }
79 }
复制代码
查询全部(可以根据使用场景组装lambda表达式):
新增一条数据:
修改数据:
删除数据:
【框架缓存性能测试】
缓存性能测试的单元测试代码:
一级二级缓存测试代码
不带缓存的查询:
执行查询100次耗时:6576.8009 ms
一级缓存开启,二级缓存未开启:
执行查询10000次耗时:1598.2349 ms
一级缓存和二级缓存同时开启:
执行查询10000次耗时:5846.0249
实际上,二级缓存开启以后,最初的查询会走一级缓存。待二级缓存对表扫描结束以后,后续查询将维护二级缓存数据,不再访问数据库表。
【系统展望】
1.查询api对特定列查询列的支持(性能提升)
2.对一对多关系的主外键查询支持
3.更多种类数据库的支持
4.打点日志的支持
【总结】
通过本文的一系列分析,不知各位看官对ORM框架的设计思路有没有一个整体的认识。如果在分析结束还没有充分理解设计思路,那么简单粗暴直接上GitHub克隆源码看呗~
项目基于.NetStandard 2.0构建,因此支持.NetCore2.0以上以及.NetFramework4.6.1以上。对更低版本不兼容。
虽然原理分析比较冗长,但是代码结构还是非常清晰的,有任何建议,还望不吝赐教,对代码质量的指教非常期待 ^_^
附源码地址: https://github.com/sevenTiny/SevenTiny.Bantina
【版权声明】
本文为七小站主原创作品,转载请注明出处:http://www.cnblogs.com/7tiny/ 且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
作者信息:
QiXiao_柒小(東)
Software Development
北森云计算 Beisen (需要内推可以私我,什么方向都可以哦~)
北京市海淀区上地 Haidian Area Beijing 100089,P.R.China
郵箱Email : dong@qixiao.me , seventiny@foxmail.com
網址Http: http://www.7tiny.com
QQ:1124999434 (专好结交天下英雄好汉,可聊天,可谈技,可约饭,可..嗯,原则是要有的~) 更多联系方式点我哦~https://www.cnblogs.com/7tiny/p/9575230.html