本文介绍如何使用 Rafy 框架中的 Sql Tree 查询:

除了开发者常用的 Linq 查询,Rafy 框架还提供了 Sql 语法树的方式来进行查询。

这种查询方式下,开发者不需要直接编写真正的 Sql 语句,而是转而使用一套中间 Sql 语法树对象。这隔离了与具体数据库的耦合,使得开发者编写的查询可以跨越多种不同的数据库运行,甚至可以在非关系型数据库中运行。同时,框架还结合托管属性,提供了方便开发者使用的 API,并尽量保持与传统 Sql 相近的语法,使得开发者可以快速理解并编写。

本文包含以下章节:

  • 快速示例
  • 使用场景
  • 代码段
  • 更多示例

快速示例


SqlTree 查询是直接以一种类似于 Sql 语法的格式,并结合实体托管属性 IManagedProperty 来进行查询的查询模式。如下:

[RepositoryQuery]
public virtual ChapterList GetBy(string name, PagingInfo pi)
{var f = QueryFactory.Instance;var t = f.Table<Chapter>();var q = f.Query(selection: f.SelectAll(),//查询所有列from: t,//要查询的实体的表where: t.Column(Chapter.NameProperty).Contains(name)//where 条件,orderBy: new List<IOrderBy> {//排序f.OrderBy(source.Column(Chapter.NameProperty), OrderDirection.Ascending)});return (ChapterList)this.QueryData(q, pi);
}

可以看到,SqlTree 语法非常简单:

  • 通过 QueryFactory.Instance 类型的单例对象来定义整个 SqlTree 查询对象。
  • 查询中使用的是实体类型(Chapter)和实体的托管属性(Chapter.NameProperty)来定义表和字段。

更多的查询语法示例,见本节后面的更多示例。

 

使用场景


当您处于以下场景时,需要使用 SqlTree 查询:

  1. Linq 查询无法支持的一些场景。
    Linq 查询目前只支持有限的一些操作符的解析,以及不太复杂的关系的分析。所以当您的查询较为复杂,已经无法使用 Linq 查询来实现时,可以考虑使用 SqlTree 查询。
  2. 需要更精确地控制 Sql 语句。
    如果想要更加精确地控制最终生成的 Sql 语句,也需要使用 SqlTree。
    例如,Linq 查询中需要两个实体有确切的实体关系才会最终生成 Join 语句;但是 SqlTree 则与 Sql 语句无异,开发者可以随意将两个实体对应的表进行 Join 操作。
  3. 需要更好的性能。
    SqlTree 查询是 Rafy 框架查询数据(表格、实体)的核心实现。在框架底层,Linq 查询也都是完全是基于 SqlTree 查询来实现的。当开发者在使用 Linq 查询时,编译器其实是生成一组对象来表示一棵表达式树,而 Rafy 框架会解析这棵树,生成更加底层的 SqlTree 对象,才交给执行引擎去生成真正的 Sql 语句并最终执行。所以,直接使用 SqlTree 则节约了表达式树的生成(大量反射与对象)与解析的性能消耗。
    同样,Rafy 没有象 Hibernate 框架定义一套新的基于字符串的查询语法(如 hql),也是因为开发者编写 hql,不但无法得到编译时的语法支持,而且性能上也需要消耗对 hql 进行解析并生成 SqlTree,不如直接使用更直接的 SqlTree。
    当然,Rafy 在 SqlTree 的基础上再推出 Linq 查询的原因,是因为 SqlTree 本身需要一定的学习周期才能使用,而开发者则更熟悉使用 Linq 语法进行查询,基本可以认为是上手即用,所以支持 Linq 查询可以简化大部分的简单开发场景。
  4. 希望编写更通用的查询。
    仓库基类 EntityRepository 中自带的 GetAll、GetById 等方法,都是面向所有实体类型的非常通用的查询。对于基于 Rafy 的上层框架的开发者而言,除了直接使用这些自带的通用查询,很多时候是需要自行编写一些类似的通用查询的。
    Linq 的 Labmda 语法中的属性表达式(e.Name)需要绑定具体的实体类型(Book e),这导致了必须使用反射去生成表达式树,才能编写通条蚁。但是,SqlTree 的语法是基于托管属性框架的,它不需要使用确切的实体属性表达式,只需要使用托管属性的运行时对象 IManagedProperty 即可(Book.NameProperty)。这使得开发人员可以更加方便地编写通用查询。例如,仓库基类 EntityRepository 中的所有查询方法,都是直接通过使用实体的托管属性来实现的,例如:GetById、GetByParentId、GetAll 等。
  5. 可以为扩展属性编写查询。
    由于扩展属性写在额外的程序集插件中的,所以当无法通过 Linq 表达式进行查询。这时就不得不通过托管属性 IManagedProperty 来定义 SqlTree 完成查询了。
    关于扩展属性,参见:扩展属性。
  6. 支持多个数据库。
    上述的场景中,其实还可以直接编写 Sql 语句来进行查询。但是这样就很难保证开发者编写的 Sql 语句能够在多个数据库上能够正确运行。
  7. 查询需要支持仓库数据层的扩展点。
    由于 Rafy 的查询核心都是基于 SqlTree 来实现的,所以内部的所有扩展点都是要依赖 SqlTree的。如果开发者直接编写 Sql 语句来查询,那么这些许多的扩展点都将无效,无法对开发者编写的这条 Sql 语句进行扩展。
    例如:当使用 幽灵插件 对所有幽灵数据进行自动过滤时,如果开发者使用手工编写的 Sql 语法进行查询,那么自动过滤功能无效,需要开发者自己进行幽灵数据的过滤。

代码段


RafySDK 中提供了两个代码段,来辅助开发者生成基本的 SqlTree 查询结构:Rafy_Query、Rafy_Query_TableQueryContent。

详情见:代码段。

更多示例


下面将会列出一些常见的 SqlTree 查询示例。通过这些代码,您将学习到如何在各种查询需求下使用 SqlTree。

基础查询:

[RepositoryQuery]
public virtual ChapterList GetBy(string name, PagingInfo pi)
{var f = QueryFactory.Instance;var t = f.Table<Chapter>();var q = f.Query(//selection: f.SelectAll(),//没有 selection,则默认表示查询所有列from: t,//要查询的实体的表where: t.Column(Chapter.NameProperty).Contains(name)//where 条件);return (ChapterList)this.QueryData(q, pi);
}

表格数据查询:

[RepositoryQuery]
public virtual LiteDataTable GetBy(string name, PagingInfo pi)
{var f = QueryFactory.Instance;var t = f.Table<Chapter>();var q = f.Query(from: t,where: t.Column(Chapter.NameProperty).Contains(name));return this.QueryTable(q, pi);//由查询实体变为查询数据表格,只是更换了这一行代码。
}

两个列的条件进行比较:
var table = f.Table(this);//使用当前的仓库来表示当前的表
var q = f.Query(from :table,where: table.Column(Chapter.NameProperty).Equal(table.Column(Chapter.CodeProperty))//两个列相等
);

使用 And、Or:
var table = f.Table(this);
var q = f.Query(from :table,where: f.And(table.Column(Chapter.NameProperty).Equal(name),f.Or(table.Column(Chapter.IdProperty).LessEqual(10),table.Column(Chapter.IdProperty).GreaterEqual(1000)))
);

Join(SerialNumberValueRepository 中的真实代码):

/// <summary>
/// 获取某个规则下最新的一个值。
/// </summary>
/// <param name="autoCodeName"></param>
/// <returns></returns>
[RepositoryQuery]
public virtual SerialNumberValue GetLastValue(string autoCodeName)
{var f = QueryFactory.Instance;var t = f.Table<SerialNumberValue>();var t2 = f.Table<SerialNumberInfo>();var q = f.Query(from: t.Join(t2),//由于 SerialNumberValue 有一个 SerialNumberInfo 的引用属性,则在使用 Join 时,不需要给出 Join 的条件。where: t2.Column(SerialNumberInfo.NameProperty).Equal(autoCodeName),orderBy: new List<IOrderBy> { f.OrderBy(t.Column(SerialNumberValue.LastUpdatedTimeProperty), OrderDirection.Descending) });return (SerialNumberValue)this.QueryData(q);
}

使用完整的 Join:

var t = f.Table<SerialNumberValue>();
var t2 = f.Table<SerialNumberInfo>();
var q = f.Query(from: t.Join(t2, t.Column(SerialNumberValue.SerialNumberInfoIdProperty).Equal(t2.Column(SerialNumberInfo.IdProperty)), JoinType.Inner),//不但可以给出具体的 Join 条件,还可以给出 Join 类型。where: t2.Column(SerialNumberInfo.NameProperty).Equal(autoCodeName),orderBy: new List<IOrderBy> { f.OrderBy(t.Column(SerialNumberValue.LastUpdatedTimeProperty), OrderDirection.Descending) }
);

Exists:

var bookTable = f.Table(this);
var chapterTable = f.Table<Chapter>();
var q = f.Query(from: bookTable,where: f.Exists(f.Query(from: chapterTable,where: chapterTable.Column(Chapter.BookIdProperty).Equal(bookTable.IdColumn)))
);

Not Exists:

var book = f.Table(this);
var chapter = f.Table<Chapter>();
var q = f.Query(from: book,where: f.Not(f.Exists(f.Query(from: chapter,where: f.And(f.Constraint(chapter.Column(Chapter.BookIdProperty), book.IdColumn),f.Constraint(chapter.Column(Chapter.NameProperty), PropertyOperator.NotEqual, chapterName)))))
);

更多示例,请参照源码中单元测试的 ORMTest 中的 TableQuery 相关方法。

PS:该文已经纳入《 Rafy 用户手册》中。

转载于:https://www.cnblogs.com/zgynhqf/p/5449775.html

Rafy 框架 - 使用 SqlTree 查询相关推荐

  1. Rafy 框架 - 幽灵插件(假删除)

    Rafy 框架又添新成员:幽灵插件.本文将解释该插件的场景.使用方法.原理. 场景 在开发各类数据库应用系统时,往往需要在删除数据时不是真正地删除数据,而只是把数据标识为'已删除'状态.这些数据在业务 ...

  2. TP框架中field查询字段

    TP框架中field查询字段 不是fields 也不是files !!!! 不是fields 也不是files !!!! 不是fields 也不是files !!!! 不是fields 也不是file ...

  3. 使用cglib实现数据库框架的级联查询

    写在前面的 这一章是之前写的 <手把手教你写一个Java的orm框架> 的追加内容.因为之前写的数据库框架不支持级联查询这个操作,对于有关联关系的表用起来还是比较麻烦,于是就准备把这个功能 ...

  4. java里ssm框架分页代码_SSM框架实现分页查询例子

    [实例简介] SSM框架实现分页查询例子,对应博客地址https://blog.csdn.net/sinat_37001576 [实例截图] [核心代码] SSM-Project └── SSM-Pr ...

  5. SSM框架实现用户查询、注册、登录——IDEA整合Spring、Spring MVC、Mybatis 框架

    目录 零.前言 一.说明 1.整合说明 2.最终目标 3.数据库准备 二.搭建整合环境 1.创建 maven 工程 2.导入依赖坐标 3.创建java和resources文件夹 4.创建类和接口文件 ...

  6. [LINQ2Dapper]最完整Dapper To Linq框架(二)---动态化查询

    目录 [LINQ2Dapper]最完整Dapper To Linq框架(一)---基础查询 [LINQ2Dapper]最完整Dapper To Linq框架(二)---动态化查询 [LINQ2Dapp ...

  7. yii mysql join_Yii框架连表查询操作示例

    本文实例讲述了Yii框架连表查询操作.分享给大家供大家参考,具体如下: Join //表连接 //查询出学生.班级.校区.记录表的所有数据 $data=Jf_record::find() ->j ...

  8. Rafy 框架 - 流水号插件

    Rafy 框架又添新成员:流水号插件.本文将解释 Rafy 框架中的流水插件的场景.使用方法. 场景 在开发各类数据库应用系统时,往往需要生成从一开始的流水号,有时还需要按月或者按日进行独立生成,如下 ...

  9. php yii框架addselect,PHP Yii框架之数据库查询操作总结

    Yii容易学习和使用.你只需要知道PHP和面向对象编程,便可以很快上手,而不必事先去学习一种新的架构或者模板语言.用Yii的开发速度非常之快,除框架本身之外,需要为应用所写的编码极少.事实上这是最高效 ...

最新文章

  1. matlab图片插值数据_使用双线性插值法放大图像(matlab实现)
  2. PHPStorm+XDebug进行调试
  3. python循环输出00-59
  4. js前台编码,asp.net后台解码 防止前台传值到后台为乱码
  5. Python实训day06am【网络爬虫(爬取接口)】
  6. 普元云计算-云计算平台项目团队组织架构与缘起
  7. Excel如何利用条件格式找出数据区域中最大的几项
  8. Android Studio1.4.x JNI开发基础-基本环境配置
  9. 微信小程序后台管理系统(后端)笔记
  10. 毕设题目:Matlab水果识别分级
  11. 备考通信复试过程中的一些知识点总结梳理——信息论基础知识
  12. itools3.0服务器维护,APP Store失联?iTools3.0告诉你如何解决
  13. OpenLayers 6 代码绘制/draw交互组件绘制两种方式绘制椭圆过程详解
  14. 每日一佳——Structure Preserving Embedding(Blake Shaw et al. ,ICML,2009)
  15. 安卓10开机时间优化分析
  16. 小米随身wif linux平台配置
  17. HART475艾默生手操器维修注意事项及指标特点
  18. 光照强度传感器BH1750
  19. Java泛型方法与普通成员方法以及案例说明(五)
  20. 百度普通收录API提交报错:site init fail

热门文章

  1. 厦门信息集团与EMC战略合作共建智慧厦门
  2. Exchange 接收连接器(Client、Default)区别
  3. 编程之美2——N!的二进制表示中最低位1的位置
  4. android 中文语音
  5. VMware中CentOS设置静态IP
  6. android 接收SDCcard插拔的广播
  7. 亲身试验 pycharm 下载 并 安装 pygame包
  8. Js中Currying的应用
  9. async/await
  10. android 画中画模式自定义,Android 8.0 Oreo 画中画模式