当前位置:
  1. 魔豆IT网
  2. 系统教程
  3. SQL
  4. 正文

如果用数据库做持久化设备的话直接写sql(存储过程)还是用ORM呢?

无疑ORM的出现给了我们一个全新的看待数据操作的视角:在面向对象的语言中,用面向对象的方法访问,操作数据

但是在使用ORM一段时间后,总是会觉得有点别扭的地方,因为关系型数据库对数据的抽象方式是基于关系代数的层次结构,而对象化的数据抽象方式是网状结构,ORM在这两种抽象模式之间映射就会产生Impedance Mismatch(阻抗不匹配)的现象,所以在映射不当的时候就会感觉非常的别扭。

那么我再想,如果抽象数据本身存在阻抗不匹配的问题,那么如果我们抽象对数据的访问呢?无论如何ORM,对关系数据库的访问仍然是通过SQL语句作为访问的界面,那么如果我们抽象SQL语句是否就能摆脱阻抗失调,能够流畅的访问操作数据了呢?我在上一篇文章发布的一个Python的DAL里做了一个小小的尝试,当然我并不是第一个这么做的人,不过我想把这种方法总结出来,也许能够在ORM以外提供一个更新的视角,能够产生出更加轻量化的数据访问组件,也是不错的。用Python实现是因为暂时我还只想到了在动态语言下如何实现,如果需要在.NET下使用,可以用IronPython。如果有同学想到了在C#下如何实现也希望能够拿出来和大家分享一下。

为什么要用动态语言来实现,首先就是不要实体类,因为我们抽象的是访问数据的方式而不是数据本身,在关系型数据库里数据就是数据行,字段的形式,在数据结构上一行数据用字典就能够表示了,而在python下我们可以把字典用下面的形式来表现:

class Row(dict):

def __getattr__(self,propertyName):

if self.has_key(propertyName):

return self[propertyName]

return None

这样一个Row的对象我们就可以通过列明对应属性来访问其中的键值,就跟一个实体类一样,这样我们就解决了数据抽象的问题。

接下来是重点,我们要抽象SQL,首先要分析SQL的结构,我们对数据的操作有 增、删、改、查四类,分别通过:

INSERT、DELETE、UPDATE、SELECT四种SQL语句来实现,每一种SQL语句又由子句构成,四种SQL语句的子句中有各自独有到,也有共有的子句,比如WHERE子句,SELECT、UPDATE、DELETE 这三种SQL操作都有WHERE子句,且语义相同,均表示待操作的数据筛选到条件,所以我将筛选条件抽象出来定义了conds这个类。

class conds:

def __init__(self,field):

self.field_name=field

self._sql=""

self._params=[]

self._has_value=False

self._sub_conds=[]

self._no_value=False

这个类用来将语言的关系运算逻辑映射到SQL语句上,所幸的是Python支持操作符重载,所以我们只需要在conds类中加入相应的buildin方法就行了。比如:

def __eq__(self,value):

return self._prepare("".join(["`",self.field_name,"`=%s"]),value)

def _prepare(self,sql,value):

if not self._has_value:

self._sql=sql

self._params.append(value)

self._has_value=True

return self

raise OperationalError,"Multiple Operate conditions"

上面代码就定义了如果 conds('col1')==5 就会映射到sql col1=%s 且增加一个参数,值为5。

同理将其他关系操作符的方法都映射到相应的buildin方法中,两个关系间是and和or,我们用__and__和__or__这两个方法来映射,还有Like找不到相应的操作符,就用like方法来表示这个关系,条件操作的结果还是条件,所以要用对象本身作为返回值。这样 (conds('col1')==5)(conds('col2')<6) 就映射到了条件 col1=%s and col2<%s这两个条件的并集。由于这么写还需要用字符串来表示列名不方便,所以我们定义一个table类来获取conds:

class TableQueryer:

'''

Support for single table simple querys

'''

def __init__(self,db,tablename):

self.tablename=tablename

self.db=db

def __call__(self,query=None):

return Operater(self.db,self.tablename,query)

def __getattr__(self,field_name):

return conds(field_name)

同理我们在数据连接类上加入getattr,

def __getattr__(self,tablename):

'''

return single table queryer for select table

'''

return TableQueryer(self,tablename)

然后我们就能够通过db.tablename.colname来获取条件对象了,之前的例子就能改成

(db.tablename.col1==5)(db.tablename.col2<6)

如果预先 tb=db.tablename 的话还可以改写成

(tb.col1==t)(tb.col2<6)

由于插入数据只需要数据表名不需要查询为基础,所以直接在TableQueryer中加入insert方法

所以插入数据就只需要db.tablename.insert(col1=value1,col2=value2...)

数据的查询、更新、删除都是基于查询条件的,所以抽象出一个操作对象Operate出来:

class Operater:

def __init__(self,db,tablename,query):

self.count=Count(db,tablename,query)

self.select=Select(db,tablename,query)

self.update=Update(db,tablename,query)

self.delete=Delete(db,tablename,query)

每一个操作抽象一个类出来,并且把查询条件保存到了每一个操作的条件里,最终的Sql生成,以及特定子句的加入,都在具体的类比如Select里完成。

我在这里为了节约一个方法名的层次出来就把TableQueryer做成了可以调用的对象,加入了__call__方法。将查询条件传入__call__方法就返回了一个Operator对象,于是通过

tb=db.tablename

q=tb((tb.col1==5)(tb.col2<6))

q.select()就能查询出结果,等价sql为 select * from tablename where col1=5 and col2<6

在得到q后,直接调用q.delete()就能把这些数据删掉

q.update(tb.col1==6)就能更新筛选出的数据

q.delete()就能直接删掉符合筛选条件的数据

项目放在 http://bitbucket.org/alexander_lee/flunt-sql-data-access-layer

相关阅读

  • 暂无推荐

《》由网友“”推荐。

转载请注明: