回顾

  上两篇文章主要讲解了我对于数据层的Unit Of Work(工作单元模式)的理解,其中包括了CUD的操作,那么今天就来谈谈R吧,文章包括以下几点:

  • 什么是Query Object
  • 基于SQL的实现

什么是Query Object

  Query Object从语义就能看出它的作用,就是将查询封装成对象,并在内部转换成SQL语句或ORM框架的语法来实现查询操作。

  既然提到了这个模式,肯定是有它的好处的,但是光用语言来说明,可能不好体会,那么就从代码的角度来看看这个模式的好处吧。

  首先,项目数据层接口都会包含根据ID和根据分页获取数据的方法,大致代码如下:

IEnumerable<T> FindAll();
IEnumerable<T> FindById<T>(string id);
IEnumerable<T> FindByPage<T>(int pageIndex, int pageSize);

  然后其他的业务可能还会提供诸如以下几种方法,代码如下:

IEnumerable<School> FindByName(string name);
IEnumerable<School> FindByAge(int age);

  光从以上几个方法就能看出查询的方式多种多样,不统一,这样子在不管在维护还是在代码的重用都会相对比较麻烦。

  但是有了Query Object模式,就可以得出一个统一的接口,代码如下:

IEnumerable<T> FindBy(Query query);
IEnumerable<T> FindBy(Query query, int pageIndex, int pageSize);

  光是接口就从原来的4-5个变成了2个,程序员在使用的时候都是同样的Query Object,而不需要内部是使用SQL还是NHibernate,又或者EF。

  研发人员只需要对相应框架实现对应的Query Object翻译类就可以轻松搞定查询任务了,那么事不宜迟,让我们一起来分析、实现吧。

基于SQL的实现

  首先我们分析一下查询条件,如:select * from school where id = '001',条件包含一个名字、值和条件符号,那么可以通过定义一个条件符号枚举和条件类来存储,代码如下:

public enum CriteriaOperator
{
None,
Eq,
Lt,
Gt
} public class Criterion
{
private string m_PropertyName = null;
public string PropertyName { get { return m_PropertyName; } } private CriteriaOperator m_Operator = CriteriaOperator.None;
public CriteriaOperator Operator
{
get { return m_Operator; }
set { m_Operator = value; }
} private object m_Value = null;
public object Value { get { return m_Value; } } private Criterion(string propertyName, object value, CriteriaOperator criteriaOperator)
{
m_PropertyName = propertyName;
m_Value = value;
m_Operator = criteriaOperator;
} public static Criterion Create(string propertyName, object value, CriteriaOperator criteriaOperator)
{
return new Criterion(propertyName, value, criteriaOperator);
}
}

  接下来需要一个更复杂的条件,如:select * from school where age > 20 and (age < 50 or name = "一中"),查询中包含了子条件,那么需要定义额外的枚举来表示and和or,其次就是查询内部要能支持子查询条件,大致代码如下:

public class Query
{
private List m_Criterions = new List();
public IEnumerable Criterions { get { return m_Criterions; } } private List m_SubQueries = new List();
public IEnumerable SubQueries { get { return m_SubQueries; } } private QueryOperator m_Operator = QueryOperator.And;
public QueryOperator Operator { get { return m_Operator; } } private Query() : this(QueryOperator.And) { } private Query(QueryOperator queryOperator)
{
m_Operator = queryOperator;
} public void Add(Criterion criterion)
{
m_Criterions.Add(criterion);
} public void AddSubQuery(Query subQuery)
{
m_SubQueries.Add(subQuery);
} public static Query Create()
{
return new Query();
} public static Query Create(QueryOperator queryOperator)
{
return new Query(queryOperator);
}
}

  我们从SQL推导出需要实现该功能的类,接下来要从功能类如果翻译成具体的SQL,回头看一下Query、Criterion,其实我们实现的是where后面的SQL语句而已,因此翻译类能完成的功能就是将Query转化为where后面的语句以及包含的参数,大致代码如下:

private static Tuple<string, List> Translate(Query query, int parameterCount)
{
List tempSQLs = new List();
List parameters = new List();
if (query.Criterions.Any())
{
tempSQLs = query.Criterions.Select(c =>
{
string oper = "";
switch (c.Operator)
{
case CriteriaOperator.None:
return null;
case CriteriaOperator.Eq:
oper = "=";
break;
case CriteriaOperator.Lt:
oper = "<";
break;
case CriteriaOperator.Gt:
oper = ">";
break;
}
string parameterName = string.Format("@param{0}", parameterCount + parameters.Count);
parameters.Add(new SqlParameter(parameterName, c.Value));
return string.Format("{0} {1} {2}", c.PropertyName, oper, parameterName);
}).Where(sql => !string.IsNullOrEmpty(sql)).ToList();
parameterCount += parameters.Count;
}
if (query.SubQueries.Any())
{
foreach (var subQuery in query.SubQueries)
{
var subResult = Translate(subQuery, parameterCount);
if (subResult == null)
continue; tempSQLs.Add(string.Format("({0})", subResult.Item1));
parameters.AddRange(subResult.Item2);
parameterCount += subResult.Item2.Count;
}
}
if (tempSQLs.Count == 0)
return null; return new Tuple<string, List>(
string.Join(query.Operator == QueryOperator.And ? " and " : " or ", tempSQLs),
parameters);
}

结尾

  到这里我们就将基于SQL的Query Object模式实现了,不过用起来还是稍微有点复杂的,这时候大家可以用Lambda表达式对以上的代码进行改造,改造成为如下格式:

var query = Query.Create();
query.Add(s => s.Age > 20 && (s.Age < 50 || s.Name == "一中"));

  具体实现的代码,我就不给出了,毕竟了解System.Linq.Expression也是有很多好处的,特别是在项目扩展方面,大家趁此机会锻炼一下自己的编码。

  看到这里相信大家也看到了,对于多表查询的情况,Query Object模式对它的支持也是比较困难的,特别是实现起来,嵌套层数太多,因此当SQL存在多表的情况下,还是得额外进行实现。

  由于时间的问题,今天就到这里了,下一篇文章我会继续Unit Of Work的方式,会基于NHibernate来实现,可能还会加上对于BeGoo的扩展,敬请期待。

  再次提醒各位,以上代码均为示例代码,虽然可以编译通过,但是最好不要直接应用到项目当中,需根据项目具体情况进行重构后使用。

  如果文章有任何错误和问题,欢迎留言,谢谢大家。

Query Object--查询对象模式(上)的更多相关文章

  1. Query Object--查询对象模式(下)

    回顾 上一篇对模式进行了介绍,并基于ADO.NET进行了实现,虽然现在ORM框架越来越流行,但是很多中小型的公司仍然是使用ADO.NET来进行数据库操作的,随着项目的需求不断增加,业务不断变化,ADO ...

  2. 被遗忘的设计模式——空对象模式(Null Object Pattern)

    GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...

  3. 空对象模式(Null Object Pattern)

    空对象模式:用一个空对象来取代null实例的检查,空对象实现一个不做任何动作的关系.(消除如if(Object == null) 这样的检查null实例代码) 例子: public abstract ...

  4. 【设计模式 - 21】之空对象模式(Null Object)

    1      模式简介 在空对象模式中,一个空对象取代NULL对象的实例的检查.NULL对象不是检查空值,而是反映一个不做任何动作的关系.这样的NULL对象也可以在数据不可用的时候提供默认的行为. 在 ...

  5. 设计模式:空对象模式(Null Object Pattern)

    设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...

  6. Hibernate Query Language查询:

    Hibernate Query Language查询: Criteria查询对查询条件进行了面向对象封装,符合编程人员的思维方式,不过HQL(Hibernate Query Language)查询提供 ...

  7. <JVM下篇:性能监控与调优篇>补充:使用OQL语言查询对象信息

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  8. NHibernate系列文章二十五:NHibernate查询之Query Over查询(附程序下载)

    摘要 这一篇文章介绍在NHibernate 3.2里引入的Query Over查询,Query Over查询跟Criteria查询类似.首先创建IQueryOver对象,然后通过调用该对象的API函数 ...

  9. js object(对象)

    http://www.cnblogs.com/pingchuanxin/p/5773326.html Object(对象)是在所有的编程语言中都十分重要的一个概念,对于事物我们可以把他们看作是一个对象 ...

随机推荐

  1. atitit.解决net.sf.json.JSONException There is a cycle in the hierarchy

    atitit.解决net.sf.json.JSONException There is a cycle in the hierarchy 1. 环境:使用hibernate4跟个,,要不个哪的对象系列 ...

  2. attilax.java 注解的本质and 使用最佳实践(3)O7

    attilax.java 注解的本质and 使用最佳实践(3)O7 1. 定义pojo 1 2. 建立注解By eclipse tps 1 3. 注解参数的可支持数据类型: 2 4. 注解处理器 2 ...

  3. XML入门级的简单学习

    xml案例<?xml version="1.0" encoding="ISO-8859-1"?> <note> <to>Ge ...

  4. Jquery对Cookie的操作

    第一步:先引用jQuery的插件jquery-1.9.1.min.js 第二步:引用jquery.cookie.js插件 下对cookie的操作: $.cookie("cookieName& ...

  5. PHP之負載均衡下的session共用

    最近忙於開發台灣運動彩券第四版的程式,所以已經很久沒有上來寫東西了,今天隨便寫點東西和大家分享. 首先說一下負載均衡,相信大家都知道負載均衡可以很好地解決網站大流量的問題,負載均衡就是把用戶的請求分發 ...

  6. Linux: Set OR Change The Library Path

    Linux: Set OR Change The Library Path by NIX CRAFT on APRIL 9, 2010 · 3 COMMENTS· LAST UPDATED AUGUS ...

  7. 传统认知PK网络认知 刚子扯谈烤串认知

    文/刚子 2013.7.23 提到认知,有太多的介绍,我就不在秀理论文字了,那样等于自我抄袭式的传播给大家,对于大家也没意思,可以推荐大家到百度里面搜索下”认知结构”,介绍的比我详细.同行老陈说的! ...

  8. 搞了个基于zookeeper的Leader/Follower切换Demo

    基于zookeeper写了个Leader选举类库demo,场景如下: 上图中的Program1..4可以部署在1台server上,也可以部署在多台server上,也可以是一个进程中的多个线程. 运行效 ...

  9. java Joda-Time 对日期、时间操作

    任何企业应用程序都需要处理时间问题.应用程序需要知道当前的时间点和下一个时间点,有时它们还必须计算这两个时间点之间的路径.使用 JDK 完成这项任务将非常痛苦和繁琐.现在来看看 Joda Time,一 ...

  10. CountDownLatch线程阻塞用法实例

    在编写多线程的工作中,有个常见的问题:主线程(main) 启动好几个子线程(task)来完成并发任务,主线程要等待所有的子线程完成之后才继续执行main的其它任务. 默认主线程退出时其它子线程不会停, ...