大约是一年半或者是两年前的样子,在那段时间里,我似乎特别痴迷于各种数据库以及相关的技术。其间看了不少书,可能是平常工作接触的多是sqlserver的缘故,我本人对其反而不是特别的感冒,所以看的多是关于postgresql和oracle的,尤其是前者。记得当时用的是一台celeron1.7G、内存256MB的方正电脑,安装这些软件让电脑变得非常的慢,终于在一次忍无可忍的僵死之后,我卸载了所有这些庞然大物。
之后台里引进了一台用于上下班记录的打卡机,为了方便统计迟到早退,我用perl/tk写了一个采集整理程序,后台用到了sqlite,这也是我和sqlite的初次邂逅。
至于对python,不好意思地说,我一直抱着骑墙的态度。如果perl可以解决的,我是断然不会用python的,这一切直到我对poe的幻想破灭为止。原先我是期待用poe来作为多线程的替代,可事实还是向着相反的方向发展。
sqlite是一个嵌入式的数据库,也就是说没有独立的维护进程,所有的维护都来自于程序本身。麻雀虽小,五脏俱全,sqlite实现了多数sql-92的标准,比如说transaction、trigger和复杂的查询等。
有很多介绍的书籍,记得有一本apress出版的《The difinitive guide to sqlite》,有毛500面的样子,用来介绍一个嵌入式数据库,着实有点长了,当然对于初学者是有其存在的价值的。
而pysqlite则是一个sqlite为python提供的api接口,它让一切对于sqlite的操作都变得异常简单。userguide并不长,但是基本上都说到点子上了,不过这里还是要对其提一下几个要点。事实上我个人认为,理解了这几点就完全足够了。
“from pysqlite2 import dbapi2 as sqlite”, 这就是一切的起源。
下面是一个涉及到我即将介绍的几点的示例程序,内容很简单,就是将一个Point对象存入sqlite建立的内存数据库中,然后原封不动地返回出来。
from
pysqlite2 import dbapi2 as sqlite
class
Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def __conform__(self, protocol):
if protocol is sqlite.PrepareProtocol:
return "%d;%d" % (self.x,
self.y)
def
adapt_converter(point):
return Point(*[int(x) for x in
point.split(";")])
sqlite.register_converter('point',
adapt_converter)
con=sqlite.connect(":memory:",detect_types=
sqlite.PARSE_COLNAMES)
cur =
con.cursor()
cur.execute('select
? as "a [point]"', (Point(2, 3), ))
print
type(cur.fetchone()[0])
首先要做一些准备工作,因为sqlite不是什么python对象都能够接受的, 为此需要一些必要的转换。这个例子里,sqlite就像是一个汽车维修站。在站里,为了维修,汽车被拆成了个个零件,等出站时又被组装成了一辆汽车。
第一步是声明一个Point类。这个类除了初始化函数__init__之外,还有一个两参量的函数__conform__,sqlite在检查存入数据库对象的时候会检查是否存在该函数,如果存在,则将该函数返回的值替代对象本身存入数据库。可以说这是一个转换器,将python对象转换为sqlite允许接纳对象,即Null、Integer、Real、Text和Blob其中之一。在这里,显然sqlite是无法接纳Point这个类所建立的Point(2,3)对象的,所以通过__conform__,实际将存入内容为“2;3”的文本。
使用“sqlite.register_adapter(Point, lambda p = point : “%d;%d” % p.x,
p.y) ”,在sqlite中注册一个适配器也可以完成转换的功能,但是我个人认为这样比较麻烦,不如写在类里来得清楚。
在处理完了Point类之后,为了使文本“2;3”在从数据库取出时能转换为Point对象,还需要用“sqlite.register_converter”注册一个叫“adapt_converter”的转换器。
这样一条拆卸组装的流水线就完成了。
实际使用到的只有两样东西,一是数据库连接对象con,还有一个就是用来执行命令并返回结果(有必要的话)的游标对象cur,它是由con对象产生的。
为了使sqlite意识到它正在处理的表中的某一列是已注册需要转换的“phone”,我们需要在建立表的时候声明该列的属性,或者在查询的时候显示地表明该列的属性。对于前者,建立con对象是应将detect_types设置为sqlite.PARSE_DELTYPES;后者,则是设置为sqlite.PARSE_COLNAMES。这里,因为使用的是一个临时表,所以只能使用后者的设置办法。
事实上,“select ? as ……”这句包含了两个方面的内容,首先它将Point(2, 3)存入了临时表中,这涉及到了Phone对象到文本的转换。之后,再将文本数据从临时表中取出,又用到了文本到Phone的反转换。
下面是一种浅显而罗嗦的写法:
cur.execute("create
table temp(t)")
cur.execute("insert
into temp values (?)", (Point(2, 3),))
cur.execute('select
? as "a [point]" from temp')
最后,打印从sqlite中取出的对象类型,内容为“<class '__main__.Point'>”。
以上这些,可以总结为两点:有必要的话,做好转换;建立con和cur。理解了这些,差不多就能用pysqlite了。当然如果要做工程、写程序还需要很多的其他知识,至少数据库设计这方面的内容是要看一下的。