欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

在Twisted下用MySQL adbapi获取自增id

程序员文章站 2022-05-06 23:49:35
...

D jango的ORM有一个很便捷的功能,其实也应该说是一个很基本的功能吧。就是在对一个model调用 save() 插入到数据库后,会将创建的

D jango的ORM有一个很便捷的功能,其实也应该说是一个很基本的功能吧。就是在对一个model调用 save() 插入到数据库后,会将创建的自增id同步到当前model上。SQL中调用 INSERT 默认的返回值是插入的行数,就目前的应用来说,其实是一个没啥意义的返回值,所以Django的ORM能够处理好自增id的同步是一件很让人愉悦的事。

不过没有使用Django,最近用的是Twisted提供的adbapi,如何获取自增id呢?

如果是我力挺的PostgreSQL的话,很简单,,给 INSERT 加上 RETURNING 语句就可以了:

INSERT INTO distributors (did, dname)
VALUES (DEFAULT, 'XYZ Widgets') RETURNING did;
不过MySQL呢?

最近用了一阵子MySQL,只是从命令行自动补全这方面来说,就已经明显地感觉到和PostgreSQL的差距了。老实说真不知道为啥那么多人如此热爱深爱着MySQL……

在*上搜了一下,找到两种方法,要么是使用

SELECT LAST_INSERT_ID();
或者是使用connector的 mysql_insert_id() 函数,这个对于Python中的 MySQLdb 来说就是connection的 insert_id() 函数,比如:

conn = MySQLdb.connect(host='heaven', user='god',
passwd='jesus', db='elysium')
cursor = conn.cursor()
cursor.execute('INSERT INTO account VALUES (%s, %s)',
('satan', 'male or female, who knows'))
new_id = conn.insert_id() # 过程也还算简单,但是对于Twisted的adbapi来说,我们的活并没有结束。为啥呢?因为adbapi用的是线程池来管理MySQL的连接的,每次Query调用,都会从线程池中获取一个线程,然后将相应的事务defer到该线程来处理。其默认的事务是以单个SQL语句为划分的,所以说,对于MySQL这样在执行完后还需要做其他操作的需求来说,默认的接口是无法满足的。这儿我再吐一下槽,还是pg好啊……

默认接口不够用,那我们就只能扩展它了。稍稍看一下adbapi的源代码我们可以发现,对于通常事务,adbapi其实是使用了 runInteraction 这个接口函数的。具体不同的事务,adbapi是将相应的callback作为其第一个参数,然后在 deferToThreadPool 时指定线程运行该callback来实现。所以,我们只需要为MySQL定义一种新的事务就可以了。

下面就是我们需要定义的新事务:

def runMySQLInsert(self, *args, **kw):
assert self.dbapiName == 'MySQLdb'
return self.runInteraction(self._runMySQLInsert, *args, **kw)

def _runMySQLInsert(self, trans, *args, **kw):
trans.execute(*args, **kw)
return trans.connection.insert_id()
Ok,接下来,把这两个函数Monkey Patch到adbapi上就完事了:)

在Twisted下用MySQL adbapi获取自增id