Sql样的数据访问

lee Alexander <[email protected]>
sender-time     Sent at 11:49 (GMT+08:00). Current time there: 2:07 PM. ✆
reply-to        [email protected]
to      [email protected]
date    Thu, Aug 26, 2010 at 11:49
subject [CPyUG] 通过抽象Sql语句来构建流畅的数据访问

立意

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

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

破题

   1 class Row(dict):
   2     def __getattr__(self,propertyName):
   3         if self.has_key(propertyName):
   4             return self[propertyName]
   5         return None

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

   1 class conds:
   2     def __init__(self,field):
   3         self.field_name=field
   4         self._sql=""
   5         self._params=[]
   6         self._has_value=False
   7         self._sub_conds=[]
   8         self._no_value=False

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

   1 def __eq__(self,value):
   2     return self._prepare("".join(["`",self.field_name,"`=%s"]),value)
   3 
   4 def _prepare(self,sql,value):
   5     if not self._has_value:
   6         self._sql=sql
   7         self._params.append(value)
   8         self._has_value=True
   9         return self
  10     raise OperationalError,"Multiple Operate conditions"

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

   1 class TableQueryer:
   2     '''
   3     Support for single table simple querys
   4     '''
   5     def __init__(self,db,tablename):
   6         self.tablename=tablename
   7         self.db=db
   8 
   9     def __call__(self,query=None):
  10         return Operater(self.db,self.tablename,query)
  11 
  12     def __getattr__(self,field_name):
  13         return conds(field_name)
  14 
  15 #同理我们在数据连接类上加入getattr,
  16 def __getattr__(self,tablename):
  17     '''
  18     return single table queryer for select table
  19     '''
  20     return TableQueryer(self,tablename)

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

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

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

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

   1 class Operater:
   2     def __init__(self,db,tablename,query):
   3         self.count=Count(db,tablename,query)
   4         self.select=Select(db,tablename,query)
   5         self.update=Update(db,tablename,query)
   6         self.delete=Delete(db,tablename,query)

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

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

Alexander.Li


反馈

创建 by -- ZoomQuiet [2010-08-26 06:17:23]