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作为了 ...
随机推荐
- highchart 设置双Y轴坐标 双x轴坐标方法
我们的图表一旦引入了两种不同单位或者数量级相差很大的数据以后,这时候需要两种坐标对其进行计量. 下面以设置双Y轴为例, y轴坐标的参数设置成: yAxis: [{ title: { text: '坐标 ...
- 减小Chrome的内存占用的参数
步骤: 1.右击chrome浏览器快捷方式 属性 目标 后面添加 --purge-memory-button 2. 按 shift+esc 查看进程
- DataGridView列排序混乱的处理方法
在C#程序开发中DataGridView可以说是使用最多的数据呈现控件了,但是在使用的过程中我们会发现当绑定的数据源有较多数据列的时候,DataGridView上显示的列的顺序就会出现混乱的现象. 那 ...
- Django基础——Form&Ajax篇
一 Form 在实际的生产环境中,登录和注册时用户提交的数据浏览器端都会使用JavaScript来进行验证(比如验证输入是否为空以及输入是否合法),但是浏览器可能会禁用JavaScirpt,同时也有人 ...
- XXXXXXXX系统失败总结
2013年底因同事离职接手了他负责的<XXXXXX病例系统>,当时由3个刚毕业的同事在做,框架使用Spring+Hibernate+SpringMVC+EasyUI,因为当时的主力开发人员 ...
- android: Android Notification
Notification即通知,用于在通知栏显示提示信息. 在较新的版本中(API level > 11),Notification类中的一些方法被Android声明deprecated(弃用 ...
- C#Winform程序如何发布并自动升级(图解)
C#Winform程序如何发布并自动升级(图解) 有不少朋友问到C#Winform程序怎么样配置升级,怎么样打包,怎么样发布的,在这里我解释一下打包和发布 关于打包的大家可以看我的文章C# w ...
- Naked Search in service
public List<TplRelease> searchTplReleaseById(TplRelease tr)throws Exception{ DBOperator dbo = ...
- 源代码目录结构--AngularJS学习笔记(一)
最近开始接触AngularJS,确实是一个相当不错的东西,可以把很多东西简化掉.又对于其中的双向绑定等的实现很好奇,加之正在学习Javascript的东西,所以觉得从源代码这块开始深入学习Angula ...
- [JS Compose] 0. Understand 'Box' or 'Container', they are just like Array!
We'll examine how to unnest function calls, capture assignment, and create a linear data flow with a ...