文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>通过抽象Sql语句来构建流畅的数据访问

通过抽象Sql语句来构建流畅的数据访问

时间:2010-08-25  来源:亚历山大同志

如果用数据库做持久化设备的话直接写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 有兴趣的TX一起研究研究





相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载