Query Object--查询对象模式(下)
回顾
上一篇对模式进行了介绍,并基于ADO.NET进行了实现,虽然现在ORM框架越来越流行,但是很多中小型的公司仍然是使用ADO.NET来进行数据库操作的,随着项目的需求不断增加,业务不断变化,ADO.NET的实现方式,会使原先简单的单表操作变得尤为复杂,特别是数据库表发生改变的情况下,无法像ORM框架那样,通过修改映射来达到统一的修改,需要靠程序员检查每一段相关的SQL来排查错误,这是非常麻烦的。
不管什么样的框架,使用起来不简单不易用的话,那么就没有设计的必要了。
因此今次的文章将会基于ORM框架来进行实现,大致内容如下:
- 基于表达式的实现
- 使用NHibernate查询
- 使用BeeGo查询
基于表达式的实现
因此在如今在C#领域内,如果没有Linq风格,对于编码人员来说就显得有些复杂了,因此扩展方向上肯定是要支持Linq。上一篇文章并没有实现对Linq的支持,而是留给大家去实现了,因此文章开头,就先复习一下吧。
首先从简单的调用方式开始吧,如:
query.Add<School>(s => s.Name == "一中");
或者
query.Add<School>(s => s.Age > 20);
分析以上两个查询条件表达式,并且跟原先的Criterion来进行对比,依然是可以通过解析表达式来生成Criterion对象的,但是由于NHibernate已经支持表达式了,因此需要重构一下Query Object模式,首先删除Criterion类,并对Query代码进行修改,代码如下:
private List<Expression> m_Criterions = new List<Expression>();
public IEnumerable<Expression> Criterions { get { return m_Expressions; } } public void Add<T>(Expression<Func<T, bool>> exp)
{
Add(exp as Expression);
} public void Add(Expression exp)
{
if (exp.NodeType == ExpressionType.Lambda)
Add((exp as LambdaExpression).Body);
else
m_Criterions.Add(exp);
}
接下来,需要支持and或者or了,由于and和or涉及到子查询的问题,当父查询的QueryOperator与子查询的QueryOperator不同的情况下,表达式就需要被转换成一个子查询了,因此代码改为:
public void Add(Expression exp)
{
if (exp.NodeType == ExpressionType.Lambda)
Add((exp as LambdaExpression).Body);
else if (exp.NodeType == ExpressionType.OrElse || exp.NodeType == ExpressionType.AndAlso)
AddByJunctionExpression(exp);
else
m_Expressions.Add(exp);
} private void AddByJunctionExpression(Expression exp)
{
var binaryExp = exp as BinaryExpression;
if ((Operator == QueryOperator.And && exp.NodeType == ExpressionType.AndAlso) ||
(Operator == QueryOperator.Or && exp.NodeType == ExpressionType.OrElse))
{
Add(binaryExp.Left);
Add(binaryExp.Right);
}
else
{
Query subQuery = new Query(exp.NodeType == ExpressionType.OrElse ? QueryOperator.Or : QueryOperator.And);
subQuery.Add(binaryExp.Left);
subQuery.Add(binaryExp.Right);
AddSubQuery(subQuery);
}
}
到这里基于表达式的Query Object就改造完成了,那么接下来就要根据数据层具体的环境来讲Query转化为对应的API来查询数据了。
使用NHibernate查询
先上代码,然后分析,大致代码为:
public static NHibernate.ICriterion ToNHibernateQuery<T>(Query query)
{
NHibernate.Junction junction;
if (query.Operator == QueryOperator.And)
junction = NHibernate.Expression.Conjunction();
else
junction = NHibernate.Expression.Disjunction(); if (expressions.Any())
{
foreach (var exp in expressions)
AppendNHibernateCriterionByExpression(junction, exp);
} this.AppendNHibernateCriterionBySubQueries(junction);
return junction;
} private static void AppendNHibernateCriterionByExpression<T>(NHibernate.Junction junction, Expression exp)
{
var binaryExp = exp as BinaryExpression;
var expression = Expression.Lambda<Func<T, bool>>(
exp,
GetParameterExpressionBy(binaryExp.Left) ?? GetParameterExpressionBy(binaryExp.Right));
junction.Add(expression);
} private static ParameterExpression GetParameterExpressionBy(Expression exp)
{
if (exp.NodeType != ExpressionType.MemberAccess)
return null; var memberExp = exp as MemberExpression;
return memberExp.Expression as ParameterExpression;
} private static void AppendNHibernateCriterionBySubQueries<T>(NHibernate.Junction junction, IEnumerable<Query> subQueries)
{
if (!subQueries.Any())
return; foreach (var subQuery in subQueries)
{
var subCriterion = ToNHibernateQuery<T>(subQuery);
junction.Add(subCriterion);
}
}
由于NHibernate内部已经实现了Query Object模式,因此在转换的过程当中,只需要将And和OR条件转化为对应的NHibernate类就行了,然后利用NHibernate对于表达式的支持将条件添加进去,最后使用ICriteria.List<T>()获取结果就可以了。
上一篇文章有提到对于Query Object模式对于外联的支持是比较麻烦的,但是在NHibernate的基础下去实现是比较简单的,这里就不再做过多的介绍了,有意向的朋友要对NHbernate做深入的研究,这里推荐大家看看李永京的文章学习一下,或者买基本NHibernate的书学习。
使用BeeGo查询
GO语言出来也有一段时间了,很多大公司也都在使用它,我们也不能落后。由于最近使用BeeGo框架搭建了数据服务器,Query Object模式当然也是必须的,因此借着这篇文章,也顺带讲讲BeeGo框架上,Query Object模式的实现。
虽然现在大部分的公司使用的数据库依然是关系型数据库,但是仍然挡不住NoSQL这个新兴数据库的脚步,由于最近一段时间都是是用Nodejs来进行web开发的,因此使用NOSQL数据库能更好的进行数据库交互,毕竟可以直接使用JSON数据结构,但是由于同事对于关系型数据库比较习惯因此不得不改为关系型数据库,于是就有了上面提到的GO数据服务器了。
为了使原先查询数据的方式不用改变,因此引用了MongoDb的API结构来实现Query Object模式,ajax查询代码为:
$.ajax({
url: '/school/find',
data: {
$or: {
name: '一中',
age: {
$gt: 20
}
}
}
});
以上结构等同于select * from school where name = '一中' or age > 20,按照以前几次实现Query Object的经验,这次要实现这种结构的转换相对还是比较简单的,在Go语言当中,通用类型是interface{},它相当于C#的Object,字典也有一些差别,但是并不妨碍具体的实现,这里为了简便(使用Go的结构太复杂了),因此直接使用switch来转换成begoo的接口的,大致代码如下:
func (this *Repository) translateToCriterion(field string, criterion map[string]interface{}, isAnd bool, isNot bool) *orm.Condition {
cond := orm.NewCondition()
var value interface{}
for k, v := range criterion {
key := field
value = v
switch k {
case "$take":
continue
case "$skip":
continue
case "$sort":
continue
case "$like":
startsWith, endsWith := strings.HasSuffix(v.(string), "%"), strings.HasPrefix(v.(string), "%")
chars := []byte(v.(string))
if startsWith && endsWith {
key = key + "__icontains";
value = string(chars[1:len(chars)-1])
} else if startsWith {
key = key + "__istartswith"
value = string(chars[:len(chars)-1])
} else {
key = key + "__iendswith"
value = string(chars[1:])
}
break
case "$gt":
key = key + "__gt"
break
case "$lt":
key = key + "__lt"
break
case "$in":
key = key + "__in"
break
case "$not":
if reflect.TypeOf(v).Kind() == reflect.Map {
value = this.translateToCriterion(field, v.(map[string]interface{}), isAnd, true)
} else {
isNot = true
}
break
case "$and":
value = this.translateToCriterion("", v.(map[string]interface{}), true, isNot)
break
case "$or":
value = this.translateToCriterion("", v.(map[string]interface{}), false, isNot)
break
default:
if v != nil && reflect.TypeOf(v).Kind() == reflect.Map {
value = this.translateToCriterion(k, v.(map[string]interface{}), isAnd, isNot)
} else if v == nil {
key = k + "__isnull"
value = true
} else {
key = k
}
break
}
subCond, isCond := value.(*orm.Condition)
if isAnd {
if isCond {
cond = cond.AndCond(subCond)
} else if isNot {
cond = cond.AndNot(key, value)
} else {
cond = cond.And(key, value)
}
} else {
if isCond {
cond = cond.OrCond(subCond)
} else if isNot {
cond = cond.OrNot(key, value)
} else {
cond = cond.Or(key, value)
}
}
}
return cond
}
这样就实现了BeeGo框架下的Query Object模式了,如果对GO有兴趣的话,也可以直接使用BeeGo框架来搭建Web应用,那么前端可以直接使用前面那样的ajax来实现数据的访问,这样还是很方便的。
结尾
Query Obejct模式虽然可以简化数据的查询,虽然对于数据权限是没有作用的,但是由于使用Query Object模式,查询接口是固定的,因此可以在查询方法内添加数据权限模块,这样可以简化数据权限实现的困难。
那么这次的文章就到这里了,如有疑问和错误请大家留言给我,谢谢。
Query Object--查询对象模式(下)的更多相关文章
- Query Object--查询对象模式(上)
回顾 上两篇文章主要讲解了我对于数据层的Unit Of Work(工作单元模式)的理解,其中包括了CUD的操作,那么今天就来谈谈R吧,文章包括以下几点: 什么是Query Object 基于SQL的实 ...
- 【 PostgreSQL】查询某模式下所有表的分布键信息
想看下某模式下所有表创建的分布键是否合理,查找系统表文档拼出如下sql,亲们如果有更好的sql或者意见欢迎留言! SELECT aaa.nspname AS "模式名", ...
- 【gp数据库】查询系统表看模式下所有表的分布键信息
Greenplum是关系型的分布式数据库,需要存储的数据库在进入数据库时,将先进行数据分布的处理工作,讲一个表的数据平均分不到每个节点上,并为每个表指定一个分发列(distribute Column) ...
- 被遗忘的设计模式——空对象模式(Null Object Pattern)
GoF(四人帮)那本<设计模式 可复用面向对象软件的基础>可谓是设计模式方面的经典之作,其中介绍的23种设计模式, 也可谓是经典中的经典.但是,设计模式的种类绝不仅仅是这23种,除此之外还 ...
- ElementUI Tree控件在懒加载模式下的重新加载和模糊查询
之所以使用懒加载是为了提高性能,而且只有在懒加载模式下默认会给所有显示节点设置展开按钮.leaf也可以做到,但是要操作数据比较麻烦. 要实现懒加载模式下的模糊查询以及重新加载必须要使用data与laz ...
- “Options模式”下的配置是如何绑定为Options对象
“Options模式”下的配置是如何绑定为Options对象 配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置.值得推荐的做法就是 ...
- 设计模式:空对象模式(Null Object Pattern)
设计模式:空对象模式(Null Object Pattern) 背景 群里聊到<ASP.NET设计模式>,这本书里有一个“Null Object Pattern”,大家就闲聊了一下这个模式 ...
- 空对象模式(Null Object Pattern)
空对象模式:用一个空对象来取代null实例的检查,空对象实现一个不做任何动作的关系.(消除如if(Object == null) 这样的检查null实例代码) 例子: public abstract ...
- JS函数的参数对象arguments在严格模式下的限制
在JS中,传入的函数的参数个数可以与定义函数的个数不一致,那么对于传入的实参的引用,则是arguments对象.然而改对象在严格模式和非严格模式下是由区分的: 1 在严格模式下arguments作为了 ...
随机推荐
- autocomplete实现联想输入,自动补全
jQuery.AutoComplete是一个基于jQuery的自动补全插件.借助于jQuery优秀的跨浏览器特性,可以兼容Chrome/IE/Firefox/Opera/Safari等多种浏览器. 特 ...
- Linux安装snmp
1.yum安装 yum -y install net-snmp* 2.修改配置文件/etc/snmp/snmpd.conf com2sec notConfigUser default public 默 ...
- paip.编程语言到底有没有优劣之分优秀之分
paip.编程语言到底有没有优劣之分优秀之分 人有没有优秀之分之分呢??狗有没有优秀之分呢?? 当然是有的,有好人坏人的说法,或者精英平民的区分..狗也有好狗狗,坏狗,疯狗嘛.. 所以,自然,编程语言 ...
- 一些java面试题
1.SQL中的having子句用来干啥的? 答:having子句的功能是进行分组(GROUP BY)后的过滤 如:select id,avg(score) from 表 group by 学号 ha ...
- 每天一个linux命令(5):rm 命令
昨天学习了创建文件和目录的命令mkdir ,今天学习一下linux中删除文件和目录的命令: rm命令.rm是常用的命令,该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所 ...
- mac下mysql数据库的配置
这里记录一下. 之前在mac下使用brew install mysql安装,但是安装完成后发现密码不好修改,上网搜了下发现mac下使用命令行安装mysql确实存在很多问题,这一点确实远不如Ubuntu ...
- Leetcode 130 Surrounded Regions DFS
将内部的O点变成X input X X X XX O O X X X O XX O X X output X X X XX X X XX X X XX O X X DFS的基本框架是 void dfs ...
- VS2013中Python学习笔记[Django Web的第一个网页]
前言 前面我简单介绍了Python的Hello World.看到有人问我搞搞Python的Web,一时兴起,就来试试看. 第一篇 VS2013中Python学习笔记[环境搭建] 简单介绍Python环 ...
- SQL Server查看所有表大小、表行数和占用空间信息
一.查看表名和对应的数据行数select a.name as '表名',b.rows as '表数据行数'from sysobjects a inner join sysindexes bon a. ...
- Android高仿微信图片选择功能的PhotoPicker
类似于微信修改头像的功能基本上每个app都会有,以前公司开发的项目就有修改头像的功能,但是用的Android系统自带的图片选择器.用Android系统的图片选择器有个好处就是稳定,不会有什么问题.但也 ...